Remediate issue 3.7 in ENS audit

Remediate issue 3.7 in ENS audit

pragma solidity >0.4.18;

/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library Buffer {
    /**
    * @dev Represents a mutable buffer. Buffers have a current value (buf) and
    *      a capacity. The capacity may be longer than the current value, in
    *      which case it can be extended without the need to allocate more memory.
    */
    struct buffer {
        bytes buf;
        uint capacity;
    }

    /**
    * @dev Initializes a buffer with an initial capacity.
    * @param buf The buffer to initialize.
    * @param capacity The number of bytes of space to allocate the buffer.
    * @return The buffer, for chaining.
    */
    function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
        if (capacity % 32 != 0) {
            capacity += 32 - (capacity % 32);
        }
        // Allocate space for the buffer data
        buf.capacity = capacity;
        assembly {
            let ptr := mload(0x40)
            mstore(buf, ptr)
            mstore(ptr, 0)
            mstore(0x40, add(ptr, capacity))
        }
        return buf;
    }

    /**
    * @dev Initializes a new buffer from an existing bytes object.
    *      Changes to the buffer may mutate the original value.
    * @param b The bytes object to initialize the buffer with.
    * @return A new buffer.
    */
    function fromBytes(bytes memory b) internal pure returns(buffer memory) {
        buffer memory buf;
        buf.buf = b;
        buf.capacity = b.length;
        return buf;
    }

    function resize(buffer memory buf, uint capacity) private pure {
        bytes memory oldbuf = buf.buf;
        init(buf, capacity);
        append(buf, oldbuf);
    }

    function max(uint a, uint b) private pure returns(uint) {
        if (a > b) {
            return a;
        }
        return b;
    }

    /**
    * @dev Sets buffer length to 0.
    * @param buf The buffer to truncate.
    * @return The original buffer, for chaining..
    */
    function truncate(buffer memory buf) internal pure returns (buffer memory) {
        assembly {
            let bufptr := mload(buf)
            mstore(bufptr, 0)
        }
        return buf;
    }

    /**
    * @dev Writes a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The start offset to write to.
    * @param data The data to append.
    * @param len The number of bytes to copy.
    * @return The original buffer, for chaining.
    */
    function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) {
        require(len <= data.length);

        if (off + len > buf.capacity) {
            resize(buf, max(buf.capacity, len + off) * 2);
        }

        uint dest;
        uint src;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Start address = buffer address + offset + sizeof(buffer length)
            dest := add(add(bufptr, 32), off)
            // Update buffer length if we're extending it
            if gt(add(len, off), buflen) {
                mstore(bufptr, add(len, off))
            }
            src := add(data, 32)
        }

        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint mask = 256 ** (32 - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }

        return buf;
    }

    /**
    * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @param len The number of bytes to copy.
    * @return The original buffer, for chaining.
    */
    function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, len);
    }

    /**
    * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, data.length);
    }

    /**
    * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write the byte at.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
        if (off > buf.capacity) {
            resize(buf, buf.capacity * 2);
        }

        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Address = buffer address + sizeof(buffer length) + off
            let dest := add(add(bufptr, off), 32)
            mstore8(dest, data)
            // Update buffer length if we extended it
            if eq(off, buflen) {
                mstore(bufptr, add(buflen, 1))
            }
        }
        return buf;
    }

    /**
    * @dev Appends a byte to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) {
        return writeUint8(buf, buf.buf.length, data);
    }

    /**
    * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
    *      exceed the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @param len The number of bytes to write (left-aligned).
    * @return The original buffer, for chaining.
    */
    function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) {
        if (len + off > buf.capacity) {
            resize(buf, max(buf.capacity, len) * 2);
        }

        uint mask = 256 ** len - 1;
        // Right-align data
        data = data >> (8 * (32 - len));
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Address = buffer address + sizeof(buffer length) + off + len
            let dest := add(add(bufptr, off), len)
            mstore(dest, or(and(mload(dest), not(mask)), data))
            // Update buffer length if we extended it
            if gt(add(off, len), mload(bufptr)) {
                mstore(bufptr, add(off, len))
            }
        }
        return buf;
    }

    /**
    * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) {
        return write(buf, off, bytes32(data), 20);
    }

    /**
    * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chhaining.
    */
    function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, bytes32(data), 20);
    }

    /**
    * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, 32);
    }

    /**
    * @dev Writes an integer to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @param len The number of bytes to write (right-aligned).
    * @return The original buffer, for chaining.
    */
    function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
        if (len + off > buf.capacity) {
            resize(buf, max(buf.capacity, len + off) * 2);
        }

        uint mask = 256 ** len - 1;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Address = buffer address + off + sizeof(buffer length) + len
            let dest := add(add(bufptr, off), len)
            mstore(dest, or(and(mload(dest), not(mask)), data))
            // Update buffer length if we extended it
            if gt(add(off, len), mload(bufptr)) {
                mstore(bufptr, add(off, len))
            }
        }
        return buf;
    }
}

pragma solidity >0.4.18;

/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library Buffer {
    /**
    * @dev Represents a mutable buffer. Buffers have a current value (buf) and
    *      a capacity. The capacity may be longer than the current value, in
    *      which case it can be extended without the need to allocate more memory.
    */
    struct buffer {
        bytes buf;
        uint capacity;
    }

    /**
    * @dev Initializes a buffer with an initial capacity.
    * @param buf The buffer to initialize.
    * @param capacity The number of bytes of space to allocate the buffer.
    * @return The buffer, for chaining.
    */
    function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
        if (capacity % 32 != 0) {
            capacity += 32 - (capacity % 32);
        }
        // Allocate space for the buffer data
        buf.capacity = capacity;
        assembly {
            let ptr := mload(0x40)
            mstore(buf, ptr)
            mstore(ptr, 0)
            mstore(0x40, add(ptr, capacity))
        }
        return buf;
    }

    /**
    * @dev Initializes a new buffer from an existing bytes object.
    *      Changes to the buffer may mutate the original value.
    * @param b The bytes object to initialize the buffer with.
    * @return A new buffer.
    */
    function fromBytes(bytes memory b) internal pure returns(buffer memory) {
        buffer memory buf;
        buf.buf = b;
        buf.capacity = b.length;
        return buf;
    }

    function resize(buffer memory buf, uint capacity) private pure {
        bytes memory oldbuf = buf.buf;
        init(buf, capacity);
        append(buf, oldbuf);
    }

    function max(uint a, uint b) private pure returns(uint) {
        if (a > b) {
            return a;
        }
        return b;
    }

    /**
    * @dev Sets buffer length to 0.
    * @param buf The buffer to truncate.
    * @return The original buffer, for chaining..
    */
    function truncate(buffer memory buf) internal pure returns (buffer memory) {
        assembly {
            let bufptr := mload(buf)
            mstore(bufptr, 0)
        }
        return buf;
    }

    /**
    * @dev Writes a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The start offset to write to.
    * @param data The data to append.
    * @param len The number of bytes to copy.
    * @return The original buffer, for chaining.
    */
    function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) {
        require(len <= data.length);

        if (off + len + buf.buf.length > buf.capacity) {
            resize(buf, max(buf.capacity, len + off) * 2);
        }

        uint dest;
        uint src;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Start address = buffer address + offset + sizeof(buffer length)
            dest := add(add(bufptr, 32), off)
            // Update buffer length if we're extending it
            if gt(add(len, off), buflen) {
                mstore(bufptr, add(len, off))
            }
            src := add(data, 32)
        }

        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint mask = 256 ** (32 - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }

        return buf;
    }

    /**
    * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @param len The number of bytes to copy.
    * @return The original buffer, for chaining.
    */
    function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, len);
    }

    /**
    * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, data.length);
    }

    /**
    * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write the byte at.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
        if (off > buf.capacity) {
            resize(buf, buf.capacity * 2);
        }

        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Address = buffer address + sizeof(buffer length) + off
            let dest := add(add(bufptr, off), 32)
            mstore8(dest, data)
            // Update buffer length if we extended it
            if eq(off, buflen) {
                mstore(bufptr, add(buflen, 1))
            }
        }
        return buf;
    }

    /**
    * @dev Appends a byte to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) {
        return writeUint8(buf, buf.buf.length, data);
    }

    /**
    * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
    *      exceed the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @param len The number of bytes to write (left-aligned).
    * @return The original buffer, for chaining.
    */
    function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) {
        if (len + off > buf.capacity) {
            resize(buf, max(buf.capacity, len) * 2);
        }

        uint mask = 256 ** len - 1;
        // Right-align data
        data = data >> (8 * (32 - len));
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Address = buffer address + sizeof(buffer length) + off + len
            let dest := add(add(bufptr, off), len)
            mstore(dest, or(and(mload(dest), not(mask)), data))
            // Update buffer length if we extended it
            if gt(add(off, len), mload(bufptr)) {
                mstore(bufptr, add(off, len))
            }
        }
        return buf;
    }

    /**
    * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
    *      capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) {
        return write(buf, off, bytes32(data), 20);
    }

    /**
    * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chhaining.
    */
    function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, bytes32(data), 20);
    }

    /**
    * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param data The data to append.
    * @return The original buffer, for chaining.
    */
    function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
        return write(buf, buf.buf.length, data, 32);
    }

    /**
    * @dev Writes an integer to the buffer. Resizes if doing so would exceed
    *      the capacity of the buffer.
    * @param buf The buffer to append to.
    * @param off The offset to write at.
    * @param data The data to append.
    * @param len The number of bytes to write (right-aligned).
    * @return The original buffer, for chaining.
    */
    function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
        if (len + off > buf.capacity) {
            resize(buf, max(buf.capacity, len + off) * 2);
        }

        uint mask = 256 ** len - 1;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Address = buffer address + off + sizeof(buffer length) + len
            let dest := add(add(bufptr, off), len)
            mstore(dest, or(and(mload(dest), not(mask)), data))
            // Update buffer length if we extended it
            if gt(add(off, len), mload(bufptr)) {
                mstore(bufptr, add(off, len))
            }
        }
        return buf;
    }
}


Remove extra state logging in test - already have transitions

import 'AVM';

contract CountdownStepFunction is AVMStepValidator {
    function validateStep(uint256[] readAccesses, uint256[] writeAccesses) external returns (bool) {
        if (readAccesses.length == 2 && readAccesses[0] == 1) {
            // Read OK
            if (writeAccesses.length == 2 && writeAccesses[0] == 1) {
                // Write address ok
                if (readAccesses[1] > 0 && writeAccesses[1] == readAccesses[1] - 1) {
                    // Correctly applied
                    return true;
                }
            } else if (readAccesses[1] == 0 && writeAccesses.length == 0) {
                // No write if we hit zero
                return true;
            }
        }
        return false;
    }

    function getStateSizes() returns (uint, uint) {
        return (2, 4);
    }
}

contract AVMTestSuite {
    AVMDisputeProcess public process;
    bytes32 zeroStore;

    // Duplicate event signatures to enable debugging in browser solidity
    // (it fails to determine which contract created an event)
    event trace(string);
    event DisputeProgress(uint disputeId, AVMDisputeProcess.DisputeState state);

    function AVMTestSuite() {
        process = new AVMDisputeProcess();

        zeroStore = sha3((uint) (0));
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
    }

    function calculateEphemeralMemory(uint step) internal returns (bytes32) {
        return sha3(
            sha3(
                sha3((bytes32) (0xdeadbeef)),
                sha3((uint) (1000000 - step))
            ), sha3(
                sha3((uint) (0)),
                sha3((uint) (0))
            )
        );
    }

    function prepareDisputeValid() returns (uint) {
        uint id = process.openDispute(
            new CountdownStepFunction(),
            this,
            this,
            sha3(calculateEphemeralMemory(0), zeroStore),
            sha3(calculateEphemeralMemory(1000000), zeroStore),
            1000000,
            300,
            15
        );

        process.doConfirmComplaint(id, process.getNextAntiReplayTag(id), false);

        bytes32[] memory states = new bytes32[](15);
        uint step = 62500;
        uint number = 62500;
        for (uint i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 5);

        number = 316406;
        step = 320312 - number;
        for (i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 15);

        number = 371334;
        step = 371578 - number;
        for (i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 0);

        number = 371105;
        step = 371120 - number;
        for (i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 0);

        number = 371091;
        step = 1;
        states = new bytes32[](14);
        for (i = 0; i < 14; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 6);

        return id;
    }

    function testDisputedWriteValid(uint id) returns (bool) {
        uint[] memory reads = new uint[](2);
        uint[] memory writes = new uint[](4);

        reads[0] = 1;
        reads[1] = 1000000 - 371096;

        writes[0] = 1;
        writes[1] = 1000000 - 371096;
        writes[2] = 1000000 - 371097;
        writes[3] = (uint) (sha3(calculateEphemeralMemory(371097), zeroStore));

        process.doProvideMemoryAccesses(id, process.getNextAntiReplayTag(id), reads, writes);

        process.doDisputeMemoryWrite(id, process.getNextAntiReplayTag(id), 0);

        bytes32[] memory proof = new bytes32[](3);
        proof[0] = sha3((bytes32) (0xdeadbeef));
        proof[1] = sha3(sha3((uint) (0)), sha3((uint) (0)));
        proof[2] = zeroStore;

        process.doProveMemoryWrite(id, process.getNextAntiReplayTag(id), proof);

        return process.isResolvedForDefendant(id);
    }

    function testDisputedWriteDifferentValueInState(uint id) returns (bool) {
        uint[] memory reads = new uint[](2);
        uint[] memory writes = new uint[](4);

        reads[0] = 1;
        reads[1] = 1000000 - 371096;

        writes[0] = 1;
        writes[1] = 1000000 - 371096;
        writes[2] = 1000000 - 371094;
        writes[3] = (uint) (sha3(calculateEphemeralMemory(371097), zeroStore));

        process.doProvideMemoryAccesses(id, process.getNextAntiReplayTag(id), reads, writes);

        bytes32 zeroStore = sha3((uint) (0), (uint) (0));
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);

        process.doDisputeMemoryWrite(id, process.getNextAntiReplayTag(id), 0);

        bytes32[] memory proof = new bytes32[](3);
        proof[0] = sha3((bytes32) (0xdeadbeef));
        proof[1] = sha3(sha3((uint) (0)), sha3((uint) (0)));
        proof[2] = zeroStore;

        process.doProveMemoryWrite(id, process.getNextAntiReplayTag(id), proof);

        return process.isResolvedForComplainant(id);
    }

    function testDisputedReadValid(uint id) returns (bool) {
        uint[] memory reads = new uint[](2);
        uint[] memory writes = new uint[](4);

        reads[0] = 1;
        reads[1] = 1000000 - 371096;

        writes[0] = 1;
        writes[1] = 1000000 - 371096;
        writes[2] = 1000000 - 371097;
        writes[3] = (uint) (sha3(calculateEphemeralMemory(371097), zeroStore));

        process.doProvideMemoryAccesses(id, process.getNextAntiReplayTag(id), reads, writes);

        process.doDisputeMemoryRead(id, process.getNextAntiReplayTag(id), 0);

        bytes32[] memory proof = new bytes32[](3);
        proof[0] = sha3((bytes32) (0xdeadbeef));
        proof[1] = sha3(sha3((uint) (0)), sha3((uint) (0)));
        proof[2] = zeroStore;

        process.doProveMemoryRead(id, process.getNextAntiReplayTag(id), proof);

        return process.isResolvedForDefendant(id);
    }
}

import 'AVM';

contract CountdownStepFunction is AVMStepValidator {
    function validateStep(uint256[] readAccesses, uint256[] writeAccesses) external returns (bool) {
        if (readAccesses.length == 2 && readAccesses[0] == 1) {
            // Read OK
            if (writeAccesses.length == 2 && writeAccesses[0] == 1) {
                // Write address ok
                if (readAccesses[1] > 0 && writeAccesses[1] == readAccesses[1] - 1) {
                    // Correctly applied
                    return true;
                }
            } else if (readAccesses[1] == 0 && writeAccesses.length == 0) {
                // No write if we hit zero
                return true;
            }
        }
        return false;
    }

    function getStateSizes() returns (uint, uint) {
        return (2, 4);
    }
}

contract AVMTestSuite {
    AVMDisputeProcess public process;
    bytes32 zeroStore;

    // Duplicate event signatures to enable debugging in browser solidity
    // (it fails to determine which contract created an event)
    event trace(string);
    event DisputeProgress(uint disputeId, AVMDisputeProcess.DisputeState state);

    function AVMTestSuite() {
        process = new AVMDisputeProcess();

        zeroStore = sha3((uint) (0));
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
    }

    function calculateEphemeralMemory(uint step) internal returns (bytes32) {
        return sha3(
            sha3(
                sha3((bytes32) (0xdeadbeef)),
                sha3((uint) (1000000 - step))
            ), sha3(
                sha3((uint) (0)),
                sha3((uint) (0))
            )
        );
    }

    event TestState(AVMDisputeProcess.DisputeState);
    function prepareDisputeValid() returns (uint) {
        uint id = process.openDispute(
            new CountdownStepFunction(),
            this,
            this,
            sha3(calculateEphemeralMemory(0), zeroStore),
            sha3(calculateEphemeralMemory(1000000), zeroStore),
            1000000,
            300,
            15
        );

        TestState(process.getDisputeState(id));

        process.doConfirmComplaint(id, process.getNextAntiReplayTag(id), false);

        TestState(process.getDisputeState(id));

        bytes32[] memory states = new bytes32[](15);
        uint step = 62500;
        uint number = 62500;
        for (uint i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        TestState(process.getDisputeState(id));

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 5);

        TestState(process.getDisputeState(id));

        number = 316406;
        step = 320312 - number;
        for (i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        TestState(process.getDisputeState(id));

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 15);

        TestState(process.getDisputeState(id));

        number = 371334;
        step = 371578 - number;
        for (i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        TestState(process.getDisputeState(id));

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 0);

        TestState(process.getDisputeState(id));

        number = 371105;
        step = 371120 - number;
        for (i = 0; i < 15; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        TestState(process.getDisputeState(id));

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 0);

        TestState(process.getDisputeState(id));

        number = 371091;
        step = 1;
        states = new bytes32[](14);
        for (i = 0; i < 14; i++) {
            states[i] = sha3(calculateEphemeralMemory(number), zeroStore);
            number += step;
        }

        process.doProvideStateRoots(id, process.getNextAntiReplayTag(id), states);

        TestState(process.getDisputeState(id));

        process.doSelectDisputedStateRoot(id, process.getNextAntiReplayTag(id), 6);

        TestState(process.getDisputeState(id));

        return id;
    }

    function testDisputedWriteValid(uint id) returns (bool) {
        uint[] memory reads = new uint[](2);
        uint[] memory writes = new uint[](4);

        reads[0] = 1;
        reads[1] = 1000000 - 371096;

        writes[0] = 1;
        writes[1] = 1000000 - 371096;
        writes[2] = 1000000 - 371097;
        writes[3] = (uint) (sha3(calculateEphemeralMemory(371097), zeroStore));

        process.doProvideMemoryAccesses(id, process.getNextAntiReplayTag(id), reads, writes);

        TestState(process.getDisputeState(id));

        process.doDisputeMemoryWrite(id, process.getNextAntiReplayTag(id), 0);

        TestState(process.getDisputeState(id));

        bytes32[] memory proof = new bytes32[](3);
        proof[0] = sha3((bytes32) (0xdeadbeef));
        proof[1] = sha3(sha3((uint) (0)), sha3((uint) (0)));
        proof[2] = zeroStore;

        process.doProveMemoryWrite(id, process.getNextAntiReplayTag(id), proof);

        TestState(process.getDisputeState(id));

        return process.isResolvedForDefendant(id);
    }

    function testDisputedWriteDifferentValueInState(uint id) returns (bool) {
        uint[] memory reads = new uint[](2);
        uint[] memory writes = new uint[](4);

        reads[0] = 1;
        reads[1] = 1000000 - 371096;

        writes[0] = 1;
        writes[1] = 1000000 - 371096;
        writes[2] = 1000000 - 371094;
        writes[3] = (uint) (sha3(calculateEphemeralMemory(371097), zeroStore));

        process.doProvideMemoryAccesses(id, process.getNextAntiReplayTag(id), reads, writes);

        TestState(process.getDisputeState(id));

        bytes32 zeroStore = sha3((uint) (0), (uint) (0));
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);
        zeroStore = sha3(zeroStore, zeroStore);

        process.doDisputeMemoryWrite(id, process.getNextAntiReplayTag(id), 0);

        TestState(process.getDisputeState(id));

        bytes32[] memory proof = new bytes32[](3);
        proof[0] = sha3((bytes32) (0xdeadbeef));
        proof[1] = sha3(sha3((uint) (0)), sha3((uint) (0)));
        proof[2] = zeroStore;

        process.doProveMemoryWrite(id, process.getNextAntiReplayTag(id), proof);

        TestState(process.getDisputeState(id));

        return process.isResolvedForComplainant(id);
    }

    function testDisputedReadValid(uint id) returns (bool) {
        uint[] memory reads = new uint[](2);
        uint[] memory writes = new uint[](4);

        reads[0] = 1;
        reads[1] = 1000000 - 371096;

        writes[0] = 1;
        writes[1] = 1000000 - 371096;
        writes[2] = 1000000 - 371097;
        writes[3] = (uint) (sha3(calculateEphemeralMemory(371097), zeroStore));

        process.doProvideMemoryAccesses(id, process.getNextAntiReplayTag(id), reads, writes);

        TestState(process.getDisputeState(id));

        process.doDisputeMemoryRead(id, process.getNextAntiReplayTag(id), 0);

        TestState(process.getDisputeState(id));

        bytes32[] memory proof = new bytes32[](3);
        proof[0] = sha3((bytes32) (0xdeadbeef));
        proof[1] = sha3(sha3((uint) (0)), sha3((uint) (0)));
        proof[2] = zeroStore;

        process.doProveMemoryRead(id, process.getNextAntiReplayTag(id), proof);

        TestState(process.getDisputeState(id));

        return process.isResolvedForDefendant(id);
    }
}


token: Update LPT to livepeer/arbitrum-lpt-bridge@078279b

//SPDX-License-Identifier: MIT
// solhint-disable-next-line
pragma solidity 0.8.8;

import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { ERC20, ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

// Copy of https://github.com/livepeer/arbitrum-lpt-bridge/blob/main/contracts/L2/token/LivepeerToken.sol
// Tests at https://github.com/livepeer/arbitrum-lpt-bridge/blob/main/test/unit/L2/livepeerToken.test.ts
contract LivepeerToken is AccessControl, ERC20Burnable, ERC20Permit {
    bytes32 private immutable MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 private immutable BURNER_ROLE = keccak256("BURNER_ROLE");

    event Mint(address indexed to, uint256 amount);
    event Burn(address indexed burner, uint256 amount);

    constructor() ERC20("Livepeer Token", "LPT") ERC20Permit("Livepeer Token") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    /**
     * @notice Function to mint tokens
     * @dev Only callable by addreses with MINTER_ROLE
     * @param _to The address that will receive the minted tokens.
     * @param _amount The amount of tokens to mint.
     */
    function mint(address _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
        _mint(_to, _amount);
        emit Mint(_to, _amount);
    }

    /**
     * @notice Burns a specific amount of msg.sender's tokens
     * @dev Only callable by addresses with BURNER_ROLE
     * @param _amount The amount of tokens to be burned
     */
    function burn(uint256 _amount) public override onlyRole(BURNER_ROLE) {
        super.burn(_amount);
        emit Burn(msg.sender, _amount);
    }

    /**
     * @notice Burns a specific amount of an address' tokens
     * @dev Only callable by addresses with BURNER_ROLE. Requires the address to approve the caller to burn the amount
     * @param _from Address to burn tokens for
     * @param _amount The amount of tokens to be burned
     */
    function burnFrom(address _from, uint256 _amount) public override onlyRole(BURNER_ROLE) {
        super.burnFrom(_from, _amount);
        emit Burn(_from, _amount);
    }
}

//SPDX-License-Identifier: MIT
// solhint-disable-next-line
pragma solidity 0.8.8;

import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { ERC20, ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

// Copy of https://github.com/livepeer/arbitrum-lpt-bridge/blob/main/contracts/L2/token/LivepeerToken.sol
// Tests at https://github.com/livepeer/arbitrum-lpt-bridge/blob/main/test/unit/L2/livepeerToken.test.ts
contract LivepeerToken is AccessControl, ERC20Burnable, ERC20Permit {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    event Mint(address indexed to, uint256 amount);
    event Burn(address indexed burner, uint256 amount);

    constructor() ERC20("Livepeer Token", "LPT") ERC20Permit("Livepeer Token") {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(BURNER_ROLE, DEFAULT_ADMIN_ROLE);
    }

    /**
     * @notice Function to mint tokens
     * @dev Only callable by addreses with MINTER_ROLE
     * @param _to The address that will receive the minted tokens.
     * @param _amount The amount of tokens to mint.
     */
    function mint(address _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
        _mint(_to, _amount);
        emit Mint(_to, _amount);
    }

    /**
     * @notice Burns a specific amount of msg.sender's tokens
     * @dev Only callable by addresses with BURNER_ROLE
     * @param _amount The amount of tokens to be burned
     */
    function burn(uint256 _amount) public override onlyRole(BURNER_ROLE) {
        super.burn(_amount);
        emit Burn(msg.sender, _amount);
    }

    /**
     * @notice Burns a specific amount of an address' tokens
     * @dev Only callable by addresses with BURNER_ROLE. Requires the address to approve the caller to burn the amount
     * @param _from Address to burn tokens for
     * @param _amount The amount of tokens to be burned
     */
    function burnFrom(address _from, uint256 _amount) public override onlyRole(BURNER_ROLE) {
        super.burnFrom(_from, _amount);
        emit Burn(_from, _amount);
    }
}


Typo fix. standart -> standard

Typo fix. standart -> standard

pragma solidity ^0.4.11;

import './BasicToken.sol';
import './ERC20.sol';

/**
 * @title Standard ERC20 token
 *
 * @dev Implemantation of the basic standard token.
 * @dev https://github.com/ethereum/EIPs/issues/20
 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is BasicToken, ERC20 {

  mapping (address => mapping (address => uint256)) allowed;

  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amout of tokens to be transfered
   */
  function transferFrom(address _from, address _to, uint256 _value) {
    var _allowance = allowed[_from][msg.sender];

    // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
    // if (_value > _allowance) throw;

    balances[_to] = balances[_to].add(_value);
    balances[_from] = balances[_from].sub(_value);
    allowed[_from][msg.sender] = _allowance.sub(_value);
    Transfer(_from, _to, _value);
  }

  /**
   * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value) {

    // To change the approve amount you first have to reduce the addresses`
    //  allowance to zero by calling `approve(_spender, 0)` if it is not
    //  already 0 to mitigate the race condition described here:
    //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;

    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifing the amount of tokens still avaible for the spender.
   */
  function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
    return allowed[_owner][_spender];
  }

}

pragma solidity ^0.4.11;

import './BasicToken.sol';
import './ERC20.sol';

/**
 * @title Standard ERC20 token
 *
 * @dev Implemantation of the basic standart token.
 * @dev https://github.com/ethereum/EIPs/issues/20
 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is BasicToken, ERC20 {

  mapping (address => mapping (address => uint256)) allowed;

  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amout of tokens to be transfered
   */
  function transferFrom(address _from, address _to, uint256 _value) {
    var _allowance = allowed[_from][msg.sender];

    // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
    // if (_value > _allowance) throw;

    balances[_to] = balances[_to].add(_value);
    balances[_from] = balances[_from].sub(_value);
    allowed[_from][msg.sender] = _allowance.sub(_value);
    Transfer(_from, _to, _value);
  }

  /**
   * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value) {

    // To change the approve amount you first have to reduce the addresses`
    //  allowance to zero by calling `approve(_spender, 0)` if it is not
    //  already 0 to mitigate the race condition described here:
    //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;

    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifing the amount of tokens still avaible for the spender.
   */
  function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
    return allowed[_owner][_spender];
  }

}


Optimized shifting in lib/Voting

Optimized shifting in lib/Voting

import 'token/Token.sol';
import './AddressList.sol';

/**
 * @dev The library for multiuser singletoken regulation.
 *      This data contains stack of variants sorted by value on its internal balance,
 *      any account(voter) can increase variant balance by self balance from given token,
 *      and any voter can decrease balance of variant but no more that given.
 *      The variant with high balance placed on top of voting pool and return by `current()`.
 */
library Voting {
    /* Voting structure */
    struct Poll {
        /* Stack of all voters */
        AddressList.Data            voters;
        /* Stack of all variants by value */
        AddressList.Data            variants;
        /* Count of shares for given variant */
        mapping(address => uint)    valueOf;
        /* Count of shares for given voter */
        mapping(address => uint)    shareOf;
        /* Poll variant for given voter */
        mapping(address => address) pollOf;
    }

    using AddressList for AddressList.Data;

    /**
     * @dev Current high value poll
     * @param _poll ref to `Poll` structure
     * @return current value
     */
    function current(Poll storage _poll) constant returns (address)
    { return _poll.variants.first(); }

    /**
     * @dev Increase poll shares for given variant
     * @param _poll ref to `Poll` structure
     * @param _variant voter variant value
     * @param _shares token represents vote
     * @param _count how much votes are given
     */
    function up(Poll storage _poll, address _voter, address _variant,
                Token _shares, uint _count) {
        // Check for already voting for any variant
        if (_poll.pollOf[_voter] != 0 && _poll.pollOf[_voter] != _variant)
            throw;

        // Try to transfer count of shares from voter to self
        if (!_shares.transferFrom(_voter, this, _count))
            throw;

        // Increase shares and set the poll
        _poll.shareOf[_voter]   += _count;
        _poll.pollOf[_voter]     = _variant;
        _poll.valueOf[_variant] += _count;

        // Append voter if not in list
        if (!_poll.voters.contains(_voter))
            _poll.voters.append(_voter);

        // Append variant if not in list
        if (!_poll.variants.contains(_variant))
            _poll.variants.append(_variant);

        // Shift variant in the stack
        shiftLeft(_poll, _variant);
    }

    /**
     * @dev Decrease poll shares of given voter
     * @param _poll ref to `Poll` structure
     * @param _count how much shares will decreased
     */
    function down(Poll storage _poll, address _voter, Token _shares, uint _count) {
        // So I can refund no more that gives from voter
        var refund = _poll.shareOf[_voter] > _count ? _count : _poll.shareOf[_voter];
        var variant = _poll.pollOf[_voter];

        // Transfer shares
        _shares.transfer(_voter, refund);
        _poll.shareOf[_voter]  -= refund;
        _poll.valueOf[variant] -= refund;

        // Clean voter poll
        if (_poll.shareOf[_voter] == 0)
            _poll.pollOf[_voter] = 0;

        // Shift right or drop when no shares
        if (_poll.valueOf[variant] > 0) {
            shiftRight(_poll, _poll.pollOf[_voter]);
        } else {
            _poll.variants.remove(variant);
        }
    }

    /*
     * Shifting mechanism
     * Thesys: the stack of variants should be sorted by valueOf value.
     * Solution:
     *  - `up` call: variant shifted left while his valueOf value is large
     *  - `down` call: varian shifted right in the stack while valueOf value is low
     */

    function shiftLeft(Poll storage _poll, address _variant) internal {
        var value = _poll.valueOf[_variant];
        var left  = _poll.variants.prevOf[_variant];

        /* XXX: possible DoS by block gas limit
                when a lot of same value variants */
        while (left != 0 && _poll.valueOf[left] < value)
            left = _poll.variants.prevOf[_variant];

        _poll.variants.remove(_variant);
        _poll.variants.append(_variant, left);
    }

    function shiftRight(Poll storage _poll, address _variant) internal {
        var value = _poll.valueOf[_variant];
        var right = _poll.variants.nextOf[_variant];

        /* XXX: possible DoS by block gas limit
                when a lot of same value variants */
        while (right != 0 && _poll.valueOf[right] > value)
            right = _poll.variants.nextOf[_variant];

        _poll.variants.remove(_variant);
        _poll.variants.prepend(_variant, right);
    }
}

import 'token/Token.sol';
import './AddressList.sol';

/**
 * @dev The library for multiuser singletoken regulation.
 *      This data contains stack of variants sorted by value on its internal balance,
 *      any account(voter) can increase variant balance by self balance from given token,
 *      and any voter can decrease balance of variant but no more that given.
 *      The variant with high balance placed on top of voting pool and return by `current()`.
 */
library Voting {
    /* Voting structure */
    struct Poll {
        /* Stack of all voters */
        AddressList.Data            voters;
        /* Stack of all variants by value */
        AddressList.Data            variants;
        /* Count of shares for given variant */
        mapping(address => uint)    valueOf;
        /* Count of shares for given voter */
        mapping(address => uint)    shareOf;
        /* Poll variant for given voter */
        mapping(address => address) pollOf;
    }

    using AddressList for AddressList.Data;

    /**
     * @dev Current high value poll
     * @param _poll ref to `Poll` structure
     * @return current value
     */
    function current(Poll storage _poll) constant returns (address)
    { return _poll.variants.first(); }

    /**
     * @dev Increase poll shares for given variant
     * @param _poll ref to `Poll` structure
     * @param _variant voter variant value
     * @param _shares token represents vote
     * @param _count how much votes are given
     */
    function up(Poll storage _poll, address _voter, address _variant,
                Token _shares, uint _count) {
        // Check for already voting for any variant
        if (_poll.pollOf[_voter] != 0 && _poll.pollOf[_voter] != _variant)
            throw;

        // Try to transfer count of shares from voter to self
        if (!_shares.transferFrom(_voter, this, _count))
            throw;

        // Increase shares and set the poll
        _poll.shareOf[_voter]   += _count;
        _poll.pollOf[_voter]     = _variant;
        _poll.valueOf[_variant] += _count;

        // Append voter if not in list
        if (!_poll.voters.contains(_voter))
            _poll.voters.append(_voter);

        // Append variant if not in list
        if (!_poll.variants.contains(_variant))
            _poll.variants.append(_variant);

        // Shift variant in the stack
        shiftLeft(_poll, _variant);
    }

    /**
     * @dev Decrease poll shares of given voter
     * @param _poll ref to `Poll` structure
     * @param _count how much shares will decreased
     */
    function down(Poll storage _poll, address _voter, Token _shares, uint _count) {
        // So I can refund no more that gives from voter
        var refund = _poll.shareOf[_voter] > _count ? _count : _poll.shareOf[_voter];
        var variant = _poll.pollOf[_voter];

        // Transfer shares
        _shares.transfer(_voter, refund);
        _poll.shareOf[_voter]  -= refund;
        _poll.valueOf[variant] -= refund;

        // Clean voter poll
        if (_poll.shareOf[_voter] == 0)
            _poll.pollOf[_voter] = 0;

        // Shift right or drop when no shares
        if (_poll.valueOf[variant] > 0) {
            shiftRight(_poll, _poll.pollOf[_voter]);
        } else {
            _poll.variants.remove(variant);
        }
    }

    /*
     * Shifting mechanism
     * Thesys: the stack of variants should be sorted by valueOf value.
     * Solution:
     *  - `up` call: variant shifted left while his valueOf value is large
     *  - `down` call: varian shifted right in the stack while valueOf value is low
     */

    function shiftLeft(Poll storage _poll, address _variant) internal {
        var value = _poll.valueOf[_variant];
        var left  = _poll.variants.prev(_variant);

        /* XXX: possible DoS by block gas limit
                when a lot of same value variants */
        while (left != 0 && _poll.valueOf[left] < value) {
            _poll.variants.swap(left, _variant);
            left = _poll.variants.prev(_variant);
        }
    }

    function shiftRight(Poll storage _poll, address _variant) internal {
        var value = _poll.valueOf[_variant];
        var right = _poll.variants.next(_variant);

        /* XXX: possible DoS by block gas limit
                when a lot of same value variants */
        while (right != 0 && _poll.valueOf[right] > value) {
            _poll.variants.swap(right, _variant);
            right = _poll.variants.next(_variant);
        }
    }
}


Update SafeERC20.sol (#1693)

Update SafeERC20.sol (#1693)

pragma solidity ^0.5.2;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require((value == 0) || (token.allowance(address(this), spender) == 0));
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.

        require(address(token).isContract());

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success);

        if (returndata.length > 0) { // Return data is optional
            require(abi.decode(returndata, (bool)));
        }
    }
}

pragma solidity ^0.5.2;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require((value == 0) || (token.allowance(address(this), spender) == 0));
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must equal true).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.

        require(address(token).isContract());

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success);

        if (returndata.length > 0) { // Return data is optional
            require(abi.decode(returndata, (bool)));
        }
    }
}


some structure

some structure

pragma solidity ^0.4.11;
contract Bet is usingOraclize {
  enum BET_STATES {
    OPEN,
    TEAM_ONE_WON,
    TEAM_TWO_WON,
    DRAW,
    ORACLE_UNDECIDED
    }

  BET_STATES bet_state;
  bool public is_featured;
  string public title;
  string public description;
  string public category;
  string public team_0; // Team 0 identifier
  string public team_1; // Team 1 identifier
  uint public team_0_bet_sum;
  uint public team_1_bet_sum;

  uint public block_match_begin;
  uint public block_match_end;
  uint public block_hard_deadline; // Hard deadline to end bet

  uint8 oracle_retries; // How many times the oracle tried to set the score on the bet
  uint constant oracle_retry_interval = 100; // Interval for retries (in blocks)

  string url_oraclize;

  uint8 winner_idx;

  event new_betting(uint8 for_team_idx, address from, amount);
  event new_winner_declared(BET_STATES winner);

  function __callback(bytes32 myid, string result) {
    // Cannot call after hard deadline
    assert(block.number < block_hard_deadline);
    // Oraclize should call this
    assert(msg.sender == oraclize_cbAddress());
    // Must be called after the bet ends
    assert(block.number >= block_match_end);
    // Can call only when bet is open or undecided
    assert(bet_state == BET_STATES.OPEN || bet_state == BET_STATES.ORACLE_UNDECIDED);

    oracle_retries += 1;
    // Oracle is retrying
    if (bet_state == BET_STATES.ORACLE_UNDECIDED) {
      assert(block.number >= (block_match_end + (oracle_retry_interval * oracle_retries)));
    }
    if (result == team_0)
      bet_state = BET_STATES.TEAM_ONE_WON;
    else if (result == team_1)
      bet_state = BET_STATES.TEAM_TWO_WON;
    else
      bet_state = BET_STATES.ORACLE_UNDECIDED;
    new_winner_declared(bet_state);
  }

  function update_result() payable {
    oraclize_query('URL', url_oraclize);
  }

  function toggle_featured() {
    if (msg.sender != ebets_address()) throw;
    is_featured = !is_featured;
  }

  //
  function bet(uint8 for_team_idx) {
    new_betting(for_team_idx, msg.sender, msg.value);
  }

  // Called by the user to collect his reward
  function collect_profit() {

  }

  // If the oracle fails or is not able to get the right answer
  function resolve_conflict(uint8 for_team_idx) {
  }
}

contract Bet is usingOraclize {
  bool public is_featured;
  string public title;
  string public description;
  string public category;
  string url_oraclize;

  uint8 winner_idx;

  event new_betting(uint8 for_team_idx, address from, amount);

  function __callback(bytes32 myid, string result) {
    if (msg.sender != oraclize_cbAddress()) throw;
      viewsCount = result;
      newYoutubeViewsCount(viewsCount);
  }

  function update_result() payable {
    oraclize_query('URL', url_oraclize);
  }

  function toggle_featured() {
    if (msg.sender != ebets_address()) throw;
    is_featured = !is_featured;
  }

  function bet(uint8 for_team_idx) {
    new_betting(for_team_idx, msg.sender, msg.value);
  }

  function collect_profit() {

  }

  //If the oracle fails or is not able to get the right answer
  function resolve_conflict(uint8 for_team_idx) {
  }
}


Tidy-ups

Tidy-ups

pragma solidity ^0.4.2;

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import "./vendor/wealdtech-solidity/contracts/ens/ENSReverseRegister.sol";

// Interesting parts of the ENS deed
contract Deed {
    address public owner;
    address public previousOwner;
}

// Interesting parts of the ENS registry
contract Registry {
    function owner(bytes32 _hash) constant returns (address);
}

// Interesting parts of the ENS registrar
contract Registrar {
    function transfer(bytes32 _hash, address newOwner);
    function entries(bytes32 _hash) constant returns (uint, Deed, uint, uint, uint);
}

contract DomainSale is ENSReverseRegister {
    Registrar public registrar;
    mapping (string => Sale) private sales;
    mapping (address => uint256) private balances;

    // Auction parameters
    uint256 private constant AUCTION_DURATION = 24 hours;
    uint256 private constant HIGH_BID_KICKIN = 7 days;
    uint256 private constant NORMAL_BID_INCREASE_PERCENTAGE = 10;
    uint256 private constant HIGH_BID_INCREASE_PERCENTAGE = 50;

    // Distribution of the sale funds
    uint256 private constant SELLER_SALE_PERCENTAGE = 90;
    uint256 private constant START_REFERRER_SALE_PERCENTAGE = 5;
    uint256 private constant BID_REFERRER_SALE_PERCENTAGE = 5;

    // ENS
    string private constant CONTRACT_ENS = "domainsale.eth";
    // Hex is namehash("eth")
    bytes32 private constant NAMEHASH_ETH = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;

    // Funds tracker.  Ensures we don't forget about any Ether
    // Funds held for sales (both buys and bids)
    uint256 private saleFunds = 0;
    // Funds pending withdrawal
    uint256 private withdrawalFunds = 0;
    // Total funds (cannot use balance as it can be stuffed)
    uint256 private totalFunds = 0;

    struct Sale {
        // The lowest direct purchase price that will be accepted
        uint256 price;
        // The lowest auction bid that will be accepted
        uint256 reserve;
        // The last bid on the auction.  0 if no bid has been made
        uint256 lastBid;
        // The address of the last bider on the auction.  0 if no bid has been made
        address lastBidder;
        // The timestamp when this auction started
        uint256 auctionStarted;
        // The timestamp at which this auction ends
        uint256 auctionEnds;
        // The address of the referrer who started the sale
        address startReferrer;
        // The address of the referrer who supplied the winning bid
        address bidReferrer;
    }

    //
    // Events
    //

    // Sent when a name is offered (can occur multiple times if the seller
    // changes their prices)
    event Offer(address indexed seller, string name, uint256 price, uint256 reserve);
    // Sent when a bid is placed for a name
    event Bid(address indexed bidder, string name, uint256 bid);
    // Sent when a name is transferred to a new owner
    event Transfer(address indexed seller, address indexed buyer, string name, uint256 value);
    // Sent when a sale for a name is cancelled
    event Cancel(string name);

    //
    // Modifiers
    //

    // Ensure that we don't lose any funds by keeping track of them as they
    // move from sales funds to withdrawal funds and confirm that they equal
    // the total funds
    modifier fundsTracker() {
        _;
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    // Actions that can only be undertaken by the seller of the name.
    // The owner of the name is this contract, so we use the previous
    // owner from the deed
    modifier onlyNameSeller(string _name) {
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed.owner() == address(this));
        require(deed.previousOwner() == msg.sender);
        _;
    }

    // It is possible for a name to be invalidated, in which case the
    // owner will be reset
    modifier deedValid(string _name) {
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed != 0);
        _;
    }

    // Actions that can only be undertaken if the name sale has attracted
    // no bids.
    modifier auctionNotStarted(string _name) {
        require(sales[_name].auctionStarted == 0);
        _;
    }

    // Allow if the name can be bid upon
    modifier canBid(string _name) {
        require(sales[_name].reserve != 0);
        _;
    }

    // Allow if the name can be purchased
    modifier canBuy(string _name) {
        require(sales[_name].price != 0);
        _;
    }

    // Payables add their value to the sales funds tracker
    modifier addSaleFunds(uint256 value) {
        saleFunds += value;
        totalFunds += value;
        _;
    }

    /**
     * @dev Constructor takes the address of the ENS registry
     */
    function DomainSale(address _registry) ENSReverseRegister(_registry, CONTRACT_ENS) {
        registrar = Registrar(Registry(_registry).owner(NAMEHASH_ETH));
    }

    //
    // Accessors for sales struct
    //

    /**
     * @dev return useful information from the sale structure in one go
     */
    function sale(string _name) public constant returns (uint256, uint256, uint256, address, uint256, uint256) {
        Sale storage s = sales[_name];
        return (s.price, s.reserve, s.lastBid, s.lastBidder, s.auctionStarted, s.auctionEnds);
    }

    /**
     * @dev a flag set if this name can be purchased through auction
     */
    function isAuction(string _name) public constant returns (bool) {
        return sales[_name].reserve != 0;
    }

    /**
     * @dev a flag set if this name can be purchased outright
     */
    function isBuyable(string _name) public constant returns (bool) {
        return sales[_name].price != 0;
    }

    /**
     * @dev a flag set if the auction has started
     */
    function auctionStarted(string _name) public constant returns (bool) {
        return sales[_name].lastBid != 0;
    }

    /**
     * @dev the time at which the auction ends
     */
    function auctionEnds(string _name) public constant returns (uint256) {
        return sales[_name].auctionEnds;
    }

    /**
     * @dev minimumBid is the greater of the minimum bid or the last bid + 10%.
     *      If an auction has been going longer than 7 days then it is the last
     *      bid + 50%.
     */
    function minimumBid(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        if (s.auctionStarted == 0) {
            return s.reserve;
        } else if (s.auctionStarted + HIGH_BID_KICKIN > now) {
            return s.lastBid + s.lastBid * NORMAL_BID_INCREASE_PERCENTAGE / 100;
        } else {
            return s.lastBid + s.lastBid * HIGH_BID_INCREASE_PERCENTAGE / 100;
        }
    }

    /**
     * @dev price is the instant purchase price.
     */
    function price(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        return s.price;
    }

    /**
     * @dev The balance available for withdrawal
     */
    function balance() public constant returns (uint256) {
        return balances[msg.sender];
    }

    //
    // Operations
    //

    /**
     * @dev offer a domain for sale.
     *      The price is the price at which a domain can be purchased directly.
     *      The reserve is the initial lowest price for which a bid can be made.
     */
    function offer(string _name, uint256 _price, uint256 reserve, address referrer) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) fundsTracker public {
        require(_price == 0 || _price > reserve);
        require(_price != 0 || reserve != 0);
        Sale storage s = sales[_name];
        s.reserve = reserve;
        s.price = _price;
        s.startReferrer = referrer;
        Offer(msg.sender, _name, _price, reserve);
    }

    /**
     * @dev cancel a sale for a domain.
     *      This can only happen if there have been no bids for the name.
     */
    function cancel(string _name) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) fundsTracker public {
        // Finished with the sale information
        delete sales[_name];

        registrar.transfer(sha3(_name), msg.sender);
        Cancel(_name);
    }

    /**
     * @dev buy a domain directly
     */
    function buy(string _name, address bidReferrer) canBuy(_name) deedValid(_name) addSaleFunds(msg.value) fundsTracker public payable {
        Sale storage s = sales[_name];
        require(msg.value >= s.price);
        require(s.auctionStarted == 0);

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        address previousOwner = deed.previousOwner();

        // Transfer the name
        registrar.transfer(sha3(_name), msg.sender);
        Transfer(previousOwner, msg.sender, _name, msg.value);

        // Distribute funds to referrers
        transferFunds(msg.value, previousOwner, s.startReferrer, bidReferrer);

        // Finished with the sale information
        delete sales[_name];

        // As we're here, return any funds that the sender is owed
        withdraw();
    }

    /**
     * @dev bid for a domain
     */
    function bid(string _name, address bidReferrer) canBid(_name) deedValid(_name) addSaleFunds(msg.value) fundsTracker public payable {
        require(msg.value >= minimumBid(_name));

        Sale storage s = sales[_name];
        require(s.auctionStarted == 0 || now < s.auctionEnds);

        if (s.auctionStarted == 0) {
          // First bid; set the auction start
          s.auctionStarted = now;
        } else {
          // Update the balance for the outbid bidder
          balances[s.lastBidder] += s.lastBid;
          saleFunds -= s.lastBid;
          withdrawalFunds += s.lastBid;
        }
        s.lastBidder = msg.sender;
        s.lastBid = msg.value;
        s.auctionEnds = now + AUCTION_DURATION;
        s.bidReferrer = bidReferrer;
        Bid(msg.sender, _name, msg.value);

        // As we're here, return any funds that the sender is owed
        withdraw();
    }

    /**
     * @dev finish an auction
     */
    function finish(string _name) deedValid(_name) fundsTracker public {
        Sale storage s = sales[_name];
        require(now > s.auctionEnds);

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));

        address previousOwner = deed.previousOwner();
        registrar.transfer(sha3(_name), s.lastBidder);
        Transfer(previousOwner, s.lastBidder, _name, s.lastBid);

        // Distribute funds to referrers
        transferFunds(s.lastBid, previousOwner, s.startReferrer, s.bidReferrer);

        // Finished with the sale information
        delete sales[_name];

        // As we're here, return any funds that the sender is owed
        withdraw();
    }

    /**
     * @dev withdraw any owned balance
     */
    function withdraw() fundsTracker public {
        uint256 amount = balances[msg.sender];
        if (amount > 0) {
            balances[msg.sender] = 0;
            withdrawalFunds -= amount;
            totalFunds -= amount;
            msg.sender.transfer(amount);
        }
    }

    /**
     * @dev Invalidate an auction if the deed is no longer active
     */
    function invalidate(string _name) fundsTracker public {
        // Ensure the deed has been invalidated
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed == 0);

        Sale storage s = sales[_name];

        // Update the balance for the winning bidder
        balances[s.lastBidder] += s.lastBid;
        saleFunds -= s.lastBid;
        withdrawalFunds += s.lastBid;

        // Finished with the sale information
        delete sales[_name];

        // Cancel the auction
        Cancel(_name);

        // As we're here, return any funds that the sender is owed
        withdraw();
    }

    //
    // Internal functions
    //

    /**
     * @dev Transfer funds for a sale to the relevant parties
     */
    function transferFunds(uint256 amount, address seller, address startReferrer, address bidReferrer) internal {
        uint256 startReferrerFunds = amount * START_REFERRER_SALE_PERCENTAGE / 100;
        uint256 bidReferrerFunds = amount * BID_REFERRER_SALE_PERCENTAGE / 100;
        uint256 sellerFunds = amount - startReferrerFunds - bidReferrerFunds;
        saleFunds -= amount;
        totalFunds -= amount;
        seller.transfer(sellerFunds);
        startReferrer.transfer(startReferrerFunds);
        bidReferrer.transfer(bidReferrerFunds);
    }
}

pragma solidity ^0.4.2;

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import "./vendor/wealdtech-solidity/contracts/ens/ENSReverseRegister.sol";

// Interesting parts of the ENS deed
contract Deed {
    address public owner;
    address public previousOwner;
}

// Interesting parts of the ENS registry
contract Registry {
    function owner(bytes32 _hash) constant returns (address);
}

// Interesting parts of the ENS registrar
contract Registrar {
    function transfer(bytes32 _hash, address newOwner);
    function entries(bytes32 _hash) constant returns (uint, Deed, uint, uint, uint);
}

contract DomainSale is ENSReverseRegister {
    Registrar public registrar;
    mapping (string => Sale) private sales;
    mapping (address => uint256) private balances;

    // Auction parameters
    uint256 private constant AUCTION_DURATION = 24 hours;
    uint256 private constant HIGH_BID_KICKIN = 7 days;
    uint256 private constant NORMAL_BID_INCREASE_PERCENTAGE = 10;
    uint256 private constant HIGH_BID_INCREASE_PERCENTAGE = 50;

    // Distribution of the sale funds
    uint256 private constant SELLER_SALE_PERCENTAGE = 90;
    uint256 private constant START_REFERRER_SALE_PERCENTAGE = 5;
    uint256 private constant BID_REFERRER_SALE_PERCENTAGE = 5;

    // ENS
    string private constant CONTRACT_ENS = "domainsale.eth";
    // Hex is namehash("eth")
    bytes32 private constant NAMEHASH_ETH = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;

    // Funds tracker.  Ensures we don't forget about any Ether
    // Funds held for sales (both buys and bids)
    uint256 private saleFunds = 0;
    // Funds pending withdrawal
    uint256 private withdrawalFunds = 0;
    // Total funds (cannot use balance as it can be stuffed)
    uint256 private totalFunds = 0;

    struct Sale {
        // The lowest direct purchase price that will be accepted
        uint256 price;
        // The lowest auction bid that will be accepted
        uint256 reserve;
        // The last bid on the auction.  0 if no bid has been made
        uint256 lastBid;
        // The address of the last bider on the auction.  0 if no bid has been made
        address lastBidder;
        // The timestamp when this auction started
        uint256 auctionStarted;
        // The timestamp at which this auction ends
        uint256 auctionEnds;
        // The address of the referrer who started the sale
        address startReferrer;
        // The address of the referrer who supplied the winning bid
        address bidReferrer;
    }

    //
    // Events
    //

    // Sent when a name is offered (can occur multiple times if the seller
    // changes their prices)
    event Offer(address indexed seller, string name, uint256 price, uint256 reserve);
    // Sent when a bid is placed for a name
    event Bid(address indexed bidder, string name, uint256 bid);
    // Sent when a name is transferred to a new owner
    event Transfer(address indexed seller, address indexed buyer, string name, uint256 value);
    // Sent when a sale for a name is cancelled
    event Cancel(string name);

    //
    // Modifiers
    //

    // Actions that can only be undertaken by the seller of the name.
    // The owner of the name is this contract, so we use the previous
    // owner from the deed
    modifier onlyNameSeller(string _name) {
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed.owner() == address(this));
        require(deed.previousOwner() == msg.sender);
        _;
    }

    // It is possible for a name to be invalidated, in which case the
    // owner will be reset
    modifier deedValid(string _name) {
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed != 0);
        _;
    }

    // Actions that can only be undertaken if the name sale has attracted
    // no bids.
    modifier auctionNotStarted(string _name) {
        require(sales[_name].auctionStarted == 0);
        _;
    }

    // Allow if the name can be bid upon
    modifier canBid(string _name) {
        require(sales[_name].reserve != 0);
        _;
    }

    // Allow if the name can be purchased
    modifier canBuy(string _name) {
        require(sales[_name].price != 0);
        _;
    }

    // Payables add their value to the sales funds tracker
    modifier addSaleFunds(uint256 value) {
        saleFunds += value;
        totalFunds += value;
        _;
    }

    /**
     * @dev Constructor takes the address of the ENS registry
     */
    function DomainSale(address _registry) ENSReverseRegister(_registry, CONTRACT_ENS) {
        registrar = Registrar(Registry(_registry).owner(NAMEHASH_ETH));
    }

    //
    // Accessors for sales struct
    //

    /**
     * @dev return useful information from the sale structure in one go
     */
    function sale(string _name) public constant returns (uint256, uint256, uint256, address, uint256, uint256) {
        Sale storage s = sales[_name];
        return (s.price, s.reserve, s.lastBid, s.lastBidder, s.auctionStarted, s.auctionEnds);
    }

    /**
     * @dev a flag set if this name can be purchased through auction
     */
    function isAuction(string _name) public constant returns (bool) {
        return sales[_name].reserve != 0;
    }

    /**
     * @dev a flag set if this name can be purchased outright
     */
    function isBuyable(string _name) public constant returns (bool) {
        return sales[_name].price != 0;
    }

    /**
     * @dev a flag set if the auction has started
     */
    function auctionStarted(string _name) public constant returns (bool) {
        return sales[_name].lastBid != 0;
    }

    /**
     * @dev the time at which the auction ends
     */
    function auctionEnds(string _name) public constant returns (uint256) {
        return sales[_name].auctionEnds;
    }

    /**
     * @dev minimumBid is the greater of the minimum bid or the last bid + 10%.
     *      If an auction has been going longer than 7 days then it is the last
     *      bid + 50%.
     */
    function minimumBid(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        if (s.auctionStarted == 0) {
            return s.reserve;
        } else if (s.auctionStarted + HIGH_BID_KICKIN > now) {
            return s.lastBid + s.lastBid * NORMAL_BID_INCREASE_PERCENTAGE / 100;
        } else {
            return s.lastBid + s.lastBid * HIGH_BID_INCREASE_PERCENTAGE / 100;
        }
    }

    /**
     * @dev price is the instant purchase price.
     */
    function price(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        return s.price;
    }

    /**
     * @dev The balance available for withdrawal
     */
    function balance() public constant returns (uint256) {
        return balances[msg.sender];
    }

    //
    // Operations
    //

    /**
     * @dev offer a domain for sale.
     *      The price is the price at which a domain can be purchased directly.
     *      The reserve is the initial lowest price for which a bid can be made.
     */
    function offer(string _name, uint256 _price, uint256 reserve, address referrer) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) public {
        require(_price == 0 || _price > reserve);
        require(_price != 0 || reserve != 0);
        Sale storage s = sales[_name];
        s.reserve = reserve;
        s.price = _price;
        s.startReferrer = referrer;
        Offer(msg.sender, _name, _price, reserve);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev cancel a sale for a domain.
     *      This can only happen if there have been no bids for the name.
     */
    function cancel(string _name) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) public {
        registrar.transfer(sha3(_name), msg.sender);
        Cancel(_name);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev buy a domain directly
     */
    function buy(string _name, address bidReferrer) canBuy(_name) deedValid(_name) addSaleFunds(msg.value) public payable {
        Sale storage s = sales[_name];
        require(msg.value >= s.price);
        require(s.auctionStarted == 0);

        // As we're here, return any funds that the sender is owed
        withdraw();

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        address previousOwner = deed.previousOwner();

        // Transfer the name
        registrar.transfer(sha3(_name), msg.sender);
        Transfer(previousOwner, msg.sender, _name, msg.value);

        // Distribute funds to referrers
        transferFunds(msg.value, previousOwner, s.startReferrer, bidReferrer);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev bid for a domain
     */
    function bid(string _name, address bidReferrer) canBid(_name) deedValid(_name) addSaleFunds(msg.value) public payable {
        require(msg.value >= minimumBid(_name));

        Sale storage s = sales[_name];
        require(s.auctionStarted == 0 || now < s.auctionEnds);

        // Update the balance for the outbid bidder
        balances[s.lastBidder] += s.lastBid;
        saleFunds -= s.lastBid;
        withdrawalFunds += s.lastBid;

        // As we're here, return any funds that the sender is owed
        withdraw();

        if (s.auctionStarted == 0) {
          // First bid; set the auction start
          s.auctionStarted = now;
        }
        s.lastBidder = msg.sender;
        s.lastBid = msg.value;
        s.auctionEnds = now + AUCTION_DURATION;
        s.bidReferrer = bidReferrer;
        Bid(msg.sender, _name, msg.value);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev finish an auction
     */
    function finish(string _name) deedValid(_name) public {
        Sale storage s = sales[_name];
        require(now > s.auctionEnds);

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));

        address previousOwner = deed.previousOwner();
        registrar.transfer(sha3(_name), s.lastBidder);
        Transfer(previousOwner, s.lastBidder, _name, s.lastBid);

        // Distribute funds to referrers
        transferFunds(s.lastBid, previousOwner, s.startReferrer, s.bidReferrer);

        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev withdraw any owned balance
     */
    function withdraw() public {
        uint256 amount = balances[msg.sender];
        if (amount > 0) {
            balances[msg.sender] = 0;
            withdrawalFunds -= amount;
            totalFunds -= amount;
            msg.sender.transfer(amount);
        }
    }

    /**
     * @dev Invalidate an auction if the deed is no longer active
     */
    function invalidate(string _name) public {
        // Ensure the deed has been invalidated
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed == 0);

        Sale storage s = sales[_name];

        // Update the balance for the winning bidder
        balances[s.lastBidder] += s.lastBid;

        // As we're here, return any funds that the sender is owed
        withdraw();

        // Cancel the auction
        Cancel(_name);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    //
    // Internal functions
    //

    /**
     * @dev Transfer funds for a sale to the relevant parties
     */
    function transferFunds(uint256 amount, address seller, address startReferrer, address bidReferrer) internal {
        uint256 startReferrerFunds = amount * START_REFERRER_SALE_PERCENTAGE / 100;
        uint256 bidReferrerFunds = amount * BID_REFERRER_SALE_PERCENTAGE / 100;
        uint256 sellerFunds = amount - startReferrerFunds - bidReferrerFunds;
        saleFunds -= amount;
        totalFunds -= amount;
        seller.transfer(sellerFunds);
        startReferrer.transfer(startReferrerFunds);
        bidReferrer.transfer(bidReferrerFunds);
    }

    /**
     * @dev Clear the storage for a name
     */
    function clearStorage(string _name) internal {
        // Clear name records
        sales[_name] = Sale({
            reserve: 0,
            price: 0,
            lastBid:0,
            lastBidder:0,
            auctionStarted:0,
            auctionEnds:0,
            startReferrer: 0,
            bidReferrer: 0
        });
    }
}