Fixed error in sanityCheck

Fixed error in sanityCheck

/**
 * Chess contract
 * Stores any amount of games with two players and current state.
 * State encoding:
 *    positive numbers for white, negative numbers for black
 *    for details, see
 *    https://github.com/ise-ethereum/on-chain-chess/wiki/Chess-board-representation
 */

 contract Chess {
    bytes constant defaultState = '\x04\x06\x05\x03\x02\x05\x06\x04\x08\x08\x08\x0c\x08\x08\x08\x08\x07\x07\x07\x07\x07\x07\x07\x07\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x09\x09\x09\x09\x09\x09\x09\x09\x08\x08\x08\x08\x08\x08\x08\x08\x0c\x0a\x0b\x0d\x0e\x0b\x0a\x0c\x08\x08\x08\x7c\x08\x08\x08\x08';

    struct Game {
        address player1;
        address player2;
        string player1Alias;
        string player2Alias;
        address nextPlayer;
        address playerWhite; // Player that is white in this game
        address winner;
        int8[128] state;
    }

    mapping (bytes32 => Game) public games;
    mapping (address => mapping (int => bytes32)) public gamesOfPlayers;
    mapping (address => int) public numberGamesOfPlayers;

    // stack of open game ids
    mapping (bytes32 => bytes32) public openGameIds;
    bytes32 public head;

    /* Flags needed for validation
     * Usage e.g. Flags(Flag.FLAG_NAME)
     * Directions(Direction.UP)
     */
    enum Player { WHITE, BLACK }
    enum Direction { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT }
    bytes constant c_Directions = "\x30\x31\x41\x51\x50\x4f\x3f\x2f";
    enum Piece { BLACK_KING, BLACK_QUEEN, BLACK_ROOK, BLACK_BISHOP, BLACK_KNIGHT, BLACK_PAWN, EMPTY, WHITE_PAWN, WHITE_KNIGHT, WHITE_BISHOP, WHITE_ROOK, WHITE_QUEEN, WHITE_KING }
    enum Flag { WHITE_KING_POS, BLACK_KING_POS, CURRENT_PLAYER, WHITE_LEFT_CASTLING, WHITE_RIGHT_CASTLING, BLACK_LEFT_CASTLING, BLACK_RIGHT_CASTLING, BLACK_EN_PASSANT, WHITE_EN_PASSANT}
    bytes constant c_Flags = "\x7b\x0b\x38\x4e\x4f\x3e\x3f\x3d\x4d\x3c\x4c";
    function Flags(Flag i) constant internal returns (uint) {
       return uint(c_Flags[uint(i)]);
    }
    function Pieces(Piece i) constant internal returns (int8) {
        return -6 + int8(uint(i));
    }
    function Directions(Direction i) constant internal returns (int8) {
        return -64 + int8(c_Directions[uint(i)]);
    }
    function Players(Player p) constant internal returns (int8) {
        if (p == Player.WHITE) {
            return 1;
        }
        return -1;
    }

    bytes constant knightMoves = '\x1f\x21\x2e\x32\x4e\x52\x5f\x61';

    function Chess() {
        head = 'end';
    }

    event GameInitialized(bytes32 indexed gameId, address indexed player1, string player1Alias, address playerWhite);
    event GameJoined(bytes32 indexed gameId, address indexed player1, string player1Alias, address indexed player2, string player2Alias, address playerWhite);
    event GameStateChanged(bytes32 indexed gameId, int8[128] state);
    event Move(bytes32 indexed gameId, address indexed player, uint256 fromIndex, uint256 toIndex);
    event GameEnded(bytes32 indexed gameId, address indexed winner);

    event DebugInts(string message, int value1, int value2, int value3);

    /**
     * Convenience function to set a flag
     * Usage: setFlag(gameId, Flag.BLACK_KING_POS, 4);
     */
    function setFlag(bytes32 gameId, Flag flag, int value) internal {
        games[gameId].state[Flags(flag)] = int8(value);
    }

    /**
     * Convenience function to set a flag
     * Usage: getFlag(gameId, Flag.BLACK_KING_POS);
     */
    function getFlag(bytes32 gameId, Flag flag) internal returns (int8) {
        return games[gameId].state[Flags(flag)];
    }

    /**
     * Initialize a new game
     * string player1Alias: Alias of the player creating the game
     * bool playAsWhite: Pass true or false depending on if the creator will play as white
     */
    function initGame(string player1Alias, bool playAsWhite) public {
        // Generate game id based on player's addresses and current block number
        bytes32 gameId = sha3(msg.sender, block.number);

        // Initialize participants
        games[gameId].player1 = msg.sender;
        games[gameId].player1Alias = player1Alias;

        // Initialize state
        for (uint i = 0; i < 128; i++) {
            // Read defaultState bytes string, which is offset by 8 to be > 0
            games[gameId].state[i] = int8(defaultState[i]) - 8;
        }

        if (playAsWhite) {
            // Player 1 will play as white
            games[gameId].playerWhite = msg.sender;

            // Game starts with White, so here player 1
            games[gameId].nextPlayer = games[gameId].player1;
        }

        // Add game to gamesOfPlayers
        gamesOfPlayers[msg.sender][numberGamesOfPlayers[msg.sender]] = gameId;
        numberGamesOfPlayers[msg.sender]++;

        // Add to openGameIds
        openGameIds[gameId] = head;
        head = gameId;

        // Sent notification events
        GameInitialized(gameId, games[gameId].player1, player1Alias, games[gameId].playerWhite);
        GameStateChanged(gameId, games[gameId].state);
    }

    /**
     * Join an initialized game
     * bytes32 gameId: ID of the game to join
     * string player2Alias: Alias of the player that is joining
     */
    function joinGame(bytes32 gameId, string player2Alias) public {
        // Check that this game does not have a second player yet
        if (games[gameId].player2 != 0) {
            throw;
        }

        games[gameId].player2 = msg.sender;
        games[gameId].player2Alias = player2Alias;

        // If the other player isn't white, player2 will play as white
        if (games[gameId].playerWhite == 0) {
            games[gameId].playerWhite = msg.sender;
            // Game starts with White, so here player2
            games[gameId].nextPlayer = games[gameId].player2;
        }

        // Add game to gamesOfPlayers
        gamesOfPlayers[msg.sender][numberGamesOfPlayers[msg.sender]] = gameId;
        numberGamesOfPlayers[msg.sender]++;

        // Remove from openGameIds
        if (head == gameId) {
            head = openGameIds[head];
            openGameIds[gameId] = 0;
        } else {
            for (var g = head; g != 'end' && openGameIds[g] != 'end'; g = openGameIds[g]) {
                if (openGameIds[g] == gameId) {
                    openGameIds[g] = openGameIds[gameId];
                    openGameIds[gameId] = 0;
                    break;
                }
            }
        }

        GameJoined(gameId, games[gameId].player1, games[gameId].player1Alias, games[gameId].player2, player2Alias, games[gameId].playerWhite);
    }

    /* validates a move and executes it */
    function move(bytes32 gameId, uint256 fromIndex, uint256 toIndex) public {
        // Check that it is this player's turn
        if (games[gameId].nextPlayer != msg.sender) {
            throw;
        }

        int8 currentPlayerColor;
        if (msg.sender == games[gameId].playerWhite) {
            currentPlayerColor = Players(Player.WHITE);
        } else {
            currentPlayerColor = Players(Player.BLACK);
        }

        int8 fromFigure = games[gameId].state[fromIndex];
        int8 toFigure = games[gameId].state[toIndex];

        // Simple sanity checks
        sanityCheck(fromIndex, toIndex, fromFigure, toFigure, currentPlayerColor);

        // Check if move is technically possible
        if (!validateMove(gameId, fromIndex, toIndex, fromFigure, toFigure, currentPlayerColor)) {
            throw;
        }

        // For all pieces except knight, check if way is free
        // In case of king, it will check that he is not in check on any of the fields
        if (abs(fromFigure) != uint(Pieces(Piece.BLACK_KNIGHT))) {
            bool checkForCheck = abs(fromFigure) == uint(Pieces(Piece.BLACK_KING));
            checkWayFree(gameId, fromIndex, toIndex, currentPlayerColor, checkForCheck);
        }

        // makeTemporaryMove
        makeTemporaryMove(gameId, fromIndex, toIndex, fromFigure, toFigure);

        // isLegal
        isLegal(gameId, fromIndex, toIndex, fromFigure, toFigure, currentPlayerColor);

        // TODO: testIfCheck

        // Set nextPlayer
        if (msg.sender == games[gameId].player1) {
            games[gameId].nextPlayer = games[gameId].player2;
        } else {
            games[gameId].nextPlayer = games[gameId].player1;
        }

        Move(gameId, msg.sender, fromIndex, toIndex);
        GameStateChanged(gameId, games[gameId].state);
    }

    function sanityCheck(uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure, int8 currentPlayerColor) {
        // check that move is within the field
        if (toIndex & 0x88 != 0 || fromIndex & 0x88 != 0) {
            throw;
        }

        // check that from and to are distinct
        if (fromIndex == toIndex) {
            throw;
        }

        // check if the toIndex is empty (= is 0) or contains an enemy figure ("positive" * "negative" = "negative")
        // --> this only allows captures (negative results)  or moves to empty fields ( = 0)
        if (fromFigure * toFigure > 0) {
            throw;
        }

        // check if mover of the figure is the owner of the figure
        // also check if there is a figure at fromIndex to move (fromFigure != 0)
        if (currentPlayerColor * fromFigure <= 0) {
            throw;
        }
    }

    /**
     * Validates if a move is technically (not legally) possible,
     * i.e. if piece is capable to move this way
     */
    function validateMove(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure, int8 movingPlayerColor) returns (bool) {
        int8 direction = getDirection(fromIndex, toIndex);
        bool isDiagonal = !(abs(direction) == 16 || abs(direction) == 1);

        DebugInts('validateMove. fromFigure, toFigure, direction', int(fromFigure), int(fromFigure), int(direction));

        // Kings
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_KING))) {
            // Normal move
            if (int(fromIndex) + direction == int(toIndex)) {
                return true;
            }
            // Castling
            if (checkForCheck(gameId, fromIndex, movingPlayerColor)) {
                // Cannot move if already in check
                return false;
            }
            if (fromFigure == Pieces(Piece.BLACK_KING)) {
                if (4 == fromIndex && toFigure == 0) {
                    if (toIndex == 2 && getFlag(gameId, Flag.BLACK_LEFT_CASTLING) >= 0) {
                        return true;
                    }
                    if (toIndex == 6 && getFlag(gameId, Flag.BLACK_RIGHT_CASTLING) >= 0) {
                        return true;
                    }
                }
            }
            if (fromFigure == Pieces(Piece.WHITE_KING)) {
                if (116 == fromIndex && toFigure == 0) {
                    if (toIndex == 114 && getFlag(gameId, Flag.WHITE_LEFT_CASTLING) >= 0) {
                        return true;
                    }
                    if (toIndex == 118 && getFlag(gameId, Flag.WHITE_RIGHT_CASTLING) >= 0) {
                        return true;
                    }
                }
            }

            return false;
        }

        // Bishops, Queens, Rooks
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_BISHOP)) ||
            abs(fromFigure) == uint(Pieces(Piece.WHITE_QUEEN)) ||
            abs(fromFigure) == uint(Pieces(Piece.WHITE_ROOK))) {

            // Bishop can only walk diagonally, Rook only non-diagonally
            if (!isDiagonal && abs(fromFigure) == uint(Pieces(Piece.BLACK_BISHOP)) ||
                isDiagonal && abs(fromFigure) == uint(Pieces(Piece.BLACK_ROOK))) {
                return false;
            }

            // Traverse all fields in direction
            int temp = int(fromIndex);
            // walk in direction while inside board to find toIndex
            while (temp & 0x88 != 0) {
                if (uint(temp) == toIndex) {
                    return true;
                }
                temp += direction;
            }

            return false;
        }

        // Pawns
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_PAWN))) {
            // Black can only move in positive, White negative direction
            if (fromFigure == Pieces(Piece.BLACK_PAWN) && direction < 0 ||
                fromFigure == Pieces(Piece.WHITE_PAWN) && direction > 0) {
                return false;
            }
            // Forward move
            if (!isDiagonal) {
                // no horizontal movement allowed
                if (abs(direction) < 2) {
                    return false;
                }
                // simple move
                if (int(fromIndex) + direction == int(toIndex)) {
                    return true;
                }
                // double move
                if (int(fromIndex) + direction + direction == int(toIndex)) {
                    // Can only do double move starting form specific ranks
                    int rank = int(fromIndex/16);
                    if (1 == rank || 6 == rank) {
                        return true;
                    }
                }
                return false;
            }
            // diagonal move
            if (int(fromIndex) + direction == int(toIndex)) {
                // if empty, the en passant flag needs to be set
                if (toFigure * fromFigure == 0) {
                    if (fromFigure == Pieces(Piece.BLACK_PAWN) &&
                        getFlag(gameId, Flag.WHITE_EN_PASSANT) == int(toIndex) ||
                        fromFigure == Pieces(Piece.WHITE_PAWN) &&
                        getFlag(gameId, Flag.BLACK_EN_PASSANT) == int(toIndex)) {
                        return true;
                    }
                    return false;
                }
                // If not empty
                return true;
            }

            return false;
        }

        // Knights
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_KNIGHT))) {
            for (uint i; i < 8; i++) {
                if (int(fromIndex) + int(knightMoves[i]) - 64 == int(toIndex)) {
                    return true;
                }
            }
            return false;
        }

        return false;
    }

    /**
     * Checks if the way between fromIndex and toIndex is unblocked
     */
    function checkWayFree(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 currentPlayerColor, bool shouldCheckForCheck) {
        int8 direction = getDirection(fromIndex, toIndex);
        int currentIndex = int(fromIndex) + direction;

        // as long as we do not reach the desired position walk in direction and check
        while (int(toIndex) != currentIndex) {
            // we reached the end of the field
            if (currentIndex & 0x88 != 0) {
                throw;
            }
            // the path is blocked
            if (games[gameId].state[uint(currentIndex)] != 0) {
                throw;
            }
            // Check for check in case of king
            if (shouldCheckForCheck && checkForCheck(gameId, uint(currentIndex), currentPlayerColor)) {
                throw;
            }
            currentIndex = currentIndex + direction;
        }
        return;
    }

    function checkForCheck(bytes32 gameId, uint256 kingAtIndex, int8 currentPlayerColor) returns (bool) {
        // TODO
        return false;
    }

    function getDirection(uint256 fromIndex, uint256 toIndex) constant returns (int8) {
        // check if the figure is moved up or left of its origin
        bool isAboveLeft = fromIndex > toIndex;

        // check if the figure is moved in an horizontal plane
        // this code works because there is an eight square difference between the horizontal panes (the offboard)
        bool isSameHorizontal = (abs(int256(fromIndex) - int256(toIndex)) < (8));

        // check if the figure is moved in a vertical line
        bool isSameVertical = (fromIndex%8 == toIndex%8);

        // check if the figure is moved to the left of its origin
        bool isLeftSide = (fromIndex%8 > toIndex%8);

        /*Check directions*/
        if (isAboveLeft) {
            if (isSameVertical) {
                return Directions(Direction.UP);
            }
            if (isSameHorizontal) {
                return Directions(Direction.LEFT);
            }
            if (isLeftSide) {
                return Directions(Direction.UP_LEFT);
            } else {
                return Directions(Direction.UP_RIGHT);
            }
        } else {
            if (isSameVertical) {
                return Directions(Direction.DOWN);
            }
            if (isSameHorizontal) {
                return Directions(Direction.RIGHT);
            }
            if (isLeftSide) {
                return Directions(Direction.DOWN_LEFT);
            } else {
                return Directions(Direction.DOWN_RIGHT);
            }
        }
    }

    function makeTemporaryMove(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure){
        // remove all en passant flags
        setFlag(gameId, Flag.WHITE_EN_PASSANT, -1);
        setFlag(gameId, Flag.WHITE_EN_PASSANT, -1);

        // <---- Special Moves ---->

        // castling
        // it already passed valid we just need to move the rook

        // Black
        if (fromFigure == Pieces(Piece.BLACK_KING)){
            setFlag(gameId, Flag.BLACK_KING_POS, int8(toIndex));
            if ((fromIndex == 4)&&(toIndex == 2)){
                games[gameId].state[0] = 0;
                games[gameId].state[3] = Pieces(Piece.BLACK_ROOK);
            }
            if ((fromIndex == 4)&&(toIndex == 6)){
                games[gameId].state[7] = 0;
                games[gameId].state[5] = Pieces(Piece.BLACK_ROOK);
            }

        }
        // White
        if (fromFigure == Pieces(Piece.WHITE_KING)){
            setFlag(gameId, Flag.WHITE_KING_POS, int8(toIndex));
            if ((fromIndex == 116)&&(toIndex == 114)){
                games[gameId].state[112] = 0;
                games[gameId].state[115] = Pieces(Piece.BLACK_ROOK);
            }
            if ((fromIndex == 116)&&(toIndex == 118)){
                games[gameId].state[119] = 0;
                games[gameId].state[117] = Pieces(Piece.BLACK_ROOK);
            }

        }

        //Remove Castling Flag if king or Rook moves. But only at the first move for better performance

        // Black
        if (fromFigure == Pieces(Piece.BLACK_KING)){
            if (fromIndex == 4){
                setFlag(gameId, Flag.BLACK_LEFT_CASTLING, -1);
                setFlag(gameId, Flag.BLACK_RIGHT_CASTLING, -1);
            }
        }
        if (fromFigure == Pieces(Piece.BLACK_ROOK)){
            if (fromIndex == 0){
                setFlag(gameId, Flag.BLACK_LEFT_CASTLING, -1);
            }
            if (fromIndex == 7){
                setFlag(gameId, Flag.BLACK_RIGHT_CASTLING, -1);
            }
        }

        // White
        if (fromFigure == Pieces(Piece.WHITE_KING)){
            if (fromIndex == 116){
                setFlag(gameId, Flag.WHITE_LEFT_CASTLING, -1);
                setFlag(gameId, Flag.WHITE_RIGHT_CASTLING, -1);
            }
        }
        if (fromFigure == Pieces(Piece.WHITE_ROOK)){
            if (fromIndex == 112){
                setFlag(gameId, Flag.WHITE_LEFT_CASTLING, -1);
            }
            if (fromIndex == 119){
                setFlag(gameId, Flag.WHITE_RIGHT_CASTLING, -1);
            }
        }

        int8 direction = getDirection(fromIndex, toIndex);

        //PAWN - EN PASSANT or DOUBLE STEP
        if(abs(fromFigure) == uint(Pieces(Piece.BLACK_PAWN))){

            // En Passant - remove catched pawn
            // en passant if figure:pawn and diagonal move to empty field
            if(is_diagonal(direction) && toFigure == Pieces(Piece.EMPTY)){
                if(fromFigure == Pieces(Piece.BLACK_PAWN)){
                    games[gameId].state[uint(int(toIndex) + Directions(Direction.UP))] = 0;
                }else{
                     games[gameId].state[uint(int(toIndex) + Directions(Direction.DOWN))] = 0;
                }
            }

            // in case of double Step: set EN_PASSANT-Flag
            else if(int(fromIndex) + direction + direction == int(toIndex)){
                if(fromFigure == Pieces(Piece.BLACK_PAWN)){
                    games[gameId].state[Flags(Flag.BLACK_EN_PASSANT)] = (int8(toIndex) + Directions(Direction.UP));
                }else{
                    games[gameId].state[Flags(Flag.WHITE_EN_PASSANT)] = (int8(toIndex) + Directions(Direction.DOWN));
                }
            }
        }

        // <---- Normal Moves --->

        games[gameId].state[toIndex] = games[gameId].state[fromIndex];
        games[gameId].state[fromIndex] = 0;

    }

    // checks whether movingPlayerColor's king gets checked by move
    function isLegal(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure, int8 movingPlayerColor) returns (bool){
        // the king already gets tested when he moves
        if (abs(fromFigure) == uint(Pieces(Piece.BLACK_KING)))
            return;

        // through move of fromFigure from fromIndex king may now be in danger from that direction
        // get that direction
        int8 kingIndex = getOwnKing(gameId, movingPlayerColor);
        int8 kingDangerDirection = getDirection(uint256(kingIndex), fromIndex);

        // get the first Figure in this direction. Threat of Knight does not change through move of fromFigure.
        // All other figures can not jump over other figures. So only the first figure matters.
        int8 firstFigureIndex = getFirstFigure(gameId, kingDangerDirection,kingIndex);

        // if we found a figure in the danger direction
        if (firstFigureIndex != -1) {
            int8 firstFigure = games[gameId].state[uint(firstFigureIndex)];

            // if its an enemy
            if (firstFigure * movingPlayerColor > 0) {
                // check if the figure can move to the field of the king
                int8 kingFigure = Pieces(Piece.BLACK_KING)* movingPlayerColor;
                if (validateMove(gameId, uint256(firstFigureIndex), uint256(kingIndex), firstFigure, kingFigure, movingPlayerColor)) {
                    // it can
                    throw;
                }
            }

        }

        return;
    }

    function testIfCheck() {

    }

    function surrender(bytes32 gameId) public {
        if (games[gameId].winner != 0) {
            // Game already ended
            throw;
        }
        if (games[gameId].player1 == msg.sender) {
            // Player 1 surrendered, player 2 won
            games[gameId].winner = games[gameId].player2;
        } else if(games[gameId].player2 == msg.sender) {
            // Player 2 surrendered, player 1 won
            games[gameId].winner = games[gameId].player1;
        } else {
            // Sender is not a participant of this game
            throw;
        }

        GameEnded(gameId, games[gameId].winner);
    }

    // gets the first figure in direction from start, not including start
    function getFirstFigure(bytes32 gameId, int8 direction, int8 start) returns (int8){
        int currentIndex = start + direction;

        // as long as we do not reach the end of the board walk in direction
        while(currentIndex & 0x88 == 0){

            // if there is a figure at current field return it
            if(games[gameId].state[uint(currentIndex)] != Pieces(Piece.EMPTY))
                return int8(currentIndex);

            //otherwise move to the next field in that direction
            currentIndex = currentIndex + direction;
        }

        return -1;
    }

    function getGameId(address player, int index) constant returns (bytes32) {
        return gamesOfPlayers[player][index];
    }

    /*------------------------HELPER FUNCTIONS------------------------*/

    // This returns the absolute value of an integer
    function abs(int256 value) returns (uint256){
        if (value>=0) return uint256(value);
        else return uint256(-1*value);
    }

    function is_diagonal(int8 direction) internal returns (bool){
      return !(abs(direction) == 16 || abs(direction) == 1);
    }

    function getOwnKing(bytes32 gameId, int8 movingPlayerColor) returns (int8){
        if(movingPlayerColor == Players(Player.WHITE))
            return getFlag(gameId, Flag.BLACK_KING_POS);
        else
            return getFlag(gameId, Flag.WHITE_KING_POS);
    }

    /* This unnamed function is called whenever someone tries to send ether to it */
    function () {
        throw;     // Prevents accidental sending of ether
    }
}

/**
 * Chess contract
 * Stores any amount of games with two players and current state.
 * State encoding:
 *    positive numbers for white, negative numbers for black
 *    for details, see
 *    https://github.com/ise-ethereum/on-chain-chess/wiki/Chess-board-representation
 */

 contract Chess {
    bytes constant defaultState = '\x04\x06\x05\x03\x02\x05\x06\x04\x08\x08\x08\x0c\x08\x08\x08\x08\x07\x07\x07\x07\x07\x07\x07\x07\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x09\x09\x09\x09\x09\x09\x09\x09\x08\x08\x08\x08\x08\x08\x08\x08\x0c\x0a\x0b\x0d\x0e\x0b\x0a\x0c\x08\x08\x08\x7c\x08\x08\x08\x08';

    struct Game {
        address player1;
        address player2;
        string player1Alias;
        string player2Alias;
        address nextPlayer;
        address playerWhite; // Player that is white in this game
        address winner;
        int8[128] state;
    }

    mapping (bytes32 => Game) public games;
    mapping (address => mapping (int => bytes32)) public gamesOfPlayers;
    mapping (address => int) public numberGamesOfPlayers;

    // stack of open game ids
    mapping (bytes32 => bytes32) public openGameIds;
    bytes32 public head;

    /* Flags needed for validation
     * Usage e.g. Flags(Flag.FLAG_NAME)
     * Directions(Direction.UP)
     */
    enum Player { WHITE, BLACK }
    enum Direction { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT }
    bytes constant c_Directions = "\x30\x31\x41\x51\x50\x4f\x3f\x2f";
    enum Piece { BLACK_KING, BLACK_QUEEN, BLACK_ROOK, BLACK_BISHOP, BLACK_KNIGHT, BLACK_PAWN, EMPTY, WHITE_PAWN, WHITE_KNIGHT, WHITE_BISHOP, WHITE_ROOK, WHITE_QUEEN, WHITE_KING }
    enum Flag { WHITE_KING_POS, BLACK_KING_POS, CURRENT_PLAYER, WHITE_LEFT_CASTLING, WHITE_RIGHT_CASTLING, BLACK_LEFT_CASTLING, BLACK_RIGHT_CASTLING, BLACK_EN_PASSANT, WHITE_EN_PASSANT}
    bytes constant c_Flags = "\x7b\x0b\x38\x4e\x4f\x3e\x3f\x3d\x4d\x3c\x4c";
    function Flags(Flag i) constant internal returns (uint) {
       return uint(c_Flags[uint(i)]);
    }
    function Pieces(Piece i) constant internal returns (int8) {
        return -6 + int8(uint(i));
    }
    function Directions(Direction i) constant internal returns (int8) {
        return -64 + int8(c_Directions[uint(i)]);
    }
    function Players(Player p) constant internal returns (int8) {
        if (p == Player.WHITE) {
            return 1;
        }
        return -1;
    }

    bytes constant knightMoves = '\x1f\x21\x2e\x32\x4e\x52\x5f\x61';

    function Chess() {
        head = 'end';
    }

    event GameInitialized(bytes32 indexed gameId, address indexed player1, string player1Alias, address playerWhite);
    event GameJoined(bytes32 indexed gameId, address indexed player1, string player1Alias, address indexed player2, string player2Alias, address playerWhite);
    event GameStateChanged(bytes32 indexed gameId, int8[128] state);
    event Move(bytes32 indexed gameId, address indexed player, uint256 fromIndex, uint256 toIndex);
    event GameEnded(bytes32 indexed gameId, address indexed winner);

    event DebugInts(string message, int value1, int value2, int value3);

    /**
     * Convenience function to set a flag
     * Usage: setFlag(gameId, Flag.BLACK_KING_POS, 4);
     */
    function setFlag(bytes32 gameId, Flag flag, int value) internal {
        games[gameId].state[Flags(flag)] = int8(value);
    }

    /**
     * Convenience function to set a flag
     * Usage: getFlag(gameId, Flag.BLACK_KING_POS);
     */
    function getFlag(bytes32 gameId, Flag flag) internal returns (int8) {
        return games[gameId].state[Flags(flag)];
    }

    /**
     * Initialize a new game
     * string player1Alias: Alias of the player creating the game
     * bool playAsWhite: Pass true or false depending on if the creator will play as white
     */
    function initGame(string player1Alias, bool playAsWhite) public {
        // Generate game id based on player's addresses and current block number
        bytes32 gameId = sha3(msg.sender, block.number);

        // Initialize participants
        games[gameId].player1 = msg.sender;
        games[gameId].player1Alias = player1Alias;

        // Initialize state
        for (uint i = 0; i < 128; i++) {
            // Read defaultState bytes string, which is offset by 8 to be > 0
            games[gameId].state[i] = int8(defaultState[i]) - 8;
        }

        if (playAsWhite) {
            // Player 1 will play as white
            games[gameId].playerWhite = msg.sender;

            // Game starts with White, so here player 1
            games[gameId].nextPlayer = games[gameId].player1;
        }

        // Add game to gamesOfPlayers
        gamesOfPlayers[msg.sender][numberGamesOfPlayers[msg.sender]] = gameId;
        numberGamesOfPlayers[msg.sender]++;

        // Add to openGameIds
        openGameIds[gameId] = head;
        head = gameId;

        // Sent notification events
        GameInitialized(gameId, games[gameId].player1, player1Alias, games[gameId].playerWhite);
        GameStateChanged(gameId, games[gameId].state);
    }

    /**
     * Join an initialized game
     * bytes32 gameId: ID of the game to join
     * string player2Alias: Alias of the player that is joining
     */
    function joinGame(bytes32 gameId, string player2Alias) public {
        // Check that this game does not have a second player yet
        if (games[gameId].player2 != 0) {
            throw;
        }

        games[gameId].player2 = msg.sender;
        games[gameId].player2Alias = player2Alias;

        // If the other player isn't white, player2 will play as white
        if (games[gameId].playerWhite == 0) {
            games[gameId].playerWhite = msg.sender;
            // Game starts with White, so here player2
            games[gameId].nextPlayer = games[gameId].player2;
        }

        // Add game to gamesOfPlayers
        gamesOfPlayers[msg.sender][numberGamesOfPlayers[msg.sender]] = gameId;
        numberGamesOfPlayers[msg.sender]++;

        // Remove from openGameIds
        if (head == gameId) {
            head = openGameIds[head];
            openGameIds[gameId] = 0;
        } else {
            for (var g = head; g != 'end' && openGameIds[g] != 'end'; g = openGameIds[g]) {
                if (openGameIds[g] == gameId) {
                    openGameIds[g] = openGameIds[gameId];
                    openGameIds[gameId] = 0;
                    break;
                }
            }
        }

        GameJoined(gameId, games[gameId].player1, games[gameId].player1Alias, games[gameId].player2, player2Alias, games[gameId].playerWhite);
    }

    /* validates a move and executes it */
    function move(bytes32 gameId, uint256 fromIndex, uint256 toIndex) public {
        // Check that it is this player's turn
        if (games[gameId].nextPlayer != msg.sender) {
            throw;
        }

        int8 currentPlayerColor;
        if (msg.sender == games[gameId].playerWhite) {
            currentPlayerColor = Players(Player.WHITE);
        } else {
            currentPlayerColor = Players(Player.BLACK);
        }

        int8 fromFigure = games[gameId].state[fromIndex];
        int8 toFigure = games[gameId].state[toIndex];

        // Simple sanity checks
        sanityCheck(fromIndex, toIndex, fromFigure, toFigure, currentPlayerColor);

        // Check if move is technically possible
        if (!validateMove(gameId, fromIndex, toIndex, fromFigure, toFigure, currentPlayerColor)) {
            throw;
        }

        // For all pieces except knight, check if way is free
        // In case of king, it will check that he is not in check on any of the fields
        if (abs(fromFigure) != uint(Pieces(Piece.BLACK_KNIGHT))) {
            bool checkForCheck = abs(fromFigure) == uint(Pieces(Piece.BLACK_KING));
            checkWayFree(gameId, fromIndex, toIndex, currentPlayerColor, checkForCheck);
        }

        // makeTemporaryMove
        makeTemporaryMove(gameId, fromIndex, toIndex, fromFigure, toFigure);

        // isLegal
        isLegal(gameId, fromIndex, toIndex, fromFigure, toFigure, currentPlayerColor);

        // TODO: testIfCheck

        // Set nextPlayer
        if (msg.sender == games[gameId].player1) {
            games[gameId].nextPlayer = games[gameId].player2;
        } else {
            games[gameId].nextPlayer = games[gameId].player1;
        }

        Move(gameId, msg.sender, fromIndex, toIndex);
        GameStateChanged(gameId, games[gameId].state);
    }

    function sanityCheck(uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure, int8 currentPlayerColor) {
        // check that move is within the field
        if (toIndex & 0x88 != 0 || fromIndex & 0x88 != 0) {
            throw;
        }

        // check that from and to are distinct
        if (fromIndex == toIndex) {
            throw;
        }

        // check if the toIndex is empty (= is 0) or contains an enemy figure ("positive" * "negative" = "negative")
        // --> this only allows captures (negative results)  or moves to empty fields ( = 0)
        // also check if there is a figure at fromIndex to move (fromFigure != 0)
        if (fromFigure * toFigure < 0) {
            throw;
        }

        // check if mover of the figure is the owner of the figure
        if (currentPlayerColor * fromFigure < 0) {
            throw;
        }
    }

    /**
     * Validates if a move is technically (not legally) possible,
     * i.e. if piece is capable to move this way
     */
    function validateMove(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure, int8 movingPlayerColor) returns (bool) {
        int8 direction = getDirection(fromIndex, toIndex);
        bool isDiagonal = !(abs(direction) == 16 || abs(direction) == 1);

        DebugInts('validateMove. fromFigure, toFigure, direction', int(fromFigure), int(fromFigure), int(direction));

        // Kings
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_KING))) {
            // Normal move
            if (int(fromIndex) + direction == int(toIndex)) {
                return true;
            }
            // Castling
            if (checkForCheck(gameId, fromIndex, movingPlayerColor)) {
                // Cannot move if already in check
                return false;
            }
            if (fromFigure == Pieces(Piece.BLACK_KING)) {
                if (4 == fromIndex && toFigure == 0) {
                    if (toIndex == 2 && getFlag(gameId, Flag.BLACK_LEFT_CASTLING) >= 0) {
                        return true;
                    }
                    if (toIndex == 6 && getFlag(gameId, Flag.BLACK_RIGHT_CASTLING) >= 0) {
                        return true;
                    }
                }
            }
            if (fromFigure == Pieces(Piece.WHITE_KING)) {
                if (116 == fromIndex && toFigure == 0) {
                    if (toIndex == 114 && getFlag(gameId, Flag.WHITE_LEFT_CASTLING) >= 0) {
                        return true;
                    }
                    if (toIndex == 118 && getFlag(gameId, Flag.WHITE_RIGHT_CASTLING) >= 0) {
                        return true;
                    }
                }
            }

            return false;
        }

        // Bishops, Queens, Rooks
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_BISHOP)) ||
            abs(fromFigure) == uint(Pieces(Piece.WHITE_QUEEN)) ||
            abs(fromFigure) == uint(Pieces(Piece.WHITE_ROOK))) {

            // Bishop can only walk diagonally, Rook only non-diagonally
            if (!isDiagonal && abs(fromFigure) == uint(Pieces(Piece.BLACK_BISHOP)) ||
                isDiagonal && abs(fromFigure) == uint(Pieces(Piece.BLACK_ROOK))) {
                return false;
            }

            // Traverse all fields in direction
            int temp = int(fromIndex);
            // walk in direction while inside board to find toIndex
            while (temp & 0x88 != 0) {
                if (uint(temp) == toIndex) {
                    return true;
                }
                temp += direction;
            }

            return false;
        }

        // Pawns
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_PAWN))) {
            // Black can only move in positive, White negative direction
            if (fromFigure == Pieces(Piece.BLACK_PAWN) && direction < 0 ||
                fromFigure == Pieces(Piece.WHITE_PAWN) && direction > 0) {
                return false;
            }
            // Forward move
            if (!isDiagonal) {
                // no horizontal movement allowed
                if (abs(direction) < 2) {
                    return false;
                }
                // simple move
                if (int(fromIndex) + direction == int(toIndex)) {
                    return true;
                }
                // double move
                if (int(fromIndex) + direction + direction == int(toIndex)) {
                    // Can only do double move starting form specific ranks
                    int rank = int(fromIndex/16);
                    if (1 == rank || 6 == rank) {
                        return true;
                    }
                }
                return false;
            }
            // diagonal move
            if (int(fromIndex) + direction == int(toIndex)) {
                // if empty, the en passant flag needs to be set
                if (toFigure * fromFigure == 0) {
                    if (fromFigure == Pieces(Piece.BLACK_PAWN) &&
                        getFlag(gameId, Flag.WHITE_EN_PASSANT) == int(toIndex) ||
                        fromFigure == Pieces(Piece.WHITE_PAWN) &&
                        getFlag(gameId, Flag.BLACK_EN_PASSANT) == int(toIndex)) {
                        return true;
                    }
                    return false;
                }
                // If not empty
                return true;
            }

            return false;
        }

        // Knights
        if (abs(fromFigure) == uint(Pieces(Piece.WHITE_KNIGHT))) {
            for (uint i; i < 8; i++) {
                if (int(fromIndex) + int(knightMoves[i]) - 64 == int(toIndex)) {
                    return true;
                }
            }
            return false;
        }

        return false;
    }

    /**
     * Checks if the way between fromIndex and toIndex is unblocked
     */
    function checkWayFree(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 currentPlayerColor, bool shouldCheckForCheck) {
        int8 direction = getDirection(fromIndex, toIndex);
        int currentIndex = int(fromIndex) + direction;

        // as long as we do not reach the desired position walk in direction and check
        while (int(toIndex) != currentIndex) {
            // we reached the end of the field
            if (currentIndex & 0x88 != 0) {
                throw;
            }
            // the path is blocked
            if (games[gameId].state[uint(currentIndex)] != 0) {
                throw;
            }
            // Check for check in case of king
            if (shouldCheckForCheck && checkForCheck(gameId, uint(currentIndex), currentPlayerColor)) {
                throw;
            }
            currentIndex = currentIndex + direction;
        }
        return;
    }

    function checkForCheck(bytes32 gameId, uint256 kingAtIndex, int8 currentPlayerColor) returns (bool) {
        // TODO
        return false;
    }

    function getDirection(uint256 fromIndex, uint256 toIndex) constant returns (int8) {
        // check if the figure is moved up or left of its origin
        bool isAboveLeft = fromIndex > toIndex;

        // check if the figure is moved in an horizontal plane
        // this code works because there is an eight square difference between the horizontal panes (the offboard)
        bool isSameHorizontal = (abs(int256(fromIndex) - int256(toIndex)) < (8));

        // check if the figure is moved in a vertical line
        bool isSameVertical = (fromIndex%8 == toIndex%8);

        // check if the figure is moved to the left of its origin
        bool isLeftSide = (fromIndex%8 > toIndex%8);

        /*Check directions*/
        if (isAboveLeft) {
            if (isSameVertical) {
                return Directions(Direction.UP);
            }
            if (isSameHorizontal) {
                return Directions(Direction.LEFT);
            }
            if (isLeftSide) {
                return Directions(Direction.UP_LEFT);
            } else {
                return Directions(Direction.UP_RIGHT);
            }
        } else {
            if (isSameVertical) {
                return Directions(Direction.DOWN);
            }
            if (isSameHorizontal) {
                return Directions(Direction.RIGHT);
            }
            if (isLeftSide) {
                return Directions(Direction.DOWN_LEFT);
            } else {
                return Directions(Direction.DOWN_RIGHT);
            }
        }
    }

    function makeTemporaryMove(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure){
        // remove all en passant flags
        setFlag(gameId, Flag.WHITE_EN_PASSANT, -1);
        setFlag(gameId, Flag.WHITE_EN_PASSANT, -1);

        // <---- Special Moves ---->

        // castling
        // it already passed valid we just need to move the rook

        // Black
        if (fromFigure == Pieces(Piece.BLACK_KING)){
            setFlag(gameId, Flag.BLACK_KING_POS, int8(toIndex));
            if ((fromIndex == 4)&&(toIndex == 2)){
                games[gameId].state[0] = 0;
                games[gameId].state[3] = Pieces(Piece.BLACK_ROOK);
            }
            if ((fromIndex == 4)&&(toIndex == 6)){
                games[gameId].state[7] = 0;
                games[gameId].state[5] = Pieces(Piece.BLACK_ROOK);
            }

        }
        // White
        if (fromFigure == Pieces(Piece.WHITE_KING)){
            setFlag(gameId, Flag.WHITE_KING_POS, int8(toIndex));
            if ((fromIndex == 116)&&(toIndex == 114)){
                games[gameId].state[112] = 0;
                games[gameId].state[115] = Pieces(Piece.BLACK_ROOK);
            }
            if ((fromIndex == 116)&&(toIndex == 118)){
                games[gameId].state[119] = 0;
                games[gameId].state[117] = Pieces(Piece.BLACK_ROOK);
            }

        }

        //Remove Castling Flag if king or Rook moves. But only at the first move for better performance

        // Black
        if (fromFigure == Pieces(Piece.BLACK_KING)){
            if (fromIndex == 4){
                setFlag(gameId, Flag.BLACK_LEFT_CASTLING, -1);
                setFlag(gameId, Flag.BLACK_RIGHT_CASTLING, -1);
            }
        }
        if (fromFigure == Pieces(Piece.BLACK_ROOK)){
            if (fromIndex == 0){
                setFlag(gameId, Flag.BLACK_LEFT_CASTLING, -1);
            }
            if (fromIndex == 7){
                setFlag(gameId, Flag.BLACK_RIGHT_CASTLING, -1);
            }
        }

        // White
        if (fromFigure == Pieces(Piece.WHITE_KING)){
            if (fromIndex == 116){
                setFlag(gameId, Flag.WHITE_LEFT_CASTLING, -1);
                setFlag(gameId, Flag.WHITE_RIGHT_CASTLING, -1);
            }
        }
        if (fromFigure == Pieces(Piece.WHITE_ROOK)){
            if (fromIndex == 112){
                setFlag(gameId, Flag.WHITE_LEFT_CASTLING, -1);
            }
            if (fromIndex == 119){
                setFlag(gameId, Flag.WHITE_RIGHT_CASTLING, -1);
            }
        }

        int8 direction = getDirection(fromIndex, toIndex);

        //PAWN - EN PASSANT or DOUBLE STEP
        if(abs(fromFigure) == uint(Pieces(Piece.BLACK_PAWN))){

            // En Passant - remove catched pawn
            // en passant if figure:pawn and diagonal move to empty field
            if(is_diagonal(direction) && toFigure == Pieces(Piece.EMPTY)){
                if(fromFigure == Pieces(Piece.BLACK_PAWN)){
                    games[gameId].state[uint(int(toIndex) + Directions(Direction.UP))] = 0;
                }else{
                     games[gameId].state[uint(int(toIndex) + Directions(Direction.DOWN))] = 0;
                }
            }

            // in case of double Step: set EN_PASSANT-Flag
            else if(int(fromIndex) + direction + direction == int(toIndex)){
                if(fromFigure == Pieces(Piece.BLACK_PAWN)){
                    games[gameId].state[Flags(Flag.BLACK_EN_PASSANT)] = (int8(toIndex) + Directions(Direction.UP));
                }else{
                    games[gameId].state[Flags(Flag.WHITE_EN_PASSANT)] = (int8(toIndex) + Directions(Direction.DOWN));
                }
            }
        }

        // <---- Normal Moves --->

        games[gameId].state[toIndex] = games[gameId].state[fromIndex];
        games[gameId].state[fromIndex] = 0;

    }

    // checks whether movingPlayerColor's king gets checked by move
    function isLegal(bytes32 gameId, uint256 fromIndex, uint256 toIndex, int8 fromFigure, int8 toFigure, int8 movingPlayerColor) returns (bool){
        // the king already gets tested when he moves
        if (abs(fromFigure) == uint(Pieces(Piece.BLACK_KING)))
            return;

        // through move of fromFigure from fromIndex king may now be in danger from that direction
        // get that direction
        int8 kingIndex = getOwnKing(gameId, movingPlayerColor);
        int8 kingDangerDirection = getDirection(uint256(kingIndex), fromIndex);

        // get the first Figure in this direction. Threat of Knight does not change through move of fromFigure.
        // All other figures can not jump over other figures. So only the first figure matters.
        int8 firstFigureIndex = getFirstFigure(gameId, kingDangerDirection,kingIndex);

        // if we found a figure in the danger direction
        if (firstFigureIndex != -1) {
            int8 firstFigure = games[gameId].state[uint(firstFigureIndex)];

            // if its an enemy
            if (firstFigure * movingPlayerColor > 0) {
                // check if the figure can move to the field of the king
                int8 kingFigure = Pieces(Piece.BLACK_KING)* movingPlayerColor;
                if (validateMove(gameId, uint256(firstFigureIndex), uint256(kingIndex), firstFigure, kingFigure, movingPlayerColor)) {
                    // it can
                    throw;
                }
            }

        }

        return;
    }

    function testIfCheck() {

    }

    function surrender(bytes32 gameId) public {
        if (games[gameId].winner != 0) {
            // Game already ended
            throw;
        }
        if (games[gameId].player1 == msg.sender) {
            // Player 1 surrendered, player 2 won
            games[gameId].winner = games[gameId].player2;
        } else if(games[gameId].player2 == msg.sender) {
            // Player 2 surrendered, player 1 won
            games[gameId].winner = games[gameId].player1;
        } else {
            // Sender is not a participant of this game
            throw;
        }

        GameEnded(gameId, games[gameId].winner);
    }

    // gets the first figure in direction from start, not including start
    function getFirstFigure(bytes32 gameId, int8 direction, int8 start) returns (int8){
        int currentIndex = start + direction;

        // as long as we do not reach the end of the board walk in direction
        while(currentIndex & 0x88 == 0){

            // if there is a figure at current field return it
            if(games[gameId].state[uint(currentIndex)] != Pieces(Piece.EMPTY))
                return int8(currentIndex);

            //otherwise move to the next field in that direction
            currentIndex = currentIndex + direction;
        }

        return -1;
    }

    function getGameId(address player, int index) constant returns (bytes32) {
        return gamesOfPlayers[player][index];
    }

    /*------------------------HELPER FUNCTIONS------------------------*/

    // This returns the absolute value of an integer
    function abs(int256 value) returns (uint256){
        if (value>=0) return uint256(value);
        else return uint256(-1*value);
    }

    function is_diagonal(int8 direction) internal returns (bool){
      return !(abs(direction) == 16 || abs(direction) == 1);
    }

    function getOwnKing(bytes32 gameId, int8 movingPlayerColor) returns (int8){
        if(movingPlayerColor == Players(Player.WHITE))
            return getFlag(gameId, Flag.BLACK_KING_POS);
        else
            return getFlag(gameId, Flag.WHITE_KING_POS);
    }

    /* This unnamed function is called whenever someone tries to send ether to it */
    function () {
        throw;     // Prevents accidental sending of ether
    }
}


Rename clean to cleanConsents

Rename clean to cleanConsents

import "BulletinBoard.sol";

/**
 * This contract performs an "eval" on arbitrary contracts but only with the
 * consent of all participants named.
 */
contract UnanimousConsent {

	enum ConsentState {
		NONE,
		CONSENTED,
		EXECUTED
	}

	struct Action {
		bool initialized;
		address target;
		uint value;
		bytes32[] calldata;
	}

	// Records the current state of consent for a given hash and address
	mapping (bytes32 =>
			mapping (address => ConsentState)) consentStates;

	// Contains all of the sent execution actions
	mapping (bytes32 => Action) actions;

	// Holds all of the participants in the current UnanimousConsent contract
	address[] participants;

	// Holds the BulletinBoard for counterfactual addressing
	BulletinBoard public bulletinBoard;

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

	/**
	 * Creates a new UnanimousConsent contract.
	 *
	 * _participants: The initial participants in the contract
	 */
	function UnanimousConsent(address[] _participants) {
		participants = _participants;
		bulletinBoard = new BulletinBoard(this);
	}

	/**
	 * Adds an `Action` to potentially be executed.
	 *
	 * _target: The target of the execution call
	 * _value: The amount of Ether to send
	 * _calldata: The calldata associated with the call
	 */
	function addAction(address _target, uint _value, bytes32[] _calldata) external {
		Action memory action = Action(true, _target, _value, _calldata);
		actions[sha3(_target, _value, _calldata)] = action;
	}

	/**
	 * Evaluates arbitrary functions of contracts, but only with universal
	 * consent. Note that any `eval` sub-calls should `throw` in the event that
	 * there is a contract execution failure (such as if a precondition isn't
	 * met or if a verification of some sort fails) to prevent this call from
	 * finishing.
	 *
	 * _actionHashes: The hashes of the `Action` that have been submitted
	 */
	function eval(bytes32[] _actionHashes) {
		uint i;
		bytes32 hash = sha3(_actionHashes);

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

		for (i = 0; i < _actionHashes.length; i++) {
			Action memory action = actions[_actionHashes[i]];
			if (!(action.initialized && action.target.call.value(action.value)(action.calldata))) {
				throw;
			}
		}
	}

	/**
	 * Provides consent for the hash of an execution call for the sender of the
	 * message. Throws if unsucessful.
	 *
	 * _hashes: The hashes of the execution call
	 */
	function consent(bytes32[] _hashes) {
		for (uint i = _hashes.length; i > 0; i--) {
			if (consentStates[_hashes[i-1]][msg.sender] == ConsentState.NONE) {
				consentStates[_hashes[i-1]][msg.sender] = ConsentState.CONSENTED;
			} else {
				throw;
			}
		}
	}

	/**
	 * Changes the participants in the current contract. Intended to only be
	 * called by this contract's `eval`.
	 *
	 * _participants: The new participants in the contract
	 */
	function changeParticipants(address[] _participants) external onlySelf {
		participants = _participants;
	}

	/**
	 * Removes a group of `Action` that have been submitted. Intended to be
	 * called by this contract's `eval`.
	 *
	 * _actionHashes: The hashes of the `Action` that have been submitted
	 */
	function cleanActions(bytes32[] _actionHashes) external onlySelf {
		for (uint i = _actionHashes.length; i > 0; i--) {
			delete actions[_actionHashes[i-1]];
		}
	}

	/**
	 * Cleans up previously approved consents. Intended to only be called by
	 * this contract's `eval`.
	 *
	 * _hashes: The list of hashes to clean
	 * _participants: The list of address whose hashes should be cleaned
	 */
	function cleanConsents(bytes32[] _hashes, address[] _participants) external onlySelf {
		for (uint i = _hashes.length; i > 0; i--) {
			for (uint j = _participants.length; j > 0; j--) {
				delete consentStates[_hashes[i-1]][_participants[j-1]];
			}
		}
	}

	/**
	 * Kills the current contract. Intended to only be called by this contract's
	 * eval.
	 *
	 * _recipient: The recipient of funds from the `selfdestruct` call
	 */
	function kill(address _recipient) external onlySelf {
		selfdestruct(_recipient);
	}
}

import "BulletinBoard.sol";

/**
 * This contract performs an "eval" on arbitrary contracts but only with the
 * consent of all participants named.
 */
contract UnanimousConsent {

	enum ConsentState {
		NONE,
		CONSENTED,
		EXECUTED
	}

	struct Action {
		bool initialized;
		address target;
		uint value;
		bytes32[] calldata;
	}

	// Records the current state of consent for a given hash and address
	mapping (bytes32 =>
			mapping (address => ConsentState)) consentStates;

	// Contains all of the sent execution actions
	mapping (bytes32 => Action) actions;

	// Holds all of the participants in the current UnanimousConsent contract
	address[] participants;

	// Holds the BulletinBoard for counterfactual addressing
	BulletinBoard public bulletinBoard;

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

	/**
	 * Creates a new UnanimousConsent contract.
	 *
	 * _participants: The initial participants in the contract
	 */
	function UnanimousConsent(address[] _participants) {
		participants = _participants;
		bulletinBoard = new BulletinBoard(this);
	}

	/**
	 * Adds an `Action` to potentially be executed.
	 *
	 * _target: The target of the execution call
	 * _value: The amount of Ether to send
	 * _calldata: The calldata associated with the call
	 */
	function addAction(address _target, uint _value, bytes32[] _calldata) external {
		Action memory action = Action(true, _target, _value, _calldata);
		actions[sha3(_target, _value, _calldata)] = action;
	}

	/**
	 * Evaluates arbitrary functions of contracts, but only with universal
	 * consent. Note that any `eval` sub-calls should `throw` in the event that
	 * there is a contract execution failure (such as if a precondition isn't
	 * met or if a verification of some sort fails) to prevent this call from
	 * finishing.
	 *
	 * _actionHashes: The hashes of the `Action` that have been submitted
	 */
	function eval(bytes32[] _actionHashes) {
		uint i;
		bytes32 hash = sha3(_actionHashes);

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

		for (i = 0; i < _actionHashes.length; i++) {
			Action memory action = actions[_actionHashes[i]];
			if (!(action.initialized && action.target.call.value(action.value)(action.calldata))) {
				throw;
			}
		}
	}

	/**
	 * Provides consent for the hash of an execution call for the sender of the
	 * message. Throws if unsucessful.
	 *
	 * _hashes: The hashes of the execution call
	 */
	function consent(bytes32[] _hashes) {
		for (uint i = _hashes.length; i > 0; i--) {
			if (consentStates[_hashes[i-1]][msg.sender] == ConsentState.NONE) {
				consentStates[_hashes[i-1]][msg.sender] = ConsentState.CONSENTED;
			} else {
				throw;
			}
		}
	}

	/**
	 * Changes the participants in the current contract. Intended to only be
	 * called by this contract's `eval`.
	 *
	 * _participants: The new participants in the contract
	 */
	function changeParticipants(address[] _participants) external onlySelf {
		participants = _participants;
	}

	/**
	 * Removes a group of `Action` that have been submitted. Intended to be
	 * called by this contract's `eval`.
	 *
	 * _actionHashes: The hashes of the `Action` that have been submitted
	 */
	function cleanActions(bytes32[] _actionHashes) external onlySelf {
		for (uint i = _actionHashes.length; i > 0; i--) {
			delete actions[_actionHashes[i-1]];
		}
	}

	/**
	 * Cleans up previously approved consents. Intended to only be called by
	 * this contract's `eval`.
	 *
	 * _hashes: The list of hashes to clean
	 * _participants: The list of address whose hashes should be cleaned
	 */
	function clean(bytes32[] _hashes, address[] _participants) external onlySelf {
		for (uint i = _hashes.length; i > 0; i--) {
			for (uint j = _participants.length; j > 0; j--) {
				delete consentStates[_hashes[i-1]][_participants[j-1]];
			}
		}
	}

	/**
	 * Kills the current contract. Intended to only be called by this contract's
	 * eval.
	 *
	 * _recipient: The recipient of funds from the `selfdestruct` call
	 */
	function kill(address _recipient) external onlySelf {
		selfdestruct(_recipient);
	}
}


Refund broadcaster if transcoder fails verification

pragma solidity ^0.4.13;

import "./IJobsManager.sol";
import "./libraries/JobLib.sol";
import "./libraries/MerkleProof.sol";
import "../Manager.sol";
import "../ContractRegistry.sol";
import "../LivepeerToken.sol";
import "../bonding/IBondingManager.sol";
import "../verification/Verifiable.sol";
import "../verification/Verifier.sol";

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

contract JobsManager is IJobsManager, Verifiable, Manager {
    using SafeMath for uint256;

    // Token address
    LivepeerToken public token;

    // Verifier address
    Verifier public verifier;

    // Content-addressed storage hash of verification code
    string public verificationCodeHash;

    // % of segments to be verified. 1 / verificationRate == % to be verified
    uint64 public verificationRate;

    // % of verifications you can fail before being slashed
    uint64 public verificationFailureThreshold;

    // Time between when endJob() is called for a job and when the job is considered inactive. Denominated in blocks
    uint256 public jobEndingPeriod;

    // Time after a transcoder calls claimWork() that it has to complete verification of claimed work
    uint256 public verificationPeriod;

    // Time after a claim's verification period during which anyone can slash the transcoder for missing a required verification
    uint256 public slashingPeriod;

    // % of stake slashed for failed verification
    uint64 public failedVerificationSlashAmount;

    // % of stake slashed for missed verification
    uint64 public missedVerificationSlashAmount;

    // % of of slashed amount awarded to finder
    uint64 public finderFee;

    // Mapping broadcaster => deposited funds for jobs
    mapping (address => uint256) public broadcasterDeposits;

    // Represents a transcode job
    struct Job {
        uint256 jobId;                        // Unique identifer for job
        string streamId;                      // Unique identifier for stream.
        string transcodingOptions;            // Options used for transcoding
        uint256 maxPricePerSegment;           // Max price (in LPT base units) per segment of a stream
        address broadcasterAddress;           // Address of broadcaster that requestes a transcoding job
        address transcoderAddress;            // Address of transcoder selected for the job
        uint256 endBlock;                     // Block at which the job is ended and considered inactive
        Claim[] claims;                       // Claims submitted for this job
        uint256 escrow;                       // Claim fees before verification and slashing periods are complete
    }

    // States of a job
    enum JobStatus { Inactive, Active }

    // Represents a transcode claim
    struct Claim {
        uint256 claimId;                                   // Unique identifier for claim
        uint256[2] segmentRange;                           // Range of segments claimed
        bytes32 claimRoot;                                 // Merkle root of segment transcode proof data
        uint256 claimBlock;                                // Block number that claim was submitted
        uint256 endVerificationBlock;                      // End of verification period for this claim
        uint256 endSlashingBlock;                          // End of slashing period for this claim
        uint256 transcoderTotalStake;                      // Transcoder's total stake at the time of claim
        mapping (uint256 => bool) segmentVerifications;    // Mapping segment number => whether segment was submitted for verification
        ClaimStatus status;                                // Status of claim (pending, slashed, complete)
    }

    // States of a transcode claim
    enum ClaimStatus { Pending, Slashed, Complete }

    // Transcode jobs
    mapping (uint256 => Job) public jobs;
    // Number of jobs created. Also used for sequential identifiers
    uint256 public numJobs;

    // Check if sender is Verifier contract
    modifier onlyVerifier() {
        require(msg.sender == address(verifier));
        _;
    }

    modifier jobExists(uint256 _jobId) {
        require(_jobId < numJobs);
        _;
    }

    modifier claimExists(uint256 _jobId, uint256 _claimId) {
        require(_claimId < jobs[_jobId].claims.length);
        _;
    }

    // Events
    event NewJob(address indexed transcoder, address indexed broadcaster, uint256 jobId);
    event NewClaim(address indexed transcoder, uint256 indexed jobId, uint256 claimId);
    event ReceivedVerification(uint256 indexed jobId, uint256 indexed claimId, uint256 segmentNumber, bool result);

    function JobsManager(address _registry, address _token, address _verifier)
        Manager(_registry)
    {
        // Set LivepeerToken address
        token = LivepeerToken(_token);
        // Set Verifier address
        verifier = Verifier(_verifier);
        // TODO: These are all test values and need to be replaced with realistic values
        // Verify all segments.
        verificationRate = 1;
        // A job becomes inactive 100 blocks after endJob() is called
        jobEndingPeriod = 50;
        // A transcoder has 100 blocks for verification after claiming work
        verificationPeriod = 50;
        // Slashing period after verification is 100 blocks
        slashingPeriod = 50;
        // Stake slashed by 20% for a failed verification
        failedVerificationSlashAmount = 20;
        // Stake slashed by 30% for a missed verification
        missedVerificationSlashAmount = 30;
    }

    /*
     * @dev Deposit funds for jobs
     * @param _amount Amount to deposit
     */
    function deposit(uint256 _amount) external returns (bool) {
        broadcasterDeposits[msg.sender] = broadcasterDeposits[msg.sender].add(_amount);
        // Transfer tokens for deposit. Sender needs to approve amount first
        token.transferFrom(msg.sender, this, _amount);

        return true;
    }

    /*
     * @dev Withdraw deposited funds
     */
    function withdraw() external returns (bool) {
        uint256 amount = broadcasterDeposits[msg.sender];
        broadcasterDeposits[msg.sender] = 0;
        token.transfer(msg.sender, amount);

        return true;
    }

    /*
     * @dev Submit a transcoding job
     * @param _streamId Unique stream identifier
     * @param _transcodingOptions Output bitrates, formats, encodings
     * @param _maxPricePerSegment Max price (in LPT base units) to pay for transcoding a segment of a stream
     */
    function job(string _streamId, string _transcodingOptions, uint256 _maxPricePerSegment) external returns (bool) {
        address electedTranscoder = bondingManager().electActiveTranscoder(_maxPricePerSegment);
        /* There must be an elected transcoder */
        require(electedTranscoder != address(0));

        Job storage job = jobs[numJobs];
        job.jobId = numJobs;
        job.streamId = _streamId;
        job.transcodingOptions = _transcodingOptions;
        job.maxPricePerSegment = _maxPricePerSegment;
        job.broadcasterAddress = msg.sender;
        job.transcoderAddress = electedTranscoder;

        NewJob(electedTranscoder, msg.sender, numJobs);

        // Increment number of created jobs
        numJobs = numJobs.add(1);

        return true;
    }

    /*
     * @dev End a job. Can be called by either a broadcaster or transcoder of a job
     * @param _jobId Job identifier
     */
    function endJob(uint256 _jobId) external returns (bool) {
        Job storage job = jobs[_jobId];

        // Job must not already have an end block
        require(job.endBlock == 0);
        // Sender must be the job's broadcaster or elected transcoder
        require(job.broadcasterAddress == msg.sender || jobs[_jobId].transcoderAddress == msg.sender);

        // Set end block for job
        job.endBlock = block.number.add(jobEndingPeriod);
    }

    /*
     * @dev Submit claim for a range of segments
     * @param _jobId Job identifier
     * @param _segmentRange Range of claimed segments
     * @param _claimRoot Merkle root of transcoded segment proof data for claimed segments
     */
    function claimWork(uint256 _jobId, uint256[2] _segmentRange, bytes32 _claimRoot) jobExists(_jobId) external returns (bool) {
        Job storage job = jobs[_jobId];

        // Job must be active
        require(jobStatus(_jobId) == JobStatus.Active);
        // Sender must be elected transcoder
        require(job.transcoderAddress == msg.sender);

        // Move fees from broadcaster deposit to escrow
        uint256 fees = _segmentRange[1].sub(_segmentRange[0]).add(1).mul(job.maxPricePerSegment);
        broadcasterDeposits[job.broadcasterAddress] = broadcasterDeposits[job.broadcasterAddress].sub(fees);
        job.escrow = job.escrow.add(fees);

        job.claims.push(Claim({
            claimId: job.claims.length,
            segmentRange: _segmentRange,
            claimRoot: _claimRoot,
            claimBlock: block.number,
            transcoderTotalStake: bondingManager().transcoderTotalStake(msg.sender),
            endVerificationBlock: block.number.add(verificationPeriod),
            endSlashingBlock: block.number.add(verificationPeriod).add(slashingPeriod),
            status: ClaimStatus.Pending
        }));

        NewClaim(job.transcoderAddress, _jobId, job.claims.length - 1);

        return true;
    }

    /*
     * @dev Submit transcode receipt and invoke transcoding verification
     * @param _jobId Job identifier
     * @param _segmentNumber Segment sequence number in stream
     * @param _dataHash Content-addressed storage hash of segment data
     * @param _transcodedDataHash Hash of transcoded segment data
     * @param _broadcasterSig Broadcaster's signature over segment hash
     * @param _proof Merkle proof for transcode receipt
     */
    function verify(
        uint256 _jobId,
        uint256 _claimId,
        uint256 _segmentNumber,
        string _dataHash,
        string _transcodedDataHash,
        bytes _broadcasterSig,
        bytes _proof
    )
        external
        payable
        returns (bool)
    {
        // Must be valid job id
        require(_jobId < numJobs);
        // Must be valid claim id
        require(_claimId < jobs[_jobId].claims.length);

        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Job must be active
        require(jobStatus(_jobId) == JobStatus.Active);
        // Sender must be elected transcoder
        require(job.transcoderAddress == msg.sender);

        // Segment must be eligible for verification
        require(JobLib.shouldVerifySegment(_segmentNumber, claim.segmentRange, claim.claimBlock, verificationRate));
        // Receipt must be valid
        require(validateReceipt(_jobId, _claimId, _segmentNumber, _dataHash, _transcodedDataHash, _broadcasterSig, _proof));

        // Mark segment as submitted for verification
        claim.segmentVerifications[_segmentNumber] = true;

        // Invoke transcoding verification. This is async and will result in a callback to receiveVerification() which is implemented by this contract
        verifier.verify(_jobId, _claimId, _segmentNumber, _dataHash, _transcodedDataHash, this);

        return true;
    }

    /*
     * @dev Callback function that receives the results of transcoding verification
     * @param _jobId Job identifier
     * @param _segmentNumber Segment being verified for job
     * @param _result Boolean result of whether verification succeeded or not
     */
    function receiveVerification(uint256 _jobId, uint256 _claimId, uint256 _segmentNumber, bool _result) onlyVerifier external returns (bool) {
        if (!_result) {
            refundBroadcaster(_jobId, _claimId);
            // Protocol slashes transcoder for failing verification (no finder)
            bondingManager().slashTranscoder(jobs[_jobId].transcoderAddress, address(0), failedVerificationSlashAmount, 0);
        }

        ReceivedVerification(_jobId, _claimId, _segmentNumber, _result);

        return true;
    }

    /*
     * @dev Distribute fees for multiple claims
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function batchDistributeFees(uint256 _jobId, uint256[] _claimIds) external returns (bool) {
        for (uint256 i = 0; i < _claimIds.length; i++) {
            distributeFees(_jobId, _claimIds[i]);
        }

        return true;
    }

    /*
     * @dev Slash transcoder for missing verification
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     * @param _segmentNumber Segment that was not verified
     */
    function missedVerificationSlash(uint256 _jobId, uint256 _claimId, uint256 _segmentNumber) external returns (bool) {
        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Must be after verification period
        require(block.number > claim.endVerificationBlock);
        // Must be before end of slashing period
        require(block.number <= claim.endSlashingBlock);
        // Claim must be pending
        require(claim.status == ClaimStatus.Pending);
        // Segment must be eligible for verification
        require(JobLib.shouldVerifySegment(_segmentNumber, claim.segmentRange, claim.claimBlock, verificationRate));
        // Transcoder must have missed verification for the segment
        require(!claim.segmentVerifications[_segmentNumber]);

        refundBroadcaster(_jobId, _claimId);

        // Slash transcoder and provide finder params
        bondingManager().slashTranscoder(job.transcoderAddress, msg.sender, missedVerificationSlashAmount, finderFee);

        // Set claim as slashed
        claim.status = ClaimStatus.Slashed;

        return true;
    }

    /*
     * @dev Distribute fees for a particular claim
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function distributeFees(uint256 _jobId, uint256 _claimId) public returns (bool) {
        // Must be valid job id
        require(_jobId < numJobs);
        // Must be valid claim id
        require(_claimId < jobs[_jobId].claims.length);

        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Sender must be elected transcoder for job
        require(job.transcoderAddress == msg.sender);
        // Claim must not be complete
        require(claim.status == ClaimStatus.Pending);
        // Slashing period must be over for claim
        require(claim.endSlashingBlock < block.number);

        uint256 fees = claim.segmentRange[1].sub(claim.segmentRange[0]).add(1).mul(job.maxPricePerSegment);
        // Deduct fees from escrow
        job.escrow = job.escrow.sub(fees);
        // Add fees to transcoder's fee pool
        bondingManager().updateTranscoderFeePool(msg.sender, fees, claim.claimBlock, claim.transcoderTotalStake);

        // Set claim as complete
        claim.status = ClaimStatus.Complete;

        return true;
    }

    /*
     * @dev Compute status of job
     * @param _jobId Job identifier
     */
    function jobStatus(uint256 _jobId) public constant returns (JobStatus) {
        if (jobs[_jobId].endBlock > 0 && jobs[_jobId].endBlock <= block.number) {
            // A job is inactive if its end block is set and the current block is greater than or equal to the job's end block
            return JobStatus.Inactive;
        } else {
            // A job is active if the current block is less than the job's end block
            return JobStatus.Active;
        }
    }

    /*
     * @dev Return claim details
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function getClaimDetails(uint256 _jobId, uint256 _claimId)
        public
        constant
        returns (uint256[2], bytes32, uint256, uint256, uint256, uint256, ClaimStatus)
    {
        Claim storage claim = jobs[_jobId].claims[_claimId];

        return (claim.segmentRange, claim.claimRoot, claim.claimBlock, claim.endVerificationBlock, claim.endSlashingBlock, claim.transcoderTotalStake, claim.status);
    }

    /*
     * @dev Validate a transcode receipt
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     * @param _segmentNumber Segment sequence number in stream
     * @param _dataHash Content-addressed storage hash of segment data
     * @param _transcodedDataHash Hash of transcoded segment data
     * @param _broadcasterSig Broadcaster's signature over segment hash
     * @param _proof Merkle proof for transcode receipt
     */
    function validateReceipt(
        uint256 _jobId,
        uint256 _claimId,
        uint256 _segmentNumber,
        string _dataHash,
        string _transcodedDataHash,
        bytes _broadcasterSig,
        bytes _proof
    )
        internal
        returns (bool)
    {
        if (ECRecovery.recover(JobLib.personalSegmentHash(jobs[_jobId].streamId, _segmentNumber, _dataHash), _broadcasterSig) != jobs[_jobId].broadcasterAddress) return false;
        if (!MerkleProof.verifyProof(_proof, jobs[_jobId].claims[_claimId].claimRoot, JobLib.transcodeReceiptHash(jobs[_jobId].streamId, _segmentNumber, _dataHash, _transcodedDataHash, _broadcasterSig))) return false;

        return true;
    }

    /*
     * @dev Refund broadcaster for a claim
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function refundBroadcaster(uint256 _jobId, uint256 _claimId) internal returns (bool) {
        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Return escrowed fees for claim
        uint256 fees = claim.segmentRange[1].sub(claim.segmentRange[0]).add(1).mul(job.maxPricePerSegment);
        job.escrow = job.escrow.sub(fees);
        broadcasterDeposits[job.broadcasterAddress] = broadcasterDeposits[job.broadcasterAddress].add(fees);

        return true;
    }

    /*
     * @dev Returns BondingManager
     */
    function bondingManager() internal constant returns (IBondingManager) {
        return IBondingManager(ContractRegistry(registry).registry(keccak256("BondingManager")));
    }

}

pragma solidity ^0.4.13;

import "./IJobsManager.sol";
import "./libraries/JobLib.sol";
import "./libraries/MerkleProof.sol";
import "../Manager.sol";
import "../ContractRegistry.sol";
import "../LivepeerToken.sol";
import "../bonding/IBondingManager.sol";
import "../verification/Verifiable.sol";
import "../verification/Verifier.sol";

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

contract JobsManager is IJobsManager, Verifiable, Manager {
    using SafeMath for uint256;

    // Token address
    LivepeerToken public token;

    // Verifier address
    Verifier public verifier;

    // Content-addressed storage hash of verification code
    string public verificationCodeHash;

    // % of segments to be verified. 1 / verificationRate == % to be verified
    uint64 public verificationRate;

    // % of verifications you can fail before being slashed
    uint64 public verificationFailureThreshold;

    // Time between when endJob() is called for a job and when the job is considered inactive. Denominated in blocks
    uint256 public jobEndingPeriod;

    // Time after a transcoder calls claimWork() that it has to complete verification of claimed work
    uint256 public verificationPeriod;

    // Time after a claim's verification period during which anyone can slash the transcoder for missing a required verification
    uint256 public slashingPeriod;

    // % of stake slashed for failed verification
    uint64 public failedVerificationSlashAmount;

    // % of stake slashed for missed verification
    uint64 public missedVerificationSlashAmount;

    // % of of slashed amount awarded to finder
    uint64 public finderFee;

    // Mapping broadcaster => deposited funds for jobs
    mapping (address => uint256) public broadcasterDeposits;

    // Represents a transcode job
    struct Job {
        uint256 jobId;                        // Unique identifer for job
        string streamId;                      // Unique identifier for stream.
        string transcodingOptions;            // Options used for transcoding
        uint256 maxPricePerSegment;           // Max price (in LPT base units) per segment of a stream
        address broadcasterAddress;           // Address of broadcaster that requestes a transcoding job
        address transcoderAddress;            // Address of transcoder selected for the job
        uint256 endBlock;                     // Block at which the job is ended and considered inactive
        Claim[] claims;                       // Claims submitted for this job
        uint256 escrow;                       // Claim fees before verification and slashing periods are complete
    }

    // States of a job
    enum JobStatus { Inactive, Active }

    // Represents a transcode claim
    struct Claim {
        uint256 claimId;                                   // Unique identifier for claim
        uint256[2] segmentRange;                           // Range of segments claimed
        bytes32 claimRoot;                                 // Merkle root of segment transcode proof data
        uint256 claimBlock;                                // Block number that claim was submitted
        uint256 endVerificationBlock;                      // End of verification period for this claim
        uint256 endSlashingBlock;                          // End of slashing period for this claim
        uint256 transcoderTotalStake;                      // Transcoder's total stake at the time of claim
        mapping (uint256 => bool) segmentVerifications;    // Mapping segment number => whether segment was submitted for verification
        ClaimStatus status;                                // Status of claim (pending, slashed, complete)
    }

    // States of a transcode claim
    enum ClaimStatus { Pending, Slashed, Complete }

    // Transcode jobs
    mapping (uint256 => Job) public jobs;
    // Number of jobs created. Also used for sequential identifiers
    uint256 public numJobs;

    // Check if sender is Verifier contract
    modifier onlyVerifier() {
        require(msg.sender == address(verifier));
        _;
    }

    modifier jobExists(uint256 _jobId) {
        require(_jobId < numJobs);
        _;
    }

    modifier claimExists(uint256 _jobId, uint256 _claimId) {
        require(_claimId < jobs[_jobId].claims.length);
        _;
    }

    // Events
    event NewJob(address indexed transcoder, address indexed broadcaster, uint256 jobId);
    event NewClaim(address indexed transcoder, uint256 indexed jobId, uint256 claimId);
    event ReceivedVerification(uint256 indexed jobId, uint256 indexed claimId, uint256 segmentNumber, bool result);

    function JobsManager(address _registry, address _token, address _verifier)
        Manager(_registry)
    {
        // Set LivepeerToken address
        token = LivepeerToken(_token);
        // Set Verifier address
        verifier = Verifier(_verifier);
        // TODO: These are all test values and need to be replaced with realistic values
        // Verify all segments.
        verificationRate = 1;
        // A job becomes inactive 100 blocks after endJob() is called
        jobEndingPeriod = 50;
        // A transcoder has 100 blocks for verification after claiming work
        verificationPeriod = 50;
        // Slashing period after verification is 100 blocks
        slashingPeriod = 50;
        // Stake slashed by 20% for a failed verification
        failedVerificationSlashAmount = 20;
        // Stake slashed by 30% for a missed verification
        missedVerificationSlashAmount = 30;
    }

    /*
     * @dev Deposit funds for jobs
     * @param _amount Amount to deposit
     */
    function deposit(uint256 _amount) external returns (bool) {
        broadcasterDeposits[msg.sender] = broadcasterDeposits[msg.sender].add(_amount);
        // Transfer tokens for deposit. Sender needs to approve amount first
        token.transferFrom(msg.sender, this, _amount);

        return true;
    }

    /*
     * @dev Withdraw deposited funds
     */
    function withdraw() external returns (bool) {
        uint256 amount = broadcasterDeposits[msg.sender];
        broadcasterDeposits[msg.sender] = 0;
        token.transfer(msg.sender, amount);

        return true;
    }

    /*
     * @dev Submit a transcoding job
     * @param _streamId Unique stream identifier
     * @param _transcodingOptions Output bitrates, formats, encodings
     * @param _maxPricePerSegment Max price (in LPT base units) to pay for transcoding a segment of a stream
     */
    function job(string _streamId, string _transcodingOptions, uint256 _maxPricePerSegment) external returns (bool) {
        address electedTranscoder = bondingManager().electActiveTranscoder(_maxPricePerSegment);
        /* There must be an elected transcoder */
        require(electedTranscoder != address(0));

        Job storage job = jobs[numJobs];
        job.jobId = numJobs;
        job.streamId = _streamId;
        job.transcodingOptions = _transcodingOptions;
        job.maxPricePerSegment = _maxPricePerSegment;
        job.broadcasterAddress = msg.sender;
        job.transcoderAddress = electedTranscoder;

        NewJob(electedTranscoder, msg.sender, numJobs);

        // Increment number of created jobs
        numJobs = numJobs.add(1);

        return true;
    }

    /*
     * @dev End a job. Can be called by either a broadcaster or transcoder of a job
     * @param _jobId Job identifier
     */
    function endJob(uint256 _jobId) external returns (bool) {
        Job storage job = jobs[_jobId];

        // Job must not already have an end block
        require(job.endBlock == 0);
        // Sender must be the job's broadcaster or elected transcoder
        require(job.broadcasterAddress == msg.sender || jobs[_jobId].transcoderAddress == msg.sender);

        // Set end block for job
        job.endBlock = block.number.add(jobEndingPeriod);
    }

    /*
     * @dev Submit claim for a range of segments
     * @param _jobId Job identifier
     * @param _segmentRange Range of claimed segments
     * @param _claimRoot Merkle root of transcoded segment proof data for claimed segments
     */
    function claimWork(uint256 _jobId, uint256[2] _segmentRange, bytes32 _claimRoot) jobExists(_jobId) external returns (bool) {
        Job storage job = jobs[_jobId];

        // Job must be active
        require(jobStatus(_jobId) == JobStatus.Active);
        // Sender must be elected transcoder
        require(job.transcoderAddress == msg.sender);

        // Move fees from broadcaster deposit to escrow
        uint256 fees = _segmentRange[1].sub(_segmentRange[0]).add(1).mul(job.maxPricePerSegment);
        broadcasterDeposits[job.broadcasterAddress] = broadcasterDeposits[job.broadcasterAddress].sub(fees);
        job.escrow = job.escrow.add(fees);

        job.claims.push(Claim({
            claimId: job.claims.length,
            segmentRange: _segmentRange,
            claimRoot: _claimRoot,
            claimBlock: block.number,
            transcoderTotalStake: bondingManager().transcoderTotalStake(msg.sender),
            endVerificationBlock: block.number.add(verificationPeriod),
            endSlashingBlock: block.number.add(verificationPeriod).add(slashingPeriod),
            status: ClaimStatus.Pending
        }));

        NewClaim(job.transcoderAddress, _jobId, job.claims.length - 1);

        return true;
    }

    /*
     * @dev Submit transcode receipt and invoke transcoding verification
     * @param _jobId Job identifier
     * @param _segmentNumber Segment sequence number in stream
     * @param _dataHash Content-addressed storage hash of segment data
     * @param _transcodedDataHash Hash of transcoded segment data
     * @param _broadcasterSig Broadcaster's signature over segment hash
     * @param _proof Merkle proof for transcode receipt
     */
    function verify(
        uint256 _jobId,
        uint256 _claimId,
        uint256 _segmentNumber,
        string _dataHash,
        string _transcodedDataHash,
        bytes _broadcasterSig,
        bytes _proof
    )
        external
        payable
        returns (bool)
    {
        // Must be valid job id
        require(_jobId < numJobs);
        // Must be valid claim id
        require(_claimId < jobs[_jobId].claims.length);

        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Job must be active
        require(jobStatus(_jobId) == JobStatus.Active);
        // Sender must be elected transcoder
        require(job.transcoderAddress == msg.sender);

        // Segment must be eligible for verification
        require(JobLib.shouldVerifySegment(_segmentNumber, claim.segmentRange, claim.claimBlock, verificationRate));
        // Receipt must be valid
        require(validateReceipt(_jobId, _claimId, _segmentNumber, _dataHash, _transcodedDataHash, _broadcasterSig, _proof));

        // Mark segment as submitted for verification
        claim.segmentVerifications[_segmentNumber] = true;

        // Invoke transcoding verification. This is async and will result in a callback to receiveVerification() which is implemented by this contract
        verifier.verify(_jobId, _claimId, _segmentNumber, _dataHash, _transcodedDataHash, this);

        return true;
    }

    /*
     * @dev Callback function that receives the results of transcoding verification
     * @param _jobId Job identifier
     * @param _segmentNumber Segment being verified for job
     * @param _result Boolean result of whether verification succeeded or not
     */
    function receiveVerification(uint256 _jobId, uint256 _claimId, uint256 _segmentNumber, bool _result) onlyVerifier external returns (bool) {
        if (!_result) {
            // Protocol slashes transcoder for failing verification (no finder)
            bondingManager().slashTranscoder(jobs[_jobId].transcoderAddress, address(0), failedVerificationSlashAmount, 0);
        }

        ReceivedVerification(_jobId, _claimId, _segmentNumber, _result);

        return true;
    }

    /*
     * @dev Distribute fees for multiple claims
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function batchDistributeFees(uint256 _jobId, uint256[] _claimIds) external returns (bool) {
        for (uint256 i = 0; i < _claimIds.length; i++) {
            distributeFees(_jobId, _claimIds[i]);
        }

        return true;
    }

    /*
     * @dev Slash transcoder for missing verification
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     * @param _segmentNumber Segment that was not verified
     */
    function missedVerificationSlash(uint256 _jobId, uint256 _claimId, uint256 _segmentNumber) external returns (bool) {
        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Must be after verification period
        require(block.number > claim.endVerificationBlock);
        // Must be before end of slashing period
        require(block.number <= claim.endSlashingBlock);
        // Claim must be pending
        require(claim.status == ClaimStatus.Pending);
        // Segment must be eligible for verification
        require(JobLib.shouldVerifySegment(_segmentNumber, claim.segmentRange, claim.claimBlock, verificationRate));
        // Transcoder must have missed verification for the segment
        require(!claim.segmentVerifications[_segmentNumber]);

        // Return escrowed fees for claim
        uint256 fees = claim.segmentRange[1].sub(claim.segmentRange[0]).add(1).mul(job.maxPricePerSegment);
        job.escrow = job.escrow.sub(fees);
        broadcasterDeposits[job.broadcasterAddress] = broadcasterDeposits[job.broadcasterAddress].add(fees);

        // Slash transcoder and provide finder params
        bondingManager().slashTranscoder(job.transcoderAddress, msg.sender, missedVerificationSlashAmount, finderFee);

        // Set claim as slashed
        claim.status = ClaimStatus.Slashed;

        return true;
    }

    /*
     * @dev Distribute fees for a particular claim
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function distributeFees(uint256 _jobId, uint256 _claimId) public returns (bool) {
        // Must be valid job id
        require(_jobId < numJobs);
        // Must be valid claim id
        require(_claimId < jobs[_jobId].claims.length);

        Job storage job = jobs[_jobId];
        Claim storage claim = job.claims[_claimId];

        // Sender must be elected transcoder for job
        require(job.transcoderAddress == msg.sender);
        // Claim must not be complete
        require(claim.status == ClaimStatus.Pending);
        // Slashing period must be over for claim
        require(claim.endSlashingBlock < block.number);

        uint256 fees = claim.segmentRange[1].sub(claim.segmentRange[0]).add(1).mul(job.maxPricePerSegment);
        // Deduct fees from escrow
        job.escrow = job.escrow.sub(fees);
        // Add fees to transcoder's fee pool
        bondingManager().updateTranscoderFeePool(msg.sender, fees, claim.claimBlock, claim.transcoderTotalStake);

        // Set claim as complete
        claim.status = ClaimStatus.Complete;

        return true;
    }

    /*
     * @dev Compute status of job
     * @param _jobId Job identifier
     */
    function jobStatus(uint256 _jobId) public constant returns (JobStatus) {
        if (jobs[_jobId].endBlock > 0 && jobs[_jobId].endBlock <= block.number) {
            // A job is inactive if its end block is set and the current block is greater than or equal to the job's end block
            return JobStatus.Inactive;
        } else {
            // A job is active if the current block is less than the job's end block
            return JobStatus.Active;
        }
    }

    /*
     * @dev Return claim details
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     */
    function getClaimDetails(uint256 _jobId, uint256 _claimId)
        public
        constant
        returns (uint256[2], bytes32, uint256, uint256, uint256, uint256, ClaimStatus)
    {
        Claim storage claim = jobs[_jobId].claims[_claimId];

        return (claim.segmentRange, claim.claimRoot, claim.claimBlock, claim.endVerificationBlock, claim.endSlashingBlock, claim.transcoderTotalStake, claim.status);
    }

    /*
     * @dev Validate a transcode receipt
     * @param _jobId Job identifier
     * @param _claimId Claim identifier
     * @param _segmentNumber Segment sequence number in stream
     * @param _dataHash Content-addressed storage hash of segment data
     * @param _transcodedDataHash Hash of transcoded segment data
     * @param _broadcasterSig Broadcaster's signature over segment hash
     * @param _proof Merkle proof for transcode receipt
     */
    function validateReceipt(
        uint256 _jobId,
        uint256 _claimId,
        uint256 _segmentNumber,
        string _dataHash,
        string _transcodedDataHash,
        bytes _broadcasterSig,
        bytes _proof
    )
        internal
        returns (bool)
    {
        if (ECRecovery.recover(JobLib.personalSegmentHash(jobs[_jobId].streamId, _segmentNumber, _dataHash), _broadcasterSig) != jobs[_jobId].broadcasterAddress) return false;
        if (!MerkleProof.verifyProof(_proof, jobs[_jobId].claims[_claimId].claimRoot, JobLib.transcodeReceiptHash(jobs[_jobId].streamId, _segmentNumber, _dataHash, _transcodedDataHash, _broadcasterSig))) return false;

        return true;
    }

    /*
     * @dev Returns BondingManager
     */
    function bondingManager() internal constant returns (IBondingManager) {
        return IBondingManager(ContractRegistry(registry).registry(keccak256("BondingManager")));
    }

}