typo fixes

typo fixes

pragma solidity ^0.4.0;

import "./Token.sol";

library NettingChannelLibrary {
    struct Participant
    {
        address node_address;

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

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

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

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

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

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

    modifier notSettledButClosed(Data storage self) {
        if (self.settled > 0 || self.closed == 0)
            throw;
        _;
    }

    modifier stillTimeout(Data storage self) {
        if (self.closed + self.settle_timeout < block.number)
            throw;
        _;
    }

    modifier timeoutOver(Data storage self) {
        if (self.closed + self.settle_timeout > block.number)
            throw;
        _;
    }

    modifier channelSettled(Data storage self) {
        if (self.settled == 0)
            throw;
        _;
    }

    function isValidNonce(Data storage self, uint64 nonce)
        private
        returns (bool)
    {
        return (
            nonce >= self.opened * (2 ** 32) &&
            nonce < (self.opened + 1) * (2 ** 32)
        );
    }

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

        if (self.closed != 0) {
            throw;
        }

        if (self.token.balanceOf(msg.sender) < amount) {
            throw;
        }

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

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

            if (self.opened == 0) {
                self.opened = block.number;
            }

            return (true, balance);
        }

        return (false, 0);
    }

    /// @notice Close a channel between two parties that was used bidirectionally
    /// @param their_transfer The latest known transfer of the other participant
    ///                       to the channel. Can also be empty, in which case
    ///                       we are attempting to close a channel without any
    ///                       transfers.
    function close(Data storage self, bytes their_transfer)
    {
        uint closer_index;
        uint counterparty_index;
        bytes memory transfer_raw;
        uint64 nonce;
        address transfer_address;
        bytes32 locksroot;
        uint256 transferred_amount;

        // close can be called only once
        if (self.closed > 0) {
            throw;
        }

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

        self.closing_address = msg.sender;
        self.closed = block.number;

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

        // An empty value means that the closer never received a transfer, or
        // he is intentionally not providing the latest transfer, in which case
        // the closing party is going to lose the tokens that were transferred
        // to him.
        if (their_transfer.length != 0) {
            (transfer_raw, transfer_address) = getTransferRawAddress(their_transfer);
            counterparty_index = index_or_throw(self, transfer_address);

            // only a message from the counter party is valid
            if (closer_index == counterparty_index) {
                throw;
            }

            // update the structure of the counterparty with its data provided
            // by the closing node
            Participant storage counterparty = self.participants[counterparty_index];

            (nonce, locksroot, transferred_amount) = decodeTransfer(transfer_raw);

            // only accept messages with a valid nonce
            if (!isValidNonce(self, nonce)) {
                throw;
            }

            counterparty.nonce = nonce;
            counterparty.locksroot = locksroot;
            counterparty.transferred_amount = transferred_amount;
        }

    }

    /// @notice Updates counter party transfer after closing.
    /// @param their_transfer The transfer the counterparty believes is the
    ///                       valid state for the first participant.
    function updateTransfer(Data storage self, bytes their_transfer)
        notSettledButClosed(self)
        stillTimeout(self)
    {
        address transfer_address;
        bytes32 locksroot;
        bytes memory transfer_raw;
        uint256 transferred_amount;
        uint64 nonce;
        uint8 caller_index;
        uint8 closer_index;

        // updateTransfer can be called by the counter party only once
        if (self.updated) {
            throw;
        }
        self.updated = true;

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

        // The closer is not allowed to call updateTransfer
        if (self.closing_address == msg.sender) {
            throw;
        }

        (transfer_raw, transfer_address) = getTransferRawAddress(their_transfer);

        // Counter party can only update the closer transfer
        if (transfer_address != self.closing_address) {
            throw;
        }

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

        (nonce, locksroot, transferred_amount) = decodeTransfer(transfer_raw);

        // only accept messages with a valid nonce
        if (!isValidNonce(self, nonce)) {
            throw;
        }

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

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

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

        // An empty locksroot means there are no pending locks
        if (counterparty.locksroot == 0) {
            throw;
        }

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

        // A lock can be withdrawn only once per participant
        if (counterparty.withdrawn_locks[hashlock]) {
            throw;
        }
        counterparty.withdrawn_locks[hashlock] = true;

        if (expiration < block.number) {
            throw;
        }

        if (hashlock != sha3(secret)) {
            throw;
        }

        h = computeMerkleRoot(locked_encoded, merkle_proof);

        if (counterparty.locksroot != h) {
            throw;
        }

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

    function computeMerkleRoot(bytes lock, bytes merkle_proof)
        private
        constant
        returns (bytes32)
    {
        if (merkle_proof.length % 32 != 0) {
            throw;
        }

        uint i;
        bytes32 h;
        bytes32 el;

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

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

        return h;
    }

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

        self.settled = block.number;

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

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

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

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

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

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

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

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

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

        kill(self);
    }

    function getTransferRawAddress(bytes memory signed_transfer) internal returns (bytes memory, address) {
        uint signature_start;
        uint length;
        bytes memory signature;
        bytes memory transfer_raw;
        bytes32 transfer_hash;
        address transfer_address;

        length = signed_transfer.length;
        signature_start = length - 65;
        signature = slice(signed_transfer, signature_start, length);
        transfer_raw = slice(signed_transfer, 0, signature_start);

        transfer_hash = sha3(transfer_raw);
        var (r, s, v) = signatureSplit(signature);
        transfer_address = ecrecover(transfer_hash, v, r, s);

        return (transfer_raw, transfer_address);
    }

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

    function decodeTransfer(bytes transfer_raw)
        internal
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        uint cmdid = uint(transfer_raw[0]);

        if (cmdid == 5) {
            return decodeDirectTransfer(transfer_raw);
        } else if (cmdid == 7) {
            return decodeMediatedTransfer(transfer_raw);
        } else if (cmdid == 8) {
            return decodeRefundTransfer(transfer_raw);
        }

        throw;
    }

    function decodeDirectTransfer(bytes memory message)
        private
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        // size of the raw message without the signature
        if (message.length != 124) {
            throw;
        }

        // Message format:
        // [0:1] cmdid
        // [1:4] pad
        // [4:12] nonce
        // [12:20] identifier
        // [20:40] token
        // [40:60] recipient
        // [60:92] transferred_amount
        // [92:124] optional_locksroot
        assembly {
            nonce := mload(add(message, 12))
            transferred_amount := mload(add(message, 92))
            locksroot := mload(add(message, 124))
        }
    }

    function decodeMediatedTransfer(bytes memory message)
        private
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        // size of the raw message without the signature
        if (message.length != 268) {
            throw;
        }

        // Message format:
        // [0:1] cmdid
        // [1:4] pad
        // [4:12] nonce
        // [12:20] identifier
        // [20:28] expiration
        // [28:48] token
        // [48:68] recipient
        // [68:88] target
        // [88:108] initiator
        // [108:140] locksroot
        // [140:172] hashlock
        // [172:204] transferred_amoun
        // [204:236] amount
        // [236:268] fee
        assembly {
            nonce := mload(add(message, 12))
            locksroot := mload(add(message, 140))
            transferred_amount := mload(add(message, 204))
        }
    }

    function decodeRefundTransfer(bytes memory message)
        private
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        // size of the raw message without the signature
        if (message.length != 196) {
            throw;
        }

        // Message format:
        // [0:1] cmdid
        // [1:4] pad
        // [4:12] nonce
        // [12:20] identifier
        // [20:28] expiration
        // [28:48] token
        // [48:68] recipient
        // [68:100] locksroot
        // [100:132] transferred_amount
        // [132:164] amount
        // [164:196] hashlock
        assembly {
            nonce := mload(add(message, 12))
            locksroot := mload(add(message, 100))
            transferred_amount := mload(add(message, 132))
        }
    }

    function decodeLock(bytes lock) internal returns (uint64 expiration, uint amount, bytes32 hashlock) {
        if (lock.length != 72) {
            throw;
        }

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

    // Get nonce from a message
    function getNonce(bytes message) internal returns (uint64 nonce) {
        // don't care about length of message since nonce is always at a fixed position
        assembly {
            nonce := mload(add(message, 12))
        }
    }

    function signatureSplit(bytes signature) private returns (bytes32 r, bytes32 s, uint8 v) {
        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(signature, 65)), 1)
        }
        // old geth sends a `v` value of [0,1], while the new, in line with the YP sends [27,28]
        if(v < 27) v += 27;
    }

    function slice(bytes a, uint start, uint end) private returns (bytes n) {
        if (a.length < end) {
            throw;
        }
        if (start < 0) {
            throw;
        }

        n = new bytes(end - start);
        for (uint i = start; i < end; i++) { //python style slice
            n[i - start] = a[i];
        }
    }

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

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

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

    function kill(Data storage self) channelSettled(self) {
        selfdestruct(0x00000000000000000000);
    }
}

pragma solidity ^0.4.0;

import "./Token.sol";

library NettingChannelLibrary {
    struct Participant
    {
        address node_address;

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

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

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

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

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

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

    modifier notSettledButClosed(Data storage self) {
        if (self.settled > 0 || self.closed == 0)
            throw;
        _;
    }

    modifier stillTimeout(Data storage self) {
        if (self.closed + self.settle_timeout < block.number)
            throw;
        _;
    }

    modifier timeoutOver(Data storage self) {
        if (self.closed + self.settle_timeout > block.number)
            throw;
        _;
    }

    modifier channelSettled(Data storage self) {
        if (self.settled == 0)
            throw;
        _;
    }

    function isValidNonce(Data storage self, uint64 nonce)
        private
        returns (bool)
    {
        return (
            nonce >= self.opened * (2 ** 32) &&
            nonce < (self.opened + 1) * (2 ** 32)
        );
    }

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

        if (self.closed != 0) {
            throw;
        }

        if (self.token.balanceOf(msg.sender) < amount) {
            throw;
        }

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

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

            if (self.opened == 0) {
                self.opened = block.number;
            }

            return (true, balance);
        }

        return (false, 0);
    }

    /// @notice Close a channel between two parties that was used bidirectionally
    /// @param their_transfer The latest known transfer of the other participant
    ///                       to the channel. Can also be empty, in which case
    ///                       we are attempting to close a channel without any
    ///                       transfers.
    function close(Data storage self, bytes their_transfer)
    {
        uint closer_index;
        uint counterparty_index;
        bytes memory transfer_raw;
        uint64 nonce;
        address transfer_address;
        bytes32 locksroot;
        uint256 transferred_amount;

        // close can be called only once
        if (self.closed > 0) {
            throw;
        }

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

        self.closing_address = msg.sender;
        self.closed = block.number;

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

        // An empty value means that the closer never received a transfer, or
        // he is intentionally not providing the latest transfer, in which case
        // the closing party is going to lose the tokens that were transferred
        // to him.
        if (their_transfer.length != 0) {
            (transfer_raw, transfer_address) = getTransferRawAddress(their_transfer);
            counterparty_index = index_or_throw(self, transfer_address);

            // only a message from the counter party is valid
            if (closer_index == counterparty_index) {
                throw;
            }

            // update the structure of the counterparty with its data provided
            // by the closing node
            Participant storage counterparty = self.participants[counterparty_index];

            (nonce, locksroot, transferred_amount) = decodeTransfer(transfer_raw);

            // only accept messages with a valid nonce
            if (!isValidNonce(self, nonce)) {
                throw;
            }

            counterparty.nonce = nonce;
            counterparty.locksroot = locksroot;
            counterparty.transferred_amount = transferred_amount;
        }

    }

    /// @notice Updates counter party transfer after closing.
    /// @param their_transfer The transfer the counterparty believes is the
    ///                       valid state for the first participant.
    function updateTransfer(Data storage self, bytes their_transfer)
        notSettledButClosed(self)
        stillTimeout(self)
    {
        address transfer_address;
        bytes32 locksroot;
        bytes memory transfer_raw;
        uint256 transferred_amount;
        uint64 nonce;
        uint8 caller_index;
        uint8 closer_index;

        // updateTransfer can be called by the counter party only once
        if (self.updated) {
            throw;
        }
        self.updated = true;

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

        // The closer is not allowed to call updateTransfer
        if (self.closing_address == msg.sender) {
            throw;
        }

        (transfer_raw, transfer_address) = getTransferRawAddress(their_transfer);

        // Counter party can only update the closer transfer
        if (transfer_address != self.closing_address) {
            throw;
        }

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

        (nonce, locksroot, transferred_amount) = decodeTransfer(transfer_raw);

        // only accept messages with a valid nonce
        if (!isValidNonce(self, nonce)) {
            throw;
        }

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

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

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

        // An empty locksroot means there are no pending locks
        if (counterparty.locksroot == 0) {
            throw;
        }

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

        // A lock can be withdrawn only once per participant
        if (counterparty.withdrawn_locks[hashlock]) {
            throw;
        }
        counterparty.withdrawn_locks[hashlock] = true;

        if (expiration < block.number) {
            throw;
        }

        if (hashlock != sha3(secret)) {
            throw;
        }

        h = computeMerkleRoot(locked_encoded, merkle_proof);

        if (counterparty.locksroot != h) {
            throw;
        }

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

    function computeMerkleRoot(bytes lock, bytes merkle_proof)
        private
        constant
        returns (bytes32)
    {
        if (merkle_proof.length % 32 != 0) {
            throw;
        }

        uint i;
        bytes32 h;
        bytes32 el;

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

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

        return h;
    }

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

        self.settled = block.number;

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

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

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

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

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

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

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

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

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

        kill(self);
    }

    function getTransferRawAddress(bytes memory signed_transfer) internal returns (bytes memory, address) {
        uint signature_start;
        uint length;
        bytes memory signature;
        bytes memory transfer_raw;
        bytes32 transfer_hash;
        address transfer_address;

        length = signed_transfer.length;
        signature_start = length - 65;
        signature = slice(signed_transfer, signature_start, length);
        transfer_raw = slice(signed_transfer, 0, signature_start);

        transfer_hash = sha3(transfer_raw);
        var (r, s, v) = signatureSplit(signature);
        transfer_address = ecrecover(transfer_hash, v, r, s);

        return (transfer_raw, transfer_address);
    }

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

    function decodeTransfer(bytes transfer_raw)
        internal
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        uint cmdid = uint(transfer_raw[0]);

        if (cmdid == 5) {
            return decodeDirectTransfer(transfer_raw);
        } else if (cmdid == 7) {
            return decodeMediatedTransfer(transfer_raw);
        } else if (cmdid == 8) {
            return decodeRefundTransfer(transfer_raw);
        }

        throw;
    }

    function decodeDirectTransfer(bytes memory message)
        private
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        // size of the raw message without the signature
        if (message.length != 124) {
            throw;
        }

        // Message format:
        // [0:1] cmdid
        // [1:4] pad
        // [4:12] nonce
        // [12:20] identifier
        // [20:40] token
        // [40:60] recipient
        // [60:92] transferred_amount
        // [92:124] optional_locksroot
        assembly {
            nonce := mload(add(message, 12))
            transferred_amount := mload(add(message, 92))
            locksroot := mload(add(message, 124))
        }
    }

    function decodeMediatedTransfer(bytes memory message)
        private
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        // size of the raw message without the signature
        if (message.length != 268) {
            throw;
        }

        // Message format:
        // [0:1] cmdid
        // [1:4] pad
        // [4:12] nonce
        // [12:20] identifier
        // [20:28] expiration
        // [28:48] token
        // [48:68] recipient
        // [68:88] target
        // [88:108] initiator
        // [108:140] locksroot
        // [140:172] hashlock
        // [172:204] transferred_amoun
        // [204:236] amount
        // [236:268] fee
        assembly {
            nonce := mload(add(message, 12))
            locksroot := mload(add(message, 140))
            transferred_amount := mload(add(message, 204))
        }
    }

    function decodeRefundTransfer(bytes memory message)
        private
        returns (uint64 nonce, bytes32 locksroot, uint256 transferred_amount)
    {
        // size of the raw message without the signature
        if (message.length != 196) {
            throw;
        }

        // Message format:
        // [0:1] cmdid
        // [1:4] pad
        // [4:12] nonce
        // [12:20] identifier
        // [20:28] expiration
        // [28:48] token
        // [48:68] recipient
        // [68:100] locksroot
        // [100:132] transferred_amount
        // [132:164] amount
        // [164:196] hashlock
        assembly {
            nonce := mload(add(message, 12))
            locksroot := mload(add(message, 100))
            transferred_amount := mload(add(message, 132))
        }
    }

    function decodeLock(bytes lock) internal returns (uint64 expiration, uint amount, bytes32 hashlock) {
        if (lock.length != 72) {
            throw;
        }

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

    // Get nonce from a message
    function getNonce(bytes message) internal returns (uint64 nonce) {
        // don't care about length of message since nonce is always at a fixed position
        assembly {
            nonce := mload(add(message, 12))
        }
    }

    function signatureSplit(bytes signature) private returns (bytes32 r, bytes32 s, uint8 v) {
        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(signature, 65)), 1)
        }
        // old geth sends a `v` value of [0,1], while the new, in line with the YP sends [27,28]
        if(v < 27) v += 27;
    }

    function slice(bytes a, uint start, uint end) private returns (bytes n) {
        if (a.length < end) {
            throw;
        }
        if (start < 0) {
            throw;
        }

        n = new bytes(end - start);
        for (uint i = start; i < end; i++) { //python style slice
            n[i - start] = a[i];
        }
    }

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

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

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

    function kill(Data storage self) channelSettled(self) {
        selfdestruct(0x00000000000000000000);
    }
}


Use safeTimelock modifier to simplify team release function

pragma solidity ^0.4.15;
import './ERC20Token.sol';
import './TokenHolder.sol';

contract ENJToken is ERC20Token, TokenHolder {

///////////////////////////////////////// VARIABLE INITIALIZATION /////////////////////////////////////////

    uint256 constant public ENJ_UNIT = 10 ** 18;
    uint256 public totalSupply = 1 * (10**9) * ENJ_UNIT;

    //  Constants
    uint256 constant public maxPresaleSupply = 600 * 10**6 * ENJ_UNIT;           // Total presale supply at max bonus
    uint256 constant public minCrowdsaleAllocation = 200 * 10**6 * ENJ_UNIT;     // Min amount for crowdsale
    uint256 constant public incentivisationAllocation = 100 * 10**6 * ENJ_UNIT;  // Incentivisation Allocation
    uint256 constant public advisorsAllocation = 50 * 10**6 * ENJ_UNIT;          // Advisors Allocation
    uint256 constant public enjinTeamAllocation = 50 * 10**6 * ENJ_UNIT;         // Enjin Team allocation

    address public crowdFundAddress;                                             // Address of the crowdfund
    address public advisorAddress;                                               // Enjin advisor's address
    address public incentivisationFundAddress;                                   // Address that holds the incentivization funds
    address public enjinTeamAddress;                                             // Enjin Team address

    //  Variables

    uint256 public totalAllocatedToAdvisors = 0;                                 // Counter to keep track of total Advisor allocation
    uint256 public totalAllocatedToTeam = 0;                                     // Counter to keep track of team token allocation
    uint256 public totalAllocated = 0;                                           // Counter to keep track of team token allocation
    uint256 constant public endTime = 1509494340;                                // 10/31/2017 @ 11:59pm (UTC) crowdsale end time (in seconds)

    bool internal isReleasedToPublic = false;                         // Flag to allow transfer/transferFrom before the end of the crowdfund

    uint256 internal teamTranchesReleased = 0;                          // Track how many tranches (allocations of 12.5% team tokens) have been released
    uint256 internal maxTeamTranches = 8;                               // The number of tranches allowed to the team until depleted

///////////////////////////////////////// MODIFIERS /////////////////////////////////////////

    // Enjin Team timelock
    modifier safeTimelock() {
        require(now >= endTime + 6 * 4 weeks);
        _;
    }

    // Advisor Team timelock
    modifier advisorTimelock() {
        require(now >= endTime + 2 * 4 weeks);
        _;
    }

    // Function only accessible by the Crowdfund contract
    modifier crowdfundOnly() {
        require(msg.sender == crowdFundAddress);
        _;
    }

    ///////////////////////////////////////// CONSTRUCTOR /////////////////////////////////////////

    /**
        @dev constructor
        @param _crowdFundAddress   Crowdfund address
        @param _advisorAddress     Advisor address
    */
    function ENJToken(address _crowdFundAddress, address _advisorAddress, address _incentivisationFundAddress, address _enjinTeamAddress)
    ERC20Token("ENJ Coin", "ENJ", 18)
     {
        crowdFundAddress = _crowdFundAddress;
        advisorAddress = _advisorAddress;
        enjinTeamAddress = _enjinTeamAddress;
        incentivisationFundAddress = _incentivisationFundAddress;
        balanceOf[_crowdFundAddress] = minCrowdsaleAllocation + maxPresaleSupply; // Total presale + crowdfund tokens
        balanceOf[_incentivisationFundAddress] = incentivisationAllocation;       // 10% Allocated for Marketing and Incentivisation
        totalAllocated += incentivisationAllocation;                              // Add to total Allocated funds
    }

///////////////////////////////////////// ERC20 OVERRIDE /////////////////////////////////////////

    /**
        @dev send coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transfer(address _to, uint256 _value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transfer(_to, _value));
            return true;
        }
        revert();
    }

    /**
        @dev an account/contract attempts to get the coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _from    source address
        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transferFrom(_from, _to, _value));
            return true;
        }
        revert();
    }

///////////////////////////////////////// ALLOCATION FUNCTIONS /////////////////////////////////////////

    /**
        @dev Release one single tranche of the Enjin Team Token allocation
        throws if before timelock (6 months) ends and if not initiated by the owner of the contract
        returns true if valid
        Schedule goes as follows:
        3 months: 12.5% (this tranche can only be released after the initial 6 months has passed)
        6 months: 12.5%
        9 months: 12.5%
        12 months: 12.5%
        15 months: 12.5%
        18 months: 12.5%
        21 months: 12.5%
        24 months: 12.5%
        @return true if successful, throws if not
    */
    function releaseEnjinTeamTokens() safeTimelock ownerOnly returns(bool success) {
        require(totalAllocatedToTeam < enjinTeamAllocation);

        uint256 enjinTeamAlloc = enjinTeamAllocation / 1000;
        uint256 currentTranche = uint256(now - endTime) / 12 weeks;     // "months" after crowdsale end time (division floored)

        if(teamTranchesReleased < maxTeamTranches && currentTranche > teamTranchesReleased) {
            teamTranchesReleased++;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        }
        revert();
    }

    /**
        @dev transfers Tokens from Enjin allocation to Team
        throws if before first timelock (6 months) ends and if no initiated by the owner of the contract
        @return true if successful, throws if not
    */
    function transferTeamAllocation(uint256 _amount) ownerOnly internal {
        balanceOf[enjinTeamAddress] = safeAdd(balanceOf[enjinTeamAddress], _amount);
        Transfer(0x0, enjinTeamAddress, _amount);
        totalAllocated = safeAdd(totalAllocated, _amount);
        totalAllocatedToTeam = safeAdd(totalAllocatedToTeam, _amount);
    }

    /**
        @dev release Advisors Token allocation
        throws if before timelock (2 months) ends or if no initiated by the advisors address
        or if there is no more allocation to give out
        returns true if valid

        @return true if successful, throws if not
    */
    function releaseAdvisorTokens() advisorTimelock ownerOnly returns(bool success) {
        require(totalAllocatedToAdvisors == 0);
        balanceOf[advisorAddress] = safeAdd(balanceOf[advisorAddress], advisorsAllocation);
        totalAllocated = safeAdd(totalAllocated, advisorsAllocation);
        totalAllocatedToAdvisors = advisorsAllocation;
        Transfer(0x0, advisorAddress, advisorsAllocation);
        return true;
    }

    /**
        @dev Retrieve unsold tokens from the crowdfund
        throws if before timelock (6 months from end of Crowdfund) ends and if no initiated by the owner of the contract
        returns true if valid

        @return true if successful, throws if not
    */
    function retrieveUnsoldTokens() safeTimelock ownerOnly returns(bool success) {
        uint256 amountOfTokens = balanceOf[crowdFundAddress];
        balanceOf[crowdFundAddress] = 0;
        balanceOf[incentivisationFundAddress] = safeAdd(balanceOf[incentivisationFundAddress], amountOfTokens);
        totalAllocated = safeAdd(totalAllocated, amountOfTokens);
        return true;
    }

    /**
        @dev Keep track of token allocations
        can only be called by the crowdfund contract
    */
    function addToAllocation(uint256 _amount) crowdfundOnly {
        totalAllocated = safeAdd(totalAllocated, _amount);
    }

    /**
        @dev Function to allow transfers
        can only be called by the owner of the contract
        Transfers will be allowed regardless after the crowdfund end time.
    */
    function allowTransfers() ownerOnly {
        isReleasedToPublic = true;
    }

    /**
        @dev User transfers are allowed/rejected
        Transfers are forbidden before the end of the crowdfund
    */
    function isTransferAllowed() internal constant returns(bool) {
        if (now > endTime || isReleasedToPublic == true) {
            return true;
        }
        return false;
    }
}

pragma solidity ^0.4.15;
import './ERC20Token.sol';
import './TokenHolder.sol';

contract ENJToken is ERC20Token, TokenHolder {

///////////////////////////////////////// VARIABLE INITIALIZATION /////////////////////////////////////////

    uint256 constant public ENJ_UNIT = 10 ** 18;
    uint256 public totalSupply = 1 * (10**9) * ENJ_UNIT;

    //  Constants
    uint256 constant public maxPresaleSupply = 600 * 10**6 * ENJ_UNIT;           // Total presale supply at max bonus
    uint256 constant public minCrowdsaleAllocation = 200 * 10**6 * ENJ_UNIT;     // Min amount for crowdsale
    uint256 constant public incentivisationAllocation = 100 * 10**6 * ENJ_UNIT;  // Incentivisation Allocation
    uint256 constant public advisorsAllocation = 50 * 10**6 * ENJ_UNIT;          // Advisors Allocation
    uint256 constant public enjinTeamAllocation = 50 * 10**6 * ENJ_UNIT;         // Enjin Team allocation

    address public crowdFundAddress;                                             // Address of the crowdfund
    address public advisorAddress;                                               // Enjin advisor's address
    address public incentivisationFundAddress;                                   // Address that holds the incentivization funds
    address public enjinTeamAddress;                                             // Enjin Team address

    //  Variables

    uint256 public totalAllocatedToAdvisors = 0;                                 // Counter to keep track of total Advisor allocation
    uint256 public totalAllocatedToTeam = 0;                                     // Counter to keep track of team token allocation
    uint256 public totalAllocated = 0;                                           // Counter to keep track of team token allocation
    uint256 constant public endTime = 1509494340;                                // 10/31/2017 @ 11:59pm (UTC) crowdsale end time (in seconds)

    bool internal isReleasedToPublic = false;                         // Flag to allow transfer/transferFrom before the end of the crowdfund

    uint256 internal teamTranchesReleased = 0;                          // Track how many tranches (allocations of 12.5% team tokens) have been released
    uint256 internal maxTeamTranches = 8;                               // The number of tranches allowed to the team until depleted

///////////////////////////////////////// MODIFIERS /////////////////////////////////////////

    // Enjin Team timelock
    modifier safeTimelock() {
        require(now >= endTime + 6 * 4 weeks);
        _;
    }

    // Advisor Team timelock
    modifier advisorTimelock() {
        require(now >= endTime + 2 * 4 weeks);
        _;
    }

    // Function only accessible by the Crowdfund contract
    modifier crowdfundOnly() {
        require(msg.sender == crowdFundAddress);
        _;
    }

    ///////////////////////////////////////// CONSTRUCTOR /////////////////////////////////////////

    /**
        @dev constructor
        @param _crowdFundAddress   Crowdfund address
        @param _advisorAddress     Advisor address
    */
    function ENJToken(address _crowdFundAddress, address _advisorAddress, address _incentivisationFundAddress, address _enjinTeamAddress)
    ERC20Token("ENJ Coin", "ENJ", 18)
     {
        crowdFundAddress = _crowdFundAddress;
        advisorAddress = _advisorAddress;
        enjinTeamAddress = _enjinTeamAddress;
        incentivisationFundAddress = _incentivisationFundAddress;
        balanceOf[_crowdFundAddress] = minCrowdsaleAllocation + maxPresaleSupply; // Total presale + crowdfund tokens
        balanceOf[_incentivisationFundAddress] = incentivisationAllocation;       // 10% Allocated for Marketing and Incentivisation
        totalAllocated += incentivisationAllocation;                              // Add to total Allocated funds
    }

///////////////////////////////////////// ERC20 OVERRIDE /////////////////////////////////////////

    /**
        @dev send coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transfer(address _to, uint256 _value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transfer(_to, _value));
            return true;
        }
        revert();
    }

    /**
        @dev an account/contract attempts to get the coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _from    source address
        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transferFrom(_from, _to, _value));
            return true;
        }
        revert();
    }

///////////////////////////////////////// ALLOCATION FUNCTIONS /////////////////////////////////////////

    /**
        @dev Release one single tranche of the Enjin Team Token allocation
        throws if before timelock (6 months) ends and if not initiated by the owner of the contract
        returns true if valid
        Schedule goes as follows:
        3 months: 12.5% (this tranche can only be released after the initial 6 months has passed)
        6 months: 12.5%
        9 months: 12.5%
        12 months: 12.5%
        15 months: 12.5%
        18 months: 12.5%
        21 months: 12.5%
        24 months: 12.5%
        @return true if successful, throws if not
    */
    function releaseEnjinTeamTokens() ownerOnly returns(bool success) {
        require(totalAllocatedToTeam < enjinTeamAllocation);

        uint256 enjinTeamAlloc = enjinTeamAllocation / 1000;
        uint256 currentTranche = uint256(now - endTime) / 12 weeks;     // "months" after crowdsale end time (division floored)

        if (now > endTime + 6 * 4 weeks) {
            if(teamTranchesReleased < maxTeamTranches && currentTranche > teamTranchesReleased) {
                teamTranchesReleased++;
                transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            }
            return true;
        }
        revert();
    }

    /**
        @dev transfers Tokens from Enjin allocation to Team
        throws if before first timelock (6 months) ends and if no initiated by the owner of the contract
        @return true if successful, throws if not
    */
    function transferTeamAllocation(uint256 _amount) ownerOnly internal {
        balanceOf[enjinTeamAddress] = safeAdd(balanceOf[enjinTeamAddress], _amount);
        Transfer(0x0, enjinTeamAddress, _amount);
        totalAllocated = safeAdd(totalAllocated, _amount);
        totalAllocatedToTeam = safeAdd(totalAllocatedToTeam, _amount);
    }

    /**
        @dev release Advisors Token allocation
        throws if before timelock (2 months) ends or if no initiated by the advisors address
        or if there is no more allocation to give out
        returns true if valid

        @return true if successful, throws if not
    */
    function releaseAdvisorTokens() advisorTimelock ownerOnly returns(bool success) {
        require(totalAllocatedToAdvisors == 0);
        balanceOf[advisorAddress] = safeAdd(balanceOf[advisorAddress], advisorsAllocation);
        totalAllocated = safeAdd(totalAllocated, advisorsAllocation);
        totalAllocatedToAdvisors = advisorsAllocation;
        Transfer(0x0, advisorAddress, advisorsAllocation);
        return true;
    }

    /**
        @dev Retrieve unsold tokens from the crowdfund
        throws if before timelock (6 months from end of Crowdfund) ends and if no initiated by the owner of the contract
        returns true if valid

        @return true if successful, throws if not
    */
    function retrieveUnsoldTokens() safeTimelock ownerOnly returns(bool success) {
        uint256 amountOfTokens = balanceOf[crowdFundAddress];
        balanceOf[crowdFundAddress] = 0;
        balanceOf[incentivisationFundAddress] = safeAdd(balanceOf[incentivisationFundAddress], amountOfTokens);
        totalAllocated = safeAdd(totalAllocated, amountOfTokens);
        return true;
    }

    /**
        @dev Keep track of token allocations
        can only be called by the crowdfund contract
    */
    function addToAllocation(uint256 _amount) crowdfundOnly {
        totalAllocated = safeAdd(totalAllocated, _amount);
    }

    /**
        @dev Function to allow transfers
        can only be called by the owner of the contract
        Transfers will be allowed regardless after the crowdfund end time.
    */
    function allowTransfers() ownerOnly {
        isReleasedToPublic = true;
    }

    /**
        @dev User transfers are allowed/rejected
        Transfers are forbidden before the end of the crowdfund
    */
    function isTransferAllowed() internal constant returns(bool) {
        if (now > endTime || isReleasedToPublic == true) {
            return true;
        }
        return false;
    }
}


works!

works!

Localsuser / avatar contract deployed here
0x687f64Da83EDbe75A6e1DD513a6d5525CE168F68

contract localsAvatar {

	address public owner;

	struct User {
		string username;
		string ipfshash;
		address[] verifications;
		uint numVerifications;
	}

	mapping (address => User) public users;

	event ValidationAdded(uint test, address _from, address _to, uint _numverifications);
	event UserAdded(address _from, string _username, string _ipfshash, uint _numverifications);

	function localsAvatar(){
		owner = msg.sender;
	}

	function addLocalsuser(string _username, string _ipfshash){
		// Save the user's ipfs hashed data
		users[msg.sender].username = _username;
		users[msg.sender].ipfshash = _ipfshash;
		users[msg.sender].verifications.push(0x000);
		users[msg.sender].numVerifications = 0;
		UserAdded(msg.sender, _username, _ipfshash, users[msg.sender].numVerifications);
	}

	function addVerification(address _localsuser){
		// Add a verfier to this user's hash
		// if(users[msg.sender].numVerifications < 2){
		// 	throw ;
		// }
		uint numval = users[_localsuser].numVerifications;
		users[_localsuser].verifications.push(msg.sender);
		users[_localsuser].numVerifications = numval + 1;

		ValidationAdded(numval, msg.sender, _localsuser, users[_localsuser].numVerifications);
		// And transfer x localcoin from verifier to user.
		// Transfer(msg.sender, _localuser, 1);
	}

	// And because my mist wallet is getting full, we need a suicide function.
    function kill() { if (msg.sender == owner) suicide(owner); }

}

contract localsAvatar {

	struct User {
		string username;
		string ipfshash;
		address[] verifications;
	}

	mapping (address => User) public users;

	function localsAvatar(){

	}

	function addLocalsuser(string _username, string _ipfshash){
		// Save the user's ipfs hashed data
		users[msg.sender].username = _username;
		users[msg.sender].ipfshash = _ipfshash;
		users[msg.sender].verifications.push(0x000);
	}

	function addVerification(address _localsuser){
		// Add a verfier to this user's hash
		users[_localsuser].verifications.push(msg.sender);
		// And transfer x localcoin from verifier to user.
		// Transfer(msg.sender, _localuser, 1);
	}

    function checkVerification(address _localsuser) returns (uint) {
        return users[_localsuser].verifications.length;
    }
}


Made reference times discrete. Renamed assessmentAnswers to assessmentResponces and modified corresponding programs

contract Master
{
  //Initialize master data store
  mapping (address => uint) tokenBalance;
  mapping (address => string) tagName;
  mapping (string => address) tagAddressFromName;
  mapping (address => address[]) achievements;
  mapping (address => bool) availability;
  mapping (address => address) users;
  event TagCreation
  ( string _tagName,
    address _tagAddress,
    address[] _parents);

  function Master()
  {
    tokenBalance[address(this)] = 1;
    address[] memory a;
    uint useless = addTag("account", a);
  }

  function mapTokenBalance(address user, uint balance)
  {
    if(tagAddressFromName[tagName[msg.sender]] == 0)
    {
      tokenBalance[user] = balance;
    }
  }
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name;
  }
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress;
  }
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment);
  }
  function mapAvailability(address user, bool available)
  {
    availability[user] = available;
  }

  function getTokenBalance(address user) returns(uint)
  {
    return tokenBalance[user];
  }
  function getTagName(address tagAddress) returns(string)
  {
    return tagName[tagAddress];
  }
  function getTagAddressFromName(string name) returns(address)
  {
    return tagAddressFromName[name];
  }
  function getAchievement(address user) returns(address[])
  {
    return achievements[user];
  }
  function getNumberOfachievments(address user) returns(uint)
  {
    return achievements[user].length;
  }
  function getAvailability(address user) returns(bool)
  {
    return availability[user] = true;
  }
  function getTagDescription(string nameOfTag) returns(string tagDescription)
  {
    return Tag(tagAddressFromName[nameOfTag]).getDescription();
  }

  function addUser()
  {
    User newUser = new User(msg.sender, address(this));
    Tag(tagAddressFromName["account"]).startAssessment(address(newUser),5);
  }

  function addTag(string name, address[] parentList, string description) returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] parents;
    if(tokenBalance[msg.sender] < 1)
    {
      response += 1;
    }
    if(tagAddressFromName[name] == 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents.push(parentList[i]);
        }
      }
      Tag newTag = new Tag(name, parents, address(this), description);
      address newTagAddress = address(newTag);
      tagName[newTagAddress] = name;
      tagAddressFromName[name] = newTagAddress;
      tokenBalance[msg.sender] -= 1;
      TagCreation(name, newTagAddress, parents);
    }
    return response;
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address master;
  string name;
  string description;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores
  event CompletedAssessment
  ( address _assessee,
    bool _pass,
    uint _score,
    address _assessment);

  function Tag(string tagName, address[] parents, address masterAddress, string tagDescription)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
    description = tagDescription;
  }

  function getDescription() returns(string)
  {
    return description;
  }

  function getOwners() returns(address[])
  {
    return owners;
  }

  function getOwnerLength() returns(uint)
  {
    return owners.length;
  }

  function getOwner(uint index) returns(address)
  {
      return owners[index];
  }

  function getParents() returns(address[])
  {
    return parentTags;
  }

  function getParentsLength() returns(uint)
  {
    return parentTags.length;
  }

  function getParent(uint index) returns(address)
  {
      return parentTags[index];
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).getOwnerLength() && Assessment(assessment).getAssessmentPoolSize() != 0; i++)
    {
      if(Assessment(assessment).getAssessorPoolLength() < Tag(tagAddress).getOwnerLength()/10)
      {
        address random = Tag(tagAddress).getOwner(getRandom(Tag(tagAddress).getOwnerLength()-1));
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool(random);
          Assessment(assessment).setAssessmentPoolSize(Assessment(assessment).getAssessmentPoolSize() -1);
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).getParentsLength(); j++)
        {
          setAssessorPool(Tag(tagAddress).getParent(j), assessment);
        }
      }
      if(Assessment(assessment).getAssessmentPoolSize() <= 0)
      {
        Assessment(assessment).setPotentialAssessors(Assessment(assessment).getNumberOfAssessors());
      }
    }
  }

  function startAssessment(address assessee, uint size)
  {
    Assessment newAssessment = new Assessment(assessee, address(this), master);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20);
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool pass, uint score, address assessee, address assessment)
  {
    if(pass == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    if(pass == false && address(this) == Master(master).getTagAddressFromName("account"))
    {
      User(assessee).suicide();
    }
    assessmentHistory[assessee].push(assessment);
    CompletedAssessment(assessee, pass, score, assessment);
  }
  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  mapping (address => uint) assessors;
  address[] potentialAssessors;
  address[] finalAssessors;
  address tag;
  address master;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  struct Results
  {
    bool pass;
    uint score;
  }
  mapping(address => string) assessmentTasks; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentResponses; //Given by the assessee as IPFS hashes
  mapping(address => Results) assessmentResults; //Pass/Fail and Score given by assessors
  uint finalScore;
  bool finalResult;
  uint startReferenceTime;
  uint confirmationReferenceTime;
  uint taskSettingReferenceTime;
  uint responseSettingReferenceTime;
  uint assessmentReferenceTime;

  function Assessment(address assesseeAddress, address tagAddress, address masterAddress)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
    master = masterAddress;
    startReferenceTime = block.timestamp;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function getNumberOfAssessors() returns(uint)
  {
    return numberOfAssessors;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }

  function getAssessmentPoolSize() returns(uint)
  {
    return poolSizeRemaining;
  }

  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setPotentialAssessors(uint numberOfAssessorsNeeded)
  {
    for(uint i = 0; i < numberOfAssessorsNeeded; i++)
    {
      address randomAssessor = assessorPool[getRandom(assessorPool.length)];
      assessors[randomAssessor] = 3;
      potentialAssessors.push(randomAssessor);
      User(randomAssessor).notification("Called As A Potential Assessor",tag, 1);
    }
    confirmationReferenceTime = now;
  }

  function getAssessorPoolLength() returns(uint)
  {
      return assessorPool.length;
  }

  function getRandom(uint i) returns(uint)
  {
    return 12;
  }

  function setTask(string data)
  {
    if(assessors[msg.sender] == 1 && now - taskSettingReferenceTime <= 3600)
    {
      assessmentTasks[msg.sender] = data;
      assessors[msg.sender] = 4;
      User(msg.sender).notification("Task Data Inputted", tag, 5);
      User(assessee).notification("New Task Available", tag, 7)
    }
    if(now - taskSettingReferenceTime > 3600)
    {
      bool invalidAssessment = false;
      for(uint i = 0; i < finalAssessors.length; i++)
      {
        if(assessors[finalAssessors[i]]!=4)
        {
          User(finalAssessors[i]).notification("Did Not Submit Task On Time", tag, 6);
          Master(master).mapTokenBalance(finalAssessors[i], Master(master).getTokenBalance(finalAssessors[i]) - 1);
          invalidAssessment = true;
        }
      }
      if(invalidAssessment = true)
      {
        cancelAssessment();
      }
    }
    uint totalStatus
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      totalStatus += assessors[finalAssessors[i]];
    }
    if(totalStatus/4 == numberOfAssessors)
    {
      referenceTime = now;
    }
  }

  function cancelAssessment()
  {
    User(assessee).notification("Assessment Cancled", tag, 8);
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      User(finalAssessors[i]).notification("Assessment Cancled", tag, 8);
    }
    selfdestruct(master);
  }

  function getTask(address assessorAddress) returns(string)
  {
    return assessmentTasks[assessorAddress];
  }

  function setResponse(address assesseeAddress, string data)
  {
    assessmentResponses[assesseeAddress] = data;
  }

  function getResponse(address assesseeAddress) returns(string)
  {
    return assessmentResponses[assesseeAddress];
  }

  function setResult(bool pass, uint score)
  {
    Results memory results;
    results.pass = pass;
    results.score = score;
    assessmentResults[msg.sender] = results;
  }

  function assess()
  {

  }

  function calculateResult()
  {

  }

  function confirmAssessor(uint confirm)
  {
    if(assessors[msg.sender] != 0 && now - confirmationReferenceTime <= 180)
    {
      assessors[msg.sender] = confirm;
      if(confirm == 1)
      {
        finalAssessors.push(msg.sender);
        User(msg.sender).notification("Confirmed As Assessing", tag, 2);
      }
      if(confirm == 2)
      {
        User(msg.sender).notification("Confirmed As Not Assessing", tag, 3);
      }
    }
    if(now - confirmationReferenceTime > 180)
    {
      for(uint i = 0; i < potentialAssessors.length; i++)
      {
        if(assessors[msg.sender]==3)
        {
          User(msg.sender).notification("Did Not Respond In Time To Be Assessor", tag, 4);
          Master(master).mapTokenBalance(msg.sender, Master(master).getTokenBalance(msg.sender) - 1);
        }
      }
      if(numberOfAssessors - finalAssessors.length != 0)
      {
        uint remaining = numberOfAssessors - finalAssessors.length;
        delete potentialAssessors;
        referenceTime = now;
        setPotentialAssessors(remaining);
      }
    }
    if(numberOfAssessors - finalAssessors.length == 0)
    {
      taskSettingReferenceTime = now;
    }
  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function cashout()
  {

  }
}

contract User
{
  address user;
  address master;
  address[] acheivements;
  string userData;

  event Notification
  ( string _description,
    address _sender,
    address _user,
    address _tag,
    uint _code);

  function User(address userAddress, address masterAddress)
  {
    user = userAddress;
    master = masterAddress;
  }

  function notification(string description, address tag, uint code)
  {
    Notification(description, msg.sender, address(this), tag, code);
  }

  function setUserData(string hash)
  {
    userData = hash;
  }

  function getUserData() returns(string)
  {
    return userData;
  }

  function suicide()
  {
    selfdestruct(master);
  }
}

contract Master
{
  //Initialize master data store
  mapping (address => uint) tokenBalance;
  mapping (address => string) tagName;
  mapping (string => address) tagAddressFromName;
  mapping (address => address[]) achievements;
  mapping (address => bool) availability;
  mapping (address => address) users;
  event TagCreation
  ( string _tagName,
    address _tagAddress,
    address[] _parents);

  function Master()
  {
    tokenBalance[address(this)] = 1;
    address[] memory a;
    uint useless = addTag("account", a);
  }

  function mapTokenBalance(address user, uint balance)
  {
    if(tagAddressFromName[tagName[msg.sender]] == 0)
    {
      tokenBalance[user] = balance;
    }
  }
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name;
  }
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress;
  }
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment);
  }
  function mapAvailability(address user, bool available)
  {
    availability[user] = available;
  }

  function getTokenBalance(address user) returns(uint)
  {
    return tokenBalance[user];
  }
  function getTagName(address tagAddress) returns(string)
  {
    return tagName[tagAddress];
  }
  function getTagAddressFromName(string name) returns(address)
  {
    return tagAddressFromName[name];
  }
  function getAchievement(address user) returns(address[])
  {
    return achievements[user];
  }
  function getNumberOfachievments(address user) returns(uint)
  {
    return achievements[user].length;
  }
  function getAvailability(address user) returns(bool)
  {
    return availability[user] = true;
  }
  function getTagDescription(string nameOfTag) returns(string tagDescription)
  {
    return Tag(tagAddressFromName[nameOfTag]).getDescription();
  }

  function addUser()
  {
    User newUser = new User(msg.sender, address(this));
    Tag(tagAddressFromName["account"]).startAssessment(address(newUser),5);
  }

  function addTag(string name, address[] parentList, string description) returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] parents;
    if(tokenBalance[msg.sender] < 1)
    {
      response += 1;
    }
    if(tagAddressFromName[name] == 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents.push(parentList[i]);
        }
      }
      Tag newTag = new Tag(name, parents, address(this), description);
      address newTagAddress = address(newTag);
      tagName[newTagAddress] = name;
      tagAddressFromName[name] = newTagAddress;
      tokenBalance[msg.sender] -= 1;
      TagCreation(name, newTagAddress, parents);
    }
    return response;
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address master;
  string name;
  string description;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores
  event CompletedAssessment
  ( address _assessee,
    bool _pass,
    uint _score,
    address _assessment);

  function Tag(string tagName, address[] parents, address masterAddress, string tagDescription)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
    description = tagDescription;
  }

  function getDescription() returns(string)
  {
    return description;
  }

  function getOwners() returns(address[])
  {
    return owners;
  }

  function getOwnerLength() returns(uint)
  {
    return owners.length;
  }

  function getOwner(uint index) returns(address)
  {
      return owners[index];
  }

  function getParents() returns(address[])
  {
    return parentTags;
  }

  function getParentsLength() returns(uint)
  {
    return parentTags.length;
  }

  function getParent(uint index) returns(address)
  {
      return parentTags[index];
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).getOwnerLength() && Assessment(assessment).getAssessmentPoolSize() != 0; i++)
    {
      if(Assessment(assessment).getAssessorPoolLength() < Tag(tagAddress).getOwnerLength()/10)
      {
        address random = Tag(tagAddress).getOwner(getRandom(Tag(tagAddress).getOwnerLength()-1));
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool(random);
          Assessment(assessment).setAssessmentPoolSize(Assessment(assessment).getAssessmentPoolSize() -1);
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).getParentsLength(); j++)
        {
          setAssessorPool(Tag(tagAddress).getParent(j), assessment);
        }
      }
      if(Assessment(assessment).getAssessmentPoolSize() <= 0)
      {
        Assessment(assessment).setPotentialAssessors(Assessment(assessment).getNumberOfAssessors());
      }
    }
  }

  function startAssessment(address assessee, uint size)
  {
    Assessment newAssessment = new Assessment(assessee, address(this), master);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20);
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool pass, uint score, address assessee, address assessment)
  {
    if(pass == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    if(pass == false && address(this) == Master(master).getTagAddressFromName("account"))
    {
      User(assessee).suicide();
    }
    assessmentHistory[assessee].push(assessment);
    CompletedAssessment(assessee, pass, score, assessment);
  }
  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  mapping (address => uint) assessors;
  address[] potentialAssessors;
  address[] finalAssessors;
  address tag;
  address master;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  struct Results
  {
    bool pass;
    uint score;
  }
  mapping(address => string) assessmentTasks; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => Results) assessmentResults; //Pass/Fail and Score given by assessors
  uint finalScore;
  bool finalResult;
  uint referenceTime;

  function Assessment(address assesseeAddress, address tagAddress, address masterAddress)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
    master = masterAddress;
    referenceTime = block.timestamp;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function getNumberOfAssessors() returns(uint)
  {
    return numberOfAssessors;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }

  function getAssessmentPoolSize() returns(uint)
  {
    return poolSizeRemaining;
  }

  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setPotentialAssessors(uint numberOfAssessorsNeeded)
  {
    for(uint i = 0; i < numberOfAssessorsNeeded; i++)
    {
      address randomAssessor = assessorPool[getRandom(assessorPool.length)];
      assessors[randomAssessor] = 3;
      potentialAssessors.push(randomAssessor);
      User(randomAssessor).notification("Called As A Potential Assessor",tag, 1);
    }
  }

  function getAssessorPoolLength() returns(uint)
  {
      return assessorPool.length;
  }

  function getRandom(uint i) returns(uint)
  {
    return 12;
  }

  function setTask(string data)
  {
    if(assessors[msg.sender] == 1 && now - referenceTime <= 3600)
    {
      assessmentTasks[msg.sender] = data;
      assessors[msg.sender] = 4;
      User(msg.sender).notification("Task Data Inputted", tag, 5);
      User(assessee).notification("New Task Available", tag, 7)
    }
    if(now - referenceTime > 3600)
    {
      bool invalidAssessment = false;
      for(uint i = 0; i < finalAssessors.length; i++)
      {
        if(assessors[finalAssessors[i]]!=4)
        {
          User(finalAssessors[i]).notification("Did Not Submit Task On Time", tag, 6);
          Master(master).mapTokenBalance(finalAssessors[i], Master(master).getTokenBalance(finalAssessors[i]) - 1);
          invalidAssessment = true;
        }
      }
      if(invalidAssessment = true)
      {
        cancelAssessment();
      }
    }
    uint totalStatus
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      totalStatus += assessors[finalAssessors[i]];
    }
    if(totalStatus/4 == numberOfAssessors)
    {
      referenceTime = now;
    }
  }

  function cancelAssessment()
  {
    User(assessee).notification("Assessment Cancled", tag, 8);
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      User(finalAssessors[i]).notification("Assessment Cancled", tag, 8);
    }
    selfdestruct(master);
  }

  function getTask(address assessorAddress) returns(string)
  {
    return assessmentTasks[assessorAddress];
  }

  function setAnswer(address assesseeAddress, string data)
  {
    assessmentAnswers[assesseeAddress] = data;
  }

  function getAnswer(address assesseeAddress) returns(string)
  {
    return assessmentAnswers[assesseeAddress];
  }

  function setResult(address assessorAddress, bool pass, uint score)
  {
    Results memory results;
    results.pass = pass;
    results.score = score;
    assessmentResults[assessorAddress] = results;
  }

  function assess()
  {

  }

  function calculateResult()
  {

  }

  function confirmAssessor(uint confirm)
  {
    if(assessors[msg.sender] != 0 && now - referenceTime <= 180)
    {
      assessors[msg.sender] = confirm;
      if(confirm == 1)
      {
        finalAssessors.push(msg.sender);
        User(msg.sender).notification("Confirmed As Assessing", tag, 2);
      }
      if(confirm == 2)
      {
        User(msg.sender).notification("Confirmed As Not Assessing", tag, 3);
      }
    }
    if(now - referenceTime > 180)
    {
      for(uint i = 0; i < potentialAssessors.length; i++)
      {
        if(assessors[msg.sender]==3)
        {
          User(msg.sender).notification("Did Not Respond In Time To Be Assessor", tag, 4);
          Master(master).mapTokenBalance(msg.sender, Master(master).getTokenBalance(msg.sender) - 1);
        }
      }
      if(numberOfAssessors - finalAssessors.length != 0)
      {
        uint remaining = numberOfAssessors - finalAssessors.length;
        delete potentialAssessors;
        referenceTime = now;
        setPotentialAssessors(remaining);
      }
    }
    if(numberOfAssessors - finalAssessors.length == 0)
    {
      referenceTime = now;
    }
  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function cashout()
  {

  }
}

contract User
{
  address user;
  address master;
  address[] acheivements;
  string userData;

  event Notification
  ( string _description,
    address _sender,
    address _user,
    address _tag,
    uint _code);

  function User(address userAddress, address masterAddress)
  {
    user = userAddress;
    master = masterAddress;
  }

  function notification(string description, address tag, uint code)
  {
    Notification(description, msg.sender, address(this), tag, code);
  }

  function setUserData(string hash)
  {
    userData = hash;
  }

  function getUserData() returns(string)
  {
    return userData;
  }

  function suicide()
  {
    selfdestruct(master);
  }
}