assert() => assertTrue()

assert() => assertTrue()

pragma solidity ^0.4.18;

import "ds-test/test.sol";

import "./item_store_registry.sol";
import "./item_store_ipfs_sha256.sol";
import "./item_store_ipfs_sha256_proxy.sol";

/**
 * @title ItemStoreIpfsSha256Test
 * @author Jonathan Brown <jbrown@mix-blockchain.org>
 * @dev Testing contract for ItemStoreIpfsSha256.
 */
contract ItemStoreIpfsSha256Test is DSTest {

    byte constant UPDATABLE = 0x01;           // True if the item is updatable. After creation can only be disabled.
    byte constant ENFORCE_REVISIONS = 0x02;   // True if the item is enforcing revisions. After creation can only be enabled.
    byte constant RETRACTABLE = 0x04;         // True if the item can be retracted. After creation can only be disabled.
    byte constant TRANSFERABLE = 0x08;        // True if the item be transfered to another user or disowned. After creation can only be disabled.
    byte constant ANONYMOUS = 0x10;           // True if the item should not have an owner.

    ItemStoreRegistry itemStoreRegistry;
    ItemStoreIpfsSha256 itemStore;
    ItemStoreIpfsSha256Proxy itemStoreProxy;

    function setUp() public {
        itemStoreRegistry = new ItemStoreRegistry();
        itemStore = new ItemStoreIpfsSha256(itemStoreRegistry);
        itemStoreProxy = new ItemStoreIpfsSha256Proxy(itemStore);
    }

    function testControlCreateSameNonce() public {
        itemStore.create(0, 0x1234, 0);
        itemStore.create(0, 0x1234, 1);
    }

    function testFailCreateSameNonce() public {
        itemStore.create(0, 0x1234, 0);
        itemStore.create(0, 0x1234, 0);
    }

    function testCreate() public {
        bytes20 itemId0 = itemStore.create(0, 0x1234, 0);
        assertTrue(itemStore.getInUse(itemId0));
        assertEq(itemStore.getFlags(itemId0), 0);
        assertEq(itemStore.getOwner(itemId0), this);
        assertEq(itemStore.getRevisionCount(itemId0), 1);
        assertTrue(!itemStore.getUpdatable(itemId0));
        assertTrue(!itemStore.getEnforceRevisions(itemId0));
        assertTrue(!itemStore.getRetractable(itemId0));
        assertTrue(!itemStore.getTransferable(itemId0));
        assertEq(itemStore.getRevisionIpfsHash(itemId0, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId0, 0), block.timestamp);

        bytes20 itemId1 = itemStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x1234, 1);
        assertTrue(itemStore.getInUse(itemId1));
        assertEq(itemStore.getFlags(itemId1), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(itemStore.getOwner(itemId1), 0);
        assertEq(itemStore.getRevisionCount(itemId1), 1);
        assertTrue(itemStore.getUpdatable(itemId1));
        assertTrue(itemStore.getEnforceRevisions(itemId1));
        assertTrue(itemStore.getRetractable(itemId1));
        assertTrue(itemStore.getTransferable(itemId1));
        assertEq(itemStore.getRevisionIpfsHash(itemId1, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId1, 0), block.timestamp);

        bytes20 itemId2 = itemStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x2345, 2);
        assertTrue(itemStore.getInUse(itemId2));
        assertEq(itemStore.getFlags(itemId2), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(itemStore.getOwner(itemId2), 0);
        assertEq(itemStore.getRevisionCount(itemId2), 1);
        assertTrue(itemStore.getUpdatable(itemId2));
        assertTrue(itemStore.getEnforceRevisions(itemId2));
        assertTrue(itemStore.getRetractable(itemId2));
        assertTrue(itemStore.getTransferable(itemId2));
        assertEq(itemStore.getRevisionIpfsHash(itemId2, 0), 0x2345);
        assertEq(itemStore.getRevisionTimestamp(itemId2, 0), block.timestamp);

        assertTrue(itemId0 != itemId1);
        assertTrue(itemId0 != itemId2);
        assertTrue(itemId1 != itemId2);
    }

    function testControlCreateNewRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
    }

    function testFailCreateNewRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.createNewRevision(itemId, 0x2345);
    }

    function testControlCreateNewRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
    }

    function testFailCreateNewRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
    }

    function testCreateNewRevision() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0, 0);
        uint revisionId = itemStore.createNewRevision(itemId, 1);
        assertEq(revisionId, 1);
        revisionId = itemStore.createNewRevision(itemId, 2);
        assertEq(revisionId, 2);
        revisionId = itemStore.createNewRevision(itemId, 3);
        assertEq(revisionId, 3);
        revisionId = itemStore.createNewRevision(itemId, 4);
        assertEq(revisionId, 4);
        revisionId = itemStore.createNewRevision(itemId, 5);
        assertEq(revisionId, 5);
        revisionId = itemStore.createNewRevision(itemId, 6);
        assertEq(revisionId, 6);
        revisionId = itemStore.createNewRevision(itemId, 7);
        assertEq(revisionId, 7);
        revisionId = itemStore.createNewRevision(itemId, 8);
        assertEq(revisionId, 8);
        revisionId = itemStore.createNewRevision(itemId, 9);
        assertEq(revisionId, 9);
        revisionId = itemStore.createNewRevision(itemId, 10);
        assertEq(revisionId, 10);
        revisionId = itemStore.createNewRevision(itemId, 11);
        assertEq(revisionId, 11);
        revisionId = itemStore.createNewRevision(itemId, 12);
        assertEq(revisionId, 12);
        revisionId = itemStore.createNewRevision(itemId, 13);
        assertEq(revisionId, 13);
        assertEq(itemStore.getRevisionCount(itemId), 14);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 2), 2);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 3), 3);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 4), 4);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 5), 5);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 6), 6);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 7), 7);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 8), 8);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 9), 9);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 10), 10);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 11), 11);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 12), 12);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 13), 13);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 2), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 3), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 4), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 5), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 6), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 7), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 8), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 9), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 10), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 11), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 12), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 13), block.timestamp);
    }

    function testControlUpdateLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.updateLatestRevision(itemId, 0x2345);
    }

    function testControlUpdateLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testControlUpdateLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testFailUpdateLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testUpdateLatestRevision() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        itemStore.updateLatestRevision(itemId, 0x2345);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x2345);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlRetractLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStoreProxy.retractLatestRevision(itemId);
    }

    function testControlRetractLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.setNotUpdatable(itemId);
        itemStore.retractLatestRevision(itemId);
    }

    function testControlRetractLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testControlRetractLatestRevisionDoesntHaveAdditionalRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionDoesntHaveAdditionalRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.retractLatestRevision(itemId);
    }

    function testRetractLatestRevision() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.createNewRevision(itemId, 0x3456);
        assertEq(itemStore.getRevisionCount(itemId), 3);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 0x2345);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 2), 0x3456);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 2), block.timestamp);
        itemStore.retractLatestRevision(itemId);
        assertEq(itemStore.getRevisionCount(itemId), 2);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 0x2345);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        itemStore.retractLatestRevision(itemId);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlRestartNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testFailRestartNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.restart(itemId, 0x2345);
    }

    function testControlRestartNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testFailRestartNotUpdatable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testControlRestartEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testFailRestartEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testRestart() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.createNewRevision(itemId, 0x3456);
        assertEq(itemStore.getRevisionCount(itemId), 3);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 0x2345);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 2), 0x3456);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 2), block.timestamp);
        itemStore.restart(itemId, 0x4567);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x4567);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlRetractNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStore.retract(itemId);
    }

    function testFailRetractNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStoreProxy.retract(itemId);
    }

    function testControlRetractNotRetractable() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStore.retract(itemId);
    }

    function testFailRetractNotRetractable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.retract(itemId);
    }

    function testRetract() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        assertTrue(itemStore.getInUse(itemId));
        assertEq(itemStore.getOwner(itemId), this);
        assertTrue(!itemStore.getUpdatable(itemId));
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        itemStore.retract(itemId);
        assertTrue(itemStore.getInUse(itemId));
        assertEq(itemStore.getOwner(itemId), 0);
        assertTrue(!itemStore.getUpdatable(itemId));
        assertEq(itemStore.getRevisionCount(itemId), 0);
    }

    function testControlTransferEnableNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
    }

    function testFailTransferEnableNotTransferable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
    }

    function testControlTransferDisableNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStoreProxy.transferDisable(itemId);
    }

    function testFailTransferDisableNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferDisable(itemId);
    }

    function testControlTransferNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testFailTransferNotTransferable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testControlTransferNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testFailTransferNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testControlTransferDisabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testFailTransferDisabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStoreProxy.transferDisable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testTransfer() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(itemStore.getOwner(itemId), this);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
        assertEq(itemStore.getOwner(itemId), itemStoreProxy);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlDisownNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.disown(itemId);
    }

    function testFailDisownNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.disown(itemId);
    }

    function testControlDisownNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.disown(itemId);
    }

    function testFailDisownNotTransferable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.disown(itemId);
    }

    function testDisown() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(itemStore.getOwner(itemId), this);
        itemStore.disown(itemId);
        assertEq(itemStore.getOwner(itemId), 0);
    }

    function testControlSetNotUpdatableNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.setNotUpdatable(itemId);
    }

    function testFailSetNotUpdatableNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.setNotUpdatable(itemId);
    }

    function testSetNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        assertTrue(itemStore.getUpdatable(itemId));
        itemStore.setNotUpdatable(itemId);
        assertTrue(!itemStore.getUpdatable(itemId));
    }

    function testControlSetEnforceRevisionsNotOwner() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.setEnforceRevisions(itemId);
    }

    function testFailSetEnforceRevisionsNotOwner() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStoreProxy.setEnforceRevisions(itemId);
    }

    function testSetEnforceRevisions() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        assertTrue(!itemStore.getEnforceRevisions(itemId));
        itemStore.setEnforceRevisions(itemId);
        assertTrue(itemStore.getEnforceRevisions(itemId));
    }

    function testControlSetNotRetractableNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStore.setNotRetractable(itemId);
    }

    function testFailSetNotRetractableNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStoreProxy.setNotRetractable(itemId);
    }

    function testSetNotRetractable() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        assertTrue(itemStore.getRetractable(itemId));
        itemStore.setNotRetractable(itemId);
        assertTrue(!itemStore.getRetractable(itemId));
    }

    function testControlSetNotTransferableNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.setNotTransferable(itemId);
    }

    function testFailSetNotTransferableNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.setNotTransferable(itemId);
    }

    function testSetNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        assertTrue(itemStore.getTransferable(itemId));
        itemStore.setNotTransferable(itemId);
        assertTrue(!itemStore.getTransferable(itemId));
    }

}

pragma solidity ^0.4.18;

import "ds-test/test.sol";

import "./item_store_registry.sol";
import "./item_store_ipfs_sha256.sol";
import "./item_store_ipfs_sha256_proxy.sol";

/**
 * @title ItemStoreIpfsSha256Test
 * @author Jonathan Brown <jbrown@mix-blockchain.org>
 * @dev Testing contract for ItemStoreIpfsSha256.
 */
contract ItemStoreIpfsSha256Test is DSTest {

    byte constant UPDATABLE = 0x01;           // True if the item is updatable. After creation can only be disabled.
    byte constant ENFORCE_REVISIONS = 0x02;   // True if the item is enforcing revisions. After creation can only be enabled.
    byte constant RETRACTABLE = 0x04;         // True if the item can be retracted. After creation can only be disabled.
    byte constant TRANSFERABLE = 0x08;        // True if the item be transfered to another user or disowned. After creation can only be disabled.
    byte constant ANONYMOUS = 0x10;           // True if the item should not have an owner.

    ItemStoreRegistry itemStoreRegistry;
    ItemStoreIpfsSha256 itemStore;
    ItemStoreIpfsSha256Proxy itemStoreProxy;

    function setUp() public {
        itemStoreRegistry = new ItemStoreRegistry();
        itemStore = new ItemStoreIpfsSha256(itemStoreRegistry);
        itemStoreProxy = new ItemStoreIpfsSha256Proxy(itemStore);
    }

    function testControlCreateSameNonce() public {
        itemStore.create(0, 0x1234, 0);
        itemStore.create(0, 0x1234, 1);
    }

    function testFailCreateSameNonce() public {
        itemStore.create(0, 0x1234, 0);
        itemStore.create(0, 0x1234, 0);
    }

    function testCreate() public {
        bytes20 itemId0 = itemStore.create(0, 0x1234, 0);
        assert(itemStore.getInUse(itemId0));
        assertEq(itemStore.getFlags(itemId0), 0);
        assertEq(itemStore.getOwner(itemId0), this);
        assertEq(itemStore.getRevisionCount(itemId0), 1);
        assert(!itemStore.getUpdatable(itemId0));
        assert(!itemStore.getEnforceRevisions(itemId0));
        assert(!itemStore.getRetractable(itemId0));
        assert(!itemStore.getTransferable(itemId0));
        assertEq(itemStore.getRevisionIpfsHash(itemId0, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId0, 0), block.timestamp);

        bytes20 itemId1 = itemStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x1234, 1);
        assert(itemStore.getInUse(itemId1));
        assertEq(itemStore.getFlags(itemId1), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(itemStore.getOwner(itemId1), 0);
        assertEq(itemStore.getRevisionCount(itemId1), 1);
        assert(itemStore.getUpdatable(itemId1));
        assert(itemStore.getEnforceRevisions(itemId1));
        assert(itemStore.getRetractable(itemId1));
        assert(itemStore.getTransferable(itemId1));
        assertEq(itemStore.getRevisionIpfsHash(itemId1, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId1, 0), block.timestamp);

        bytes20 itemId2 = itemStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x2345, 2);
        assert(itemStore.getInUse(itemId2));
        assertEq(itemStore.getFlags(itemId2), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(itemStore.getOwner(itemId2), 0);
        assertEq(itemStore.getRevisionCount(itemId2), 1);
        assert(itemStore.getUpdatable(itemId2));
        assert(itemStore.getEnforceRevisions(itemId2));
        assert(itemStore.getRetractable(itemId2));
        assert(itemStore.getTransferable(itemId2));
        assertEq(itemStore.getRevisionIpfsHash(itemId2, 0), 0x2345);
        assertEq(itemStore.getRevisionTimestamp(itemId2, 0), block.timestamp);

        assert(itemId0 != itemId1);
        assert(itemId0 != itemId2);
        assert(itemId1 != itemId2);
    }

    function testControlCreateNewRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
    }

    function testFailCreateNewRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.createNewRevision(itemId, 0x2345);
    }

    function testControlCreateNewRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
    }

    function testFailCreateNewRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
    }

    function testCreateNewRevision() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0, 0);
        uint revisionId = itemStore.createNewRevision(itemId, 1);
        assertEq(revisionId, 1);
        revisionId = itemStore.createNewRevision(itemId, 2);
        assertEq(revisionId, 2);
        revisionId = itemStore.createNewRevision(itemId, 3);
        assertEq(revisionId, 3);
        revisionId = itemStore.createNewRevision(itemId, 4);
        assertEq(revisionId, 4);
        revisionId = itemStore.createNewRevision(itemId, 5);
        assertEq(revisionId, 5);
        revisionId = itemStore.createNewRevision(itemId, 6);
        assertEq(revisionId, 6);
        revisionId = itemStore.createNewRevision(itemId, 7);
        assertEq(revisionId, 7);
        revisionId = itemStore.createNewRevision(itemId, 8);
        assertEq(revisionId, 8);
        revisionId = itemStore.createNewRevision(itemId, 9);
        assertEq(revisionId, 9);
        revisionId = itemStore.createNewRevision(itemId, 10);
        assertEq(revisionId, 10);
        revisionId = itemStore.createNewRevision(itemId, 11);
        assertEq(revisionId, 11);
        revisionId = itemStore.createNewRevision(itemId, 12);
        assertEq(revisionId, 12);
        revisionId = itemStore.createNewRevision(itemId, 13);
        assertEq(revisionId, 13);
        assertEq(itemStore.getRevisionCount(itemId), 14);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 2), 2);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 3), 3);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 4), 4);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 5), 5);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 6), 6);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 7), 7);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 8), 8);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 9), 9);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 10), 10);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 11), 11);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 12), 12);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 13), 13);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 2), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 3), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 4), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 5), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 6), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 7), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 8), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 9), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 10), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 11), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 12), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 13), block.timestamp);
    }

    function testControlUpdateLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.updateLatestRevision(itemId, 0x2345);
    }

    function testControlUpdateLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testControlUpdateLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testFailUpdateLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        itemStore.updateLatestRevision(itemId, 0x2345);
    }

    function testUpdateLatestRevision() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        itemStore.updateLatestRevision(itemId, 0x2345);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x2345);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlRetractLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStoreProxy.retractLatestRevision(itemId);
    }

    function testControlRetractLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.setNotUpdatable(itemId);
        itemStore.retractLatestRevision(itemId);
    }

    function testControlRetractLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testControlRetractLatestRevisionDoesntHaveAdditionalRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.retractLatestRevision(itemId);
    }

    function testFailRetractLatestRevisionDoesntHaveAdditionalRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.retractLatestRevision(itemId);
    }

    function testRetractLatestRevision() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.createNewRevision(itemId, 0x3456);
        assertEq(itemStore.getRevisionCount(itemId), 3);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 0x2345);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 2), 0x3456);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 2), block.timestamp);
        itemStore.retractLatestRevision(itemId);
        assertEq(itemStore.getRevisionCount(itemId), 2);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 0x2345);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        itemStore.retractLatestRevision(itemId);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlRestartNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testFailRestartNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.restart(itemId, 0x2345);
    }

    function testControlRestartNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testFailRestartNotUpdatable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testControlRestartEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testFailRestartEnforceRevisions() public {
        bytes20 itemId = itemStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        itemStore.restart(itemId, 0x2345);
    }

    function testRestart() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.createNewRevision(itemId, 0x2345);
        itemStore.createNewRevision(itemId, 0x3456);
        assertEq(itemStore.getRevisionCount(itemId), 3);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 1), 0x2345);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 2), 0x3456);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 1), block.timestamp);
        assertEq(itemStore.getRevisionTimestamp(itemId, 2), block.timestamp);
        itemStore.restart(itemId, 0x4567);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x4567);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlRetractNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStore.retract(itemId);
    }

    function testFailRetractNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStoreProxy.retract(itemId);
    }

    function testControlRetractNotRetractable() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStore.retract(itemId);
    }

    function testFailRetractNotRetractable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.retract(itemId);
    }

    function testRetract() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        assert(itemStore.getInUse(itemId));
        assertEq(itemStore.getOwner(itemId), this);
        assert(!itemStore.getUpdatable(itemId));
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        itemStore.retract(itemId);
        assert(itemStore.getInUse(itemId));
        assertEq(itemStore.getOwner(itemId), 0);
        assert(!itemStore.getUpdatable(itemId));
        assertEq(itemStore.getRevisionCount(itemId), 0);
    }

    function testControlTransferEnableNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
    }

    function testFailTransferEnableNotTransferable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
    }

    function testControlTransferDisableNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStoreProxy.transferDisable(itemId);
    }

    function testFailTransferDisableNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferDisable(itemId);
    }

    function testControlTransferNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testFailTransferNotTransferable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testControlTransferNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testFailTransferNotEnabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testControlTransferDisabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testFailTransferDisabled() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.transferEnable(itemId);
        itemStoreProxy.transferDisable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
    }

    function testTransfer() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(itemStore.getOwner(itemId), this);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
        itemStoreProxy.transferEnable(itemId);
        itemStore.transfer(itemId, itemStoreProxy);
        assertEq(itemStore.getOwner(itemId), itemStoreProxy);
        assertEq(itemStore.getRevisionCount(itemId), 1);
        assertEq(itemStore.getRevisionIpfsHash(itemId, 0), 0x1234);
        assertEq(itemStore.getRevisionTimestamp(itemId, 0), block.timestamp);
    }

    function testControlDisownNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.disown(itemId);
    }

    function testFailDisownNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.disown(itemId);
    }

    function testControlDisownNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.disown(itemId);
    }

    function testFailDisownNotTransferable() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.disown(itemId);
    }

    function testDisown() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(itemStore.getOwner(itemId), this);
        itemStore.disown(itemId);
        assertEq(itemStore.getOwner(itemId), 0);
    }

    function testControlSetNotUpdatableNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStore.setNotUpdatable(itemId);
    }

    function testFailSetNotUpdatableNotOwner() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        itemStoreProxy.setNotUpdatable(itemId);
    }

    function testSetNotUpdatable() public {
        bytes20 itemId = itemStore.create(UPDATABLE, 0x1234, 0);
        assert(itemStore.getUpdatable(itemId));
        itemStore.setNotUpdatable(itemId);
        assert(!itemStore.getUpdatable(itemId));
    }

    function testControlSetEnforceRevisionsNotOwner() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStore.setEnforceRevisions(itemId);
    }

    function testFailSetEnforceRevisionsNotOwner() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        itemStoreProxy.setEnforceRevisions(itemId);
    }

    function testSetEnforceRevisions() public {
        bytes20 itemId = itemStore.create(0, 0x1234, 0);
        assert(!itemStore.getEnforceRevisions(itemId));
        itemStore.setEnforceRevisions(itemId);
        assert(itemStore.getEnforceRevisions(itemId));
    }

    function testControlSetNotRetractableNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStore.setNotRetractable(itemId);
    }

    function testFailSetNotRetractableNotOwner() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        itemStoreProxy.setNotRetractable(itemId);
    }

    function testSetNotRetractable() public {
        bytes20 itemId = itemStore.create(RETRACTABLE, 0x1234, 0);
        assert(itemStore.getRetractable(itemId));
        itemStore.setNotRetractable(itemId);
        assert(!itemStore.getRetractable(itemId));
    }

    function testControlSetNotTransferableNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStore.setNotTransferable(itemId);
    }

    function testFailSetNotTransferableNotOwner() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        itemStoreProxy.setNotTransferable(itemId);
    }

    function testSetNotTransferable() public {
        bytes20 itemId = itemStore.create(TRANSFERABLE, 0x1234, 0);
        assert(itemStore.getTransferable(itemId));
        itemStore.setNotTransferable(itemId);
        assert(!itemStore.getTransferable(itemId));
    }

}


Check that contract is not already registered in modifier instead of inline.

pragma solidity ^0.4.0;

import "abstract_blobstore.sol";

/**
 * @title BlobStoreRegistry
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract BlobStoreRegistry {

    /**
     * @dev Mapping of contract id to contract addresses.
     */
    mapping (bytes12 => address) contractAddresses;

    /**
     * @dev An AbstractBlobStore contract has been registered.
     * @param contractId Id of the contract.
     * @param contractAddress Address of the contract.
     */
    event logRegistration(bytes12 indexed contractId, address indexed contractAddress);

    /**
     * @dev Throw if contract is registered.
     * @param contractId Id of the contract.
     */
    modifier isNotRegistered(bytes12 contractId) {
        if (contractAddresses[contractId] != 0) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if contract is not registered.
     * @param contractId Id of the contract.
     */
    modifier isRegistered(bytes12 contractId) {
        if (contractAddresses[contractId] == 0) {
            throw;
        }
        _;
    }

    /**
     * @dev Register the calling BlobStore contract.
     * @param contractId Id of the BlobStore contract.
     */
    function register(bytes12 contractId) isNotRegistered(contractId) external {
        // Record the calling contract address.
        contractAddresses[contractId] = msg.sender;
        // Log the registration.
        logRegistration(contractId, msg.sender);
    }

    /**
     * @dev Get an AbstractBlobStore contract.
     * @param contractId Id of the contract.
     * @return blobStore The AbstractBlobStore contract.
     */
    function getBlobStore(bytes12 contractId) isRegistered(contractId) constant external returns (AbstractBlobStore blobStore) {
        blobStore = AbstractBlobStore(contractAddresses[contractId]);
    }

}

pragma solidity ^0.4.0;

import "abstract_blobstore.sol";

/**
 * @title BlobStoreRegistry
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract BlobStoreRegistry {

    /**
     * @dev Mapping of contract id to contract addresses.
     */
    mapping (bytes12 => address) contractAddresses;

    /**
     * @dev An AbstractBlobStore contract has been registered.
     * @param contractId Id of the contract.
     * @param contractAddress Address of the contract.
     */
    event logRegistration(bytes12 indexed contractId, address indexed contractAddress);

    /**
     * @dev Throw if contract is not registered.
     * @param contractId Id of the contract.
     */
    modifier isRegistered(bytes12 contractId) {
        if (contractAddresses[contractId] == 0) {
            throw;
        }
        _;
    }

    /**
     * @dev Register the calling BlobStore contract.
     * @param contractId Id of the BlobStore contract.
     */
    function register(bytes12 contractId) external {
        // Check if this contractId has been registered before.
        if (contractAddresses[contractId] != 0) {
            throw;
        }
        // Record the calling contract address.
        contractAddresses[contractId] = msg.sender;
        // Log the registration.
        logRegistration(contractId, msg.sender);
    }

    /**
     * @dev Get an AbstractBlobStore contract.
     * @param contractId Id of the contract.
     * @return blobStore The AbstractBlobStore contract.
     */
    function getBlobStore(bytes12 contractId) isRegistered(contractId) constant external returns (AbstractBlobStore blobStore) {
        blobStore = AbstractBlobStore(contractAddresses[contractId]);
    }

}


Update src/main.sol

Update src/main.sol

pragma solidity ^0.4.11;

// https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs#transferable-fungibles-see-erc-20-for-the-latest

contract ERC20Token {
    // Triggered when tokens are transferred.
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    // Triggered whenever approve(address _spender, uint256 _value) is called.
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // Get the total token supply
    function totalSupply() constant returns (uint256 supply);

    // Get the account `balance` of another account with address `_owner`
    function balanceOf(address _owner) constant returns (uint256 balance);

    // Send `_value` amount of tokens to address `_to`
    function transfer(address _to, uint256 _value) returns (bool success);

    // Send `_value` amount of tokens from address `_from` to address `_to`
    // The transferFrom method is used for a withdraw workflow, allowing contracts to send tokens on your behalf,
    // for example to "deposit" to a contract address and/or to charge fees in sub-currencies;
    // the command should fail unless the _from account has deliberately authorized the sender of the message
    // via some mechanism; we propose these standardized APIs for approval:
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);

    // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
    // If this function is called again it overwrites the current allowance with _value.
    function approve(address _spender, uint256 _value) returns (bool success);

    // Returns the amount which _spender is still allowed to withdraw from _owner
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);
}

contract PrimasToken is ERC20Token {
    address public initialOwner;
    uint256 public supply   = 100000000;  // 100, 000, 000
    string  public name     = 'Primas';
    uint8   public decimals = 0;
    string  public symbol   = 'PST';
    string  public version  = 'v0.1';
    bool    public transfersEnabled = true;
    uint    public creationBlock;

    mapping (address => uint256) balance;
    mapping (address => mapping (address => uint256)) m_allowance;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    function PrimasToken() {
        initialOwner        = msg.sender;
        balance[msg.sender] = supply;
        creationBlock       = block.number;
    }

    function balanceOf(address _account) constant returns (uint) {
        return balance[_account];
    }

    function totalSupply() constant returns (uint) {
        return supply;
    }

    function transfer(address _to, uint256 _value) returns (bool success) {
        if (!transfersEnabled) throw;

        Transfer(msg.sender, _to, _value);

        return doTransfer(msg.sender, _to, _value);
    }

    function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
        if (msg.sender != initialOwner) throw;
        if (!transfersEnabled) throw;

        if (m_allowance[_from][msg.sender] < _value) return false;

        if (doTransfer(_from, _to, _value)) {
            m_allowance[_from][msg.sender] -= _value;
            return true;
        } else {
            return false;
        }
    }

    function doTransfer(address _from, address _to, uint _value) internal returns (bool success) {
        if (!transfersEnabled) throw;
        if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) {
            balance[_from] -= _value;
            balance[_to] += _value;
            Transfer(_from, _to, _value);
            return true;
        } else {
            return false;
        }
    }

    function approve(address _spender, uint256 _value) returns (bool success) {
        if (!transfersEnabled) throw;

        // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        if ( (_value != 0) && (m_allowance[msg.sender][_spender] != 0) ) throw;

        m_allowance[msg.sender][_spender] = _value;

        Approval(msg.sender, _spender, _value);

        return true;
    }

    function allowance(address _owner, address _spender) constant returns (uint256) {
        if (!transfersEnabled) throw;

        return m_allowance[_owner][_spender];
    }

    function enableTransfers(bool _transfersEnabled) constant returns (bool) {
        if (msg.sender != initialOwner) throw;
        transfersEnabled = _transfersEnabled;
        return transfersEnabled;
    }
}

pragma solidity ^0.4.11;

// https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs#transferable-fungibles-see-erc-20-for-the-latest

contract ERC20Token {
    // Triggered when tokens are transferred.
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    // Triggered whenever approve(address _spender, uint256 _value) is called.
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // Get the total token supply
    function totalSupply() constant returns (uint256 supply);

    // Get the account `balance` of another account with address `_owner`
    function balanceOf(address _owner) constant returns (uint256 balance);

    // Send `_value` amount of tokens to address `_to`
    function transfer(address _to, uint256 _value) returns (bool success);

    // Send `_value` amount of tokens from address `_from` to address `_to`
    // The transferFrom method is used for a withdraw workflow, allowing contracts to send tokens on your behalf,
    // for example to "deposit" to a contract address and/or to charge fees in sub-currencies;
    // the command should fail unless the _from account has deliberately authorized the sender of the message
    // via some mechanism; we propose these standardized APIs for approval:
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);

    // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
    // If this function is called again it overwrites the current allowance with _value.
    function approve(address _spender, uint256 _value) returns (bool success);

    // Returns the amount which _spender is still allowed to withdraw from _owner
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);
}

contract PrimasToken is ERC20Token {
    address public initialOwner;
    uint256 public supply   = 100000000;  // 100, 000, 000
    string  public name     = 'Primas';
    uint8   public decimals = 0;
    string  public symbol   = 'PST';
    string  public version  = 'v0.1';
    bool    public transfersEnabled = true;
    uint    public creationBlock;

    mapping (address => uint256) balance;
    mapping (address => mapping (address => uint256)) m_allowance;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    function PrimasToken() {
        initialOwner        = msg.sender;
        balance[msg.sender] = supply;
        creationBlock       = block.number;
    }

    function balanceOf(address _account) constant returns (uint) {
        return balance[_account];
    }

    function totalSupply() constant returns (uint) {
        return supply;
    }

    function transfer(address _to, uint256 _value) returns (bool success) {
        if (!transfersEnabled) throw;

        Transfer(msg.sender, _to, _value);

        return doTransfer(msg.sender, _to, _value);
    }

    function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
        if (msg.sender != initialOwner) throw;
        if (!transfersEnabled) throw;

        if (m_allowance[_from][msg.sender] < _value) return false;

        if (doTransfer(_from, _to, _value)) {
            m_allowance[_from][msg.sender] -= _value;
            return true;
        } else {
            return false;
        }
    }

    function doTransfer(address _from, address _to, uint _value) internal returns (bool success) {
        if (!transfersEnabled) throw;

        if (_from == initialOwner) {
            if ((balance[_from] - _value) <= 4900) {
                // 冻结
                return false;
            }
        }

        if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) {
            balance[_from] -= _value;
            balance[_to] += _value;
            Transfer(_from, _to, _value);
            return true;
        } else {
            return false;
        }
    }

    function approve(address _spender, uint256 _value) returns (bool success) {
        if (!transfersEnabled) throw;

        // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        if ( (_value != 0) && (m_allowance[msg.sender][_spender] != 0) ) throw;

        m_allowance[msg.sender][_spender] = _value;

        Approval(msg.sender, _spender, _value);

        return true;
    }

    function allowance(address _owner, address _spender) constant returns (uint256) {
        if (!transfersEnabled) throw;

        return m_allowance[_owner][_spender];
    }

    function enableTransfers(bool _transfersEnabled) constant returns (bool) {
        if (msg.sender != initialOwner) throw;
        transfersEnabled = _transfersEnabled;
        return transfersEnabled;
    }
}


updating some documentation

updating some documentation

/*
    Copyright 2017 Phillip A. Elsasser

    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.
*/

pragma solidity 0.4.18;

import "./libraries/MathLib.sol";
import "./tokens/MarketToken.sol";
import "./Linkable.sol";
import "./MarketContract.sol";

import "zeppelin-solidity/contracts/token/SafeERC20.sol";
import "zeppelin-solidity/contracts/token/ERC20.sol";

/// @title MarketCollateralPool is a contract controlled by a specific Market Contract.  It holds collateral balances
/// as well as user balances and open positions.  It should be instantiated and then linked by a MarketContract.
/// @author Phil Elsasser <phil@marketprotcol.io>
contract MarketCollateralPool is Linkable {
    using MathLib for uint;
    using MathLib for int;
    using SafeERC20 for ERC20;

    struct UserNetPosition {
        address userAddress;
        Position[] positions;   // all open positions (lifo upon exit - allows us to not reindex array!)
        int netPosition;        // net position across all prices / executions
    }

    struct Position {
        uint price;
        int qty;
    }

    uint public collateralPoolBalance;                                 // current balance of all collateral committed
    mapping(address => UserNetPosition) addressToUserPosition;
    mapping(address => uint) public userAddressToAccountBalance;       // stores account balances allowed to be allocated to orders
    address public MKT_TOKEN_ADDRESS;
    MarketToken MKT_TOKEN;
    MarketContract MKT_CONTRACT;

    event UpdatedUserBalance(address indexed user, uint balance);
    event UpdatedPoolBalance(uint balance);

    /// @dev instantiates a collateral pool that is unique to the supplied address of a MarketContract. This pairing
    /// is 1:1
    /// @param marketContractAddress deployed address of a MarketContract
    function MarketCollateralPool(address marketContractAddress) Linkable(marketContractAddress) public {
        MKT_CONTRACT = MarketContract(marketContractAddress);
        MKT_TOKEN_ADDRESS = MKT_CONTRACT.MKT_TOKEN_ADDRESS();
        MKT_TOKEN = MarketToken(MKT_TOKEN_ADDRESS);
    }

    /// @notice get the net position for a give user address
    /// @param userAddress address to return position for
    /// @return the users current open position.
    function getUserPosition(address userAddress) external view returns (int) {
        return addressToUserPosition[userAddress].netPosition;
    }

    /// @param userAddress address of user
    /// @return the users currently unallocated token balance
    function getUserAccountBalance(address userAddress) external view returns (uint) {
        return userAddressToAccountBalance[userAddress];
    }

    /*
    // EXTERNAL METHODS
    */

    /// @notice deposits tokens to the smart contract to fund the user account and provide needed tokens for collateral
    /// pool upon trade matching.
    /// @param depositAmount qty of ERC20 tokens to deposit to the smart contract to cover open orders and collateral
    function depositTokensForTrading(uint256 depositAmount) external {
        // user must call approve!
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        uint256 balanceAfterDeposit = userAddressToAccountBalance[msg.sender].add(depositAmount);
        ERC20(MKT_CONTRACT.BASE_TOKEN_ADDRESS()).safeTransferFrom(msg.sender, this, depositAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterDeposit;
        UpdatedUserBalance(msg.sender, balanceAfterDeposit);
    }

    // @notice called by a user after settlement has occurred.  This function will finalize all accounting around any
    // outstanding positions and return all remaining collateral to the caller. This should only be called after
    // settlement has occurred.
    function settleAndClose() external {
        require(MKT_CONTRACT.isSettled());
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        UserNetPosition storage userNetPos = addressToUserPosition[msg.sender];
        if (userNetPos.netPosition != 0) {
            // this user has a position that we need to settle based upon the settlement price of the contract
            reduceUserNetPosition(
                userNetPos,
                msg.sender,
                userNetPos.netPosition * - 1,
                MKT_CONTRACT.settlementPrice()
            );
        }
        // transfer all balances back to user.
        withdrawTokens(userAddressToAccountBalance[msg.sender]);
    }

    /// @dev called by our linked MarketContract when a trade occurs to update both maker and takers positions.
    /// @param maker address of the maker in the trade
    /// @param taker address of the taker in the trade
    /// @param qty quantity transacted between parties
    /// @param price agreed price of the matched trade.
    function updatePositions(
        address maker,
        address taker,
        int qty,
        uint price
    ) external onlyLinked
    {
        updatePosition(
            maker,
            qty,
            price
        );
        // continue process for taker, but qty is opposite sign for taker
        updatePosition(
            taker,
            qty * -1,
            price
        );
    }

    /*
    // PUBLIC METHODS
    */

    /// @notice removes token from users trading account
    /// @param withdrawAmount qty of token to attempt to withdraw
    function withdrawTokens(uint256 withdrawAmount) public {
        require(userAddressToAccountBalance[msg.sender] >= withdrawAmount);   // ensure sufficient balance
        uint256 balanceAfterWithdrawal = userAddressToAccountBalance[msg.sender].subtract(withdrawAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterWithdrawal;   // update balance before external call!
        ERC20(MKT_CONTRACT.BASE_TOKEN_ADDRESS()).safeTransfer(msg.sender, withdrawAmount);
        UpdatedUserBalance(msg.sender, balanceAfterWithdrawal);
    }

    /*
    // PRIVATE METHODS
    */

    /// @notice moves collateral from a user's account to the pool upon trade execution.
    /// @param fromAddress address of user entering trade
    /// @param collateralAmount amount of collateral to transfer from user account to collateral pool
    function commitCollateralToPool(
        address fromAddress,
        uint collateralAmount
    ) private
    {
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        require(userAddressToAccountBalance[fromAddress] >= collateralAmount);   // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[fromAddress].subtract(collateralAmount);
        userAddressToAccountBalance[fromAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.add(collateralAmount);
        UpdatedUserBalance(fromAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    /// @notice withdraws collateral from pool to a user account upon exit or trade settlement
    /// @param toAddress address of user
    /// @param collateralAmount amount to transfer from pool to user.
    function withdrawCollateralFromPool(
        address toAddress,
        uint collateralAmount
    ) private
    {
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        require(collateralPoolBalance >= collateralAmount); // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[toAddress].add(collateralAmount);
        userAddressToAccountBalance[toAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.subtract(collateralAmount);
        UpdatedUserBalance(toAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    /// @dev handles all needed internal accounting when a user enters into a new trade
    /// @param userAddress storage struct containing position information for this user
    /// @param qty signed quantity this users position is changing by, + for buy and - for sell
    /// @param price transacted price of the new position / trade
    function updatePosition(
        address userAddress,
        int qty,
        uint price
    ) private
    {
        UserNetPosition storage userNetPosition = addressToUserPosition[userAddress];
        if (userNetPosition.netPosition == 0 || userNetPosition.netPosition.isSameSign(qty)) {
            // new position or adding to open pos
            addUserNetPosition(
                userNetPosition,
                userAddress,
                qty,
                price
            );
        } else {  // opposite side from open position, reduce, flattened, or flipped.
            if (userNetPosition.netPosition >= qty * -1) { // pos is reduced or flattened
                reduceUserNetPosition(
                    userNetPosition,
                    userAddress,
                    qty,
                    price
                );
            } else {    // pos is flipped, reduce and then create new open pos!

                reduceUserNetPosition(
                    userNetPosition,
                    userAddress,
                    userNetPosition.netPosition * -1,
                    price
                ); // flatten completely

                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                addUserNetPosition(
                    userNetPosition,
                    userAddress,
                    newNetPos,
                    price
                );
            }
        }
        userNetPosition.netPosition = userNetPosition.netPosition.add(qty);   // keep track of total net pos across all prices for user.
    }

    /// @dev calculates the needed collateral for a new position and commits it to the pool removing it from the
    /// users account and creates the needed Position struct to record the new position.
    /// @param userNetPosition current positions held by user
    /// @param userAddress address of user entering into the position
    /// @param qty signed quantity of the trade
    /// @param price agreed price of trade
    function addUserNetPosition(
        UserNetPosition storage userNetPosition,
        address userAddress,
        int qty,
        uint price
    ) private
    {
        uint neededCollateral = MathLib.calculateNeededCollateral(
            MKT_CONTRACT.PRICE_FLOOR(),
            MKT_CONTRACT.PRICE_CAP(),
            MKT_CONTRACT.QTY_DECIMAL_PLACES(),
            qty,
            price
        );
        commitCollateralToPool(userAddress, neededCollateral);
        userNetPosition.positions.push(Position(price, qty));   // append array with new position
    }

    /// @dev reduces net position correctly allocating collateral back to user
    /// @param userNetPos storage struct for this users position
    /// @param userAddress address of user who is reducing their pos
    /// @param qty signed quantity of the qty to reduce this users position by
    /// @param price transacted price
    function reduceUserNetPosition(
        UserNetPosition storage userNetPos,
        address userAddress,
        int qty,
        uint price
    ) private
    {
        uint collateralToReturnToUserAccount = 0;
        int qtyToReduce = qty;                      // note: this sign is opposite of our users position
        assert(userNetPos.positions.length != 0);   // sanity check
        while (qtyToReduce != 0) {   //TODO: ensure we dont run out of gas here!
            Position storage position = userNetPos.positions[userNetPos.positions.length - 1];  // get the last pos (LIFO)
            if (position.qty.abs() <= qtyToReduce.abs()) {   // this position is completely consumed!
                collateralToReturnToUserAccount = collateralToReturnToUserAccount.add(
                    MathLib.calculateNeededCollateral(
                        MKT_CONTRACT.PRICE_FLOOR(),
                        MKT_CONTRACT.PRICE_CAP(),
                        MKT_CONTRACT.QTY_DECIMAL_PLACES(),
                        position.qty,
                        price
                    )
                );
                qtyToReduce = qtyToReduce.add(position.qty);
                userNetPos.positions.length--;  // remove this position from our array.
            } else {  // this position stays, just reduce the qty.
                position.qty = position.qty.add(qtyToReduce);
                // pos is opp sign of qty we are reducing here!
                collateralToReturnToUserAccount = collateralToReturnToUserAccount.add(
                    MathLib.calculateNeededCollateral(
                        MKT_CONTRACT.PRICE_FLOOR(),
                        MKT_CONTRACT.PRICE_CAP(),
                        MKT_CONTRACT.QTY_DECIMAL_PLACES(),
                        qtyToReduce * -1,
                        price
                    )
                );
                //qtyToReduce = 0; // completely reduced now!
                break;
            }
        }

        if (collateralToReturnToUserAccount != 0) {  // allocate funds back to user acct.
            withdrawCollateralFromPool(userAddress, collateralToReturnToUserAccount);
        }
    }
}

/*
    Copyright 2017 Phillip A. Elsasser

    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.
*/

pragma solidity 0.4.18;

import "./libraries/MathLib.sol";
import "./tokens/MarketToken.sol";
import "./Linkable.sol";
import "./MarketContract.sol";

import "zeppelin-solidity/contracts/token/SafeERC20.sol";
import "zeppelin-solidity/contracts/token/ERC20.sol";

/// @title MarketCollateralPool is a contract controlled by a specific Market Contract.  It holds collateral balances
/// as well as user balances and open positions.  It should be instantiated and then linked by a MarketContract.
/// @author Phil Elsasser <phil@marketprotcol.io>
contract MarketCollateralPool is Linkable {
    using MathLib for uint;
    using MathLib for int;
    using SafeERC20 for ERC20;

    struct UserNetPosition {
        address userAddress;
        Position[] positions;   // all open positions (lifo upon exit - allows us to not reindex array!)
        int netPosition;        // net position across all prices / executions
    }

    struct Position {
        uint price;
        int qty;
    }

    uint public collateralPoolBalance;                                 // current balance of all collateral committed
    mapping(address => UserNetPosition) addressToUserPosition;
    mapping(address => uint) public userAddressToAccountBalance;       // stores account balances allowed to be allocated to orders
    address public MKT_TOKEN_ADDRESS;
    MarketToken MKT_TOKEN;
    MarketContract MKT_CONTRACT;

    event UpdatedUserBalance(address indexed user, uint balance);
    event UpdatedPoolBalance(uint balance);

    function MarketCollateralPool(address marketContractAddress) Linkable(marketContractAddress) public {
        MKT_CONTRACT = MarketContract(marketContractAddress);
        MKT_TOKEN_ADDRESS = MKT_CONTRACT.MKT_TOKEN_ADDRESS();
        MKT_TOKEN = MarketToken(MKT_TOKEN_ADDRESS);
    }

    /// @notice get the net position for a give user address
    /// @param userAddress address to return position for
    /// @return the users current open position.
    function getUserPosition(address userAddress) external view returns (int) {
        return addressToUserPosition[userAddress].netPosition;
    }

    /// @param userAddress address of user
    /// @return the users currently unallocated token balance
    function getUserAccountBalance(address userAddress) external view returns (uint) {
        return userAddressToAccountBalance[userAddress];
    }

    /*
    // EXTERNAL METHODS
    */

    /// @notice deposits tokens to the smart contract to fund the user account and provide needed tokens for collateral
    /// pool upon trade matching.
    /// @param depositAmount qty of ERC20 tokens to deposit to the smart contract to cover open orders and collateral
    function depositTokensForTrading(uint256 depositAmount) external {
        // user must call approve!
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        uint256 balanceAfterDeposit = userAddressToAccountBalance[msg.sender].add(depositAmount);
        ERC20(MKT_CONTRACT.BASE_TOKEN_ADDRESS()).safeTransferFrom(msg.sender, this, depositAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterDeposit;
        UpdatedUserBalance(msg.sender, balanceAfterDeposit);
    }

    // @notice called by a user after settlement has occurred.  This function will finalize all accounting around any
    // outstanding positions and return all remaining collateral to the caller. This should only be called after
    // settlement has occurred.
    function settleAndClose() external {
        require(MKT_CONTRACT.isSettled());
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        UserNetPosition storage userNetPos = addressToUserPosition[msg.sender];
        if (userNetPos.netPosition != 0) {
            // this user has a position that we need to settle based upon the settlement price of the contract
            reduceUserNetPosition(
                userNetPos,
                msg.sender,
                userNetPos.netPosition * - 1,
                MKT_CONTRACT.settlementPrice()
            );
        }
        // transfer all balances back to user.
        withdrawTokens(userAddressToAccountBalance[msg.sender]);
    }

    /// @param maker address of the maker in the trade
    /// @param taker address of the taker in the trade
    /// @param qty quantity transacted between parties
    /// @param price agreed price of the matched trade.
    function updatePositions(
        address maker,
        address taker,
        int qty,
        uint price
    ) external onlyLinked
    {
        updatePosition(
            maker,
            qty,
            price
        );
        // continue process for taker, but qty is opposite sign for taker
        updatePosition(
            taker,
            qty * -1,
            price
        );
    }

    /*
    // PUBLIC METHODS
    */

    /// @notice removes token from users trading account
    /// @param withdrawAmount qty of token to attempt to withdraw
    function withdrawTokens(uint256 withdrawAmount) public {
        require(userAddressToAccountBalance[msg.sender] >= withdrawAmount);   // ensure sufficient balance
        uint256 balanceAfterWithdrawal = userAddressToAccountBalance[msg.sender].subtract(withdrawAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterWithdrawal;   // update balance before external call!
        ERC20(MKT_CONTRACT.BASE_TOKEN_ADDRESS()).safeTransfer(msg.sender, withdrawAmount);
        UpdatedUserBalance(msg.sender, balanceAfterWithdrawal);
    }

    /*
    // PRIVATE METHODS
    */

    /// @notice moves collateral from a user's account to the pool upon trade execution.
    /// @param fromAddress address of user entering trade
    /// @param collateralAmount amount of collateral to transfer from user account to collateral pool
    function commitCollateralToPool(
        address fromAddress,
        uint collateralAmount
    ) private
    {
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        require(userAddressToAccountBalance[fromAddress] >= collateralAmount);   // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[fromAddress].subtract(collateralAmount);
        userAddressToAccountBalance[fromAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.add(collateralAmount);
        UpdatedUserBalance(fromAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    /// @notice withdraws collateral from pool to a user account upon exit or trade settlement
    /// @param toAddress address of user
    /// @param collateralAmount amount to transfer from pool to user.
    function withdrawCollateralFromPool(
        address toAddress,
        uint collateralAmount
    ) private
    {
        require(MKT_TOKEN.isUserEnabledForContract(address(MKT_CONTRACT), msg.sender));
        require(collateralPoolBalance >= collateralAmount); // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[toAddress].add(collateralAmount);
        userAddressToAccountBalance[toAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.subtract(collateralAmount);
        UpdatedUserBalance(toAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    /// @dev handles all needed internal accounting when a user enters into a new trade
    /// @param userAddress storage struct containing position information for this user
    /// @param qty signed quantity this users position is changing by, + for buy and - for sell
    /// @param price transacted price of the new position / trade
    function updatePosition(
        address userAddress,
        int qty,
        uint price
    ) private
    {
        UserNetPosition storage userNetPosition = addressToUserPosition[userAddress];
        if (userNetPosition.netPosition == 0 || userNetPosition.netPosition.isSameSign(qty)) {
            // new position or adding to open pos
            addUserNetPosition(
                userNetPosition,
                userAddress,
                qty,
                price
            );
        } else {  // opposite side from open position, reduce, flattened, or flipped.
            if (userNetPosition.netPosition >= qty * -1) { // pos is reduced or flattened
                reduceUserNetPosition(
                    userNetPosition,
                    userAddress,
                    qty,
                    price
                );
            } else {    // pos is flipped, reduce and then create new open pos!

                reduceUserNetPosition(
                    userNetPosition,
                    userAddress,
                    userNetPosition.netPosition * -1,
                    price
                ); // flatten completely

                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                addUserNetPosition(
                    userNetPosition,
                    userAddress,
                    newNetPos,
                    price
                );
            }
        }
        userNetPosition.netPosition = userNetPosition.netPosition.add(qty);   // keep track of total net pos across all prices for user.
    }

    /// @dev calculates the needed collateral for a new position and commits it to the pool removing it from the
    /// users account and creates the needed Position struct to record the new position.
    /// @param userNetPosition current positions held by user
    /// @param userAddress address of user entering into the position
    /// @param qty signed quantity of the trade
    /// @param price agreed price of trade
    function addUserNetPosition(
        UserNetPosition storage userNetPosition,
        address userAddress,
        int qty,
        uint price
    ) private
    {
        uint neededCollateral = MathLib.calculateNeededCollateral(
            MKT_CONTRACT.PRICE_FLOOR(),
            MKT_CONTRACT.PRICE_CAP(),
            MKT_CONTRACT.QTY_DECIMAL_PLACES(),
            qty,
            price
        );
        commitCollateralToPool(userAddress, neededCollateral);
        userNetPosition.positions.push(Position(price, qty));   // append array with new position
    }

    /// @dev reduces net position correctly allocating collateral back to user
    /// @param userNetPos storage struct for this users position
    /// @param userAddress address of user who is reducing their pos
    /// @param qty signed quantity of the qty to reduce this users position by
    /// @param price transacted price
    function reduceUserNetPosition(
        UserNetPosition storage userNetPos,
        address userAddress,
        int qty,
        uint price
    ) private
    {
        uint collateralToReturnToUserAccount = 0;
        int qtyToReduce = qty;                      // note: this sign is opposite of our users position
        assert(userNetPos.positions.length != 0);   // sanity check
        while (qtyToReduce != 0) {   //TODO: ensure we dont run out of gas here!
            Position storage position = userNetPos.positions[userNetPos.positions.length - 1];  // get the last pos (LIFO)
            if (position.qty.abs() <= qtyToReduce.abs()) {   // this position is completely consumed!
                collateralToReturnToUserAccount = collateralToReturnToUserAccount.add(
                    MathLib.calculateNeededCollateral(
                        MKT_CONTRACT.PRICE_FLOOR(),
                        MKT_CONTRACT.PRICE_CAP(),
                        MKT_CONTRACT.QTY_DECIMAL_PLACES(),
                        position.qty,
                        price
                    )
                );
                qtyToReduce = qtyToReduce.add(position.qty);
                userNetPos.positions.length--;  // remove this position from our array.
            } else {  // this position stays, just reduce the qty.
                position.qty = position.qty.add(qtyToReduce);
                // pos is opp sign of qty we are reducing here!
                collateralToReturnToUserAccount = collateralToReturnToUserAccount.add(
                    MathLib.calculateNeededCollateral(
                        MKT_CONTRACT.PRICE_FLOOR(),
                        MKT_CONTRACT.PRICE_CAP(),
                        MKT_CONTRACT.QTY_DECIMAL_PLACES(),
                        qtyToReduce * -1,
                        price
                    )
                );
                //qtyToReduce = 0; // completely reduced now!
                break;
            }
        }

        if (collateralToReturnToUserAccount != 0) {  // allocate funds back to user acct.
            withdrawCollateralFromPool(userAddress, collateralToReturnToUserAccount);
        }
    }
}


Comment on why roundLockAmount is needed

pragma solidity ^0.4.17;

import "../ManagerProxyTarget.sol";
import "./IRoundsManager.sol";
import "../bonding/IBondingManager.sol";
import "../token/IMinter.sol";

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

contract RoundsManager is ManagerProxyTarget, IRoundsManager {
    using SafeMath for uint256;

    // Round length in blocks
    uint256 public roundLength;

    // Lock period of a round as a % of round length
    // Transcoders cannot join the transcoder pool or change their rates during the lock period at the end of a round
    // The lock period provides delegators time to review transcoder information without changes
    // # of blocks in the lock period = (roundLength * roundLockAmount) / PERC_DIVISOR
    uint256 public roundLockAmount;

    // Last initialized round. After first round, this is the last round during which initializeRound() was called
    uint256 public lastInitializedRound;

    function RoundsManager(address _controller) public Manager(_controller) {}

    /*
     * @dev Batch set protocol parameters. Only callable by the controller owner
     * @param _roundLength Round length in blocks
     */
    function setParameters(uint256 _roundLength, uint256 _roundLockAmount) external onlyControllerOwner {
        // Must be a valid percentage
        require(_roundLockAmount <= PERC_DIVISOR);

        roundLength = _roundLength;
        roundLockAmount = _roundLockAmount;

        if (lastInitializedRound == 0) {
            lastInitializedRound = currentRound();
        }

        ParameterUpdate("all");
    }

    /*
     * @dev Set round length. Only callable by the controller owner
     * @param _roundLength Round length in blocks
     */
    function setRoundLength(uint256 _roundLength) external onlyControllerOwner {
        roundLength = _roundLength;

        ParameterUpdate("roundLength");
    }

    /*
     * @dev Initialize the current round. Called once at the start of any round
     */
    function initializeRound() external whenSystemNotPaused {
        uint256 currRound = currentRound();

        // Check if already called for the current round
        require(lastInitializedRound < currRound);

        // Set current round as initialized
        lastInitializedRound = currRound;

        bondingManager().setActiveTranscoders();
        minter().setCurrentRewardTokens();

        NewRound(currRound);
    }

    /*
     * @dev Return current round
     */
    function currentRound() public view returns (uint256) {
        return block.number.div(roundLength);
    }

    /*
     * @dev Return start block of current round
     */
    function currentRoundStartBlock() public view returns (uint256) {
        return currentRound().mul(roundLength);
    }

    /*
     * @dev Check if current round is initialized i.e. block.number / roundLength == lastInitializedRound
     */
    function currentRoundInitialized() public view returns (bool) {
        return lastInitializedRound == currentRound();
    }

    /*
     * @dev Check if we are in the lock period of the current round
     */
    function currentRoundLocked() public view returns (bool) {
        return block.number.sub(currentRoundStartBlock()) >= roundLength.sub(roundLength.mul(roundLockAmount).div(PERC_DIVISOR));
    }

    /*
     * @dev Return BondingManager contract (interface)
     */
    function bondingManager() internal view returns (IBondingManager) {
        return IBondingManager(controller.getContract(keccak256("BondingManager")));
    }

    function minter() internal view returns (IMinter) {
        return IMinter(controller.getContract(keccak256("Minter")));
    }
}

pragma solidity ^0.4.17;

import "../ManagerProxyTarget.sol";
import "./IRoundsManager.sol";
import "../bonding/IBondingManager.sol";
import "../token/IMinter.sol";

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

contract RoundsManager is ManagerProxyTarget, IRoundsManager {
    using SafeMath for uint256;

    // Round length in blocks
    uint256 public roundLength;

    // Lock period of a round as a % of round length
    // # of blocks in the lock period = (roundLength * roundLockAmount) / PERC_DIVISOR
    uint256 public roundLockAmount;

    // Last initialized round. After first round, this is the last round during which initializeRound() was called
    uint256 public lastInitializedRound;

    function RoundsManager(address _controller) public Manager(_controller) {}

    /*
     * @dev Batch set protocol parameters. Only callable by the controller owner
     * @param _roundLength Round length in blocks
     */
    function setParameters(uint256 _roundLength, uint256 _roundLockAmount) external onlyControllerOwner {
        // Must be a valid percentage
        require(_roundLockAmount <= PERC_DIVISOR);

        roundLength = _roundLength;
        roundLockAmount = _roundLockAmount;

        if (lastInitializedRound == 0) {
            lastInitializedRound = currentRound();
        }

        ParameterUpdate("all");
    }

    /*
     * @dev Set round length. Only callable by the controller owner
     * @param _roundLength Round length in blocks
     */
    function setRoundLength(uint256 _roundLength) external onlyControllerOwner {
        roundLength = _roundLength;

        ParameterUpdate("roundLength");
    }

    /*
     * @dev Initialize the current round. Called once at the start of any round
     */
    function initializeRound() external whenSystemNotPaused {
        uint256 currRound = currentRound();

        // Check if already called for the current round
        require(lastInitializedRound < currRound);

        // Set current round as initialized
        lastInitializedRound = currRound;

        bondingManager().setActiveTranscoders();
        minter().setCurrentRewardTokens();

        NewRound(currRound);
    }

    /*
     * @dev Return current round
     */
    function currentRound() public view returns (uint256) {
        return block.number.div(roundLength);
    }

    /*
     * @dev Return start block of current round
     */
    function currentRoundStartBlock() public view returns (uint256) {
        return currentRound().mul(roundLength);
    }

    /*
     * @dev Check if current round is initialized i.e. block.number / roundLength == lastInitializedRound
     */
    function currentRoundInitialized() public view returns (bool) {
        return lastInitializedRound == currentRound();
    }

    /*
     * @dev Check if we are in the lock period of the current round
     */
    function currentRoundLocked() public view returns (bool) {
        return block.number.sub(currentRoundStartBlock()) >= roundLength.sub(roundLength.mul(roundLockAmount).div(PERC_DIVISOR));
    }

    /*
     * @dev Return BondingManager contract (interface)
     */
    function bondingManager() internal view returns (IBondingManager) {
        return IBondingManager(controller.getContract(keccak256("BondingManager")));
    }

    function minter() internal view returns (IMinter) {
        return IMinter(controller.getContract(keccak256("Minter")));
    }
}


Remove unused code

Remove unused code

pragma solidity ^0.4.15;

import 'zeppelin-solidity/contracts/math/SafeMath.sol';
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'zeppelin-solidity/contracts/crowdsale/Crowdsale.sol';

/**
 * @title MultiCappedCrowdsale
 * @dev Extension of Crowsdale with a soft cap and a hard cap.
 * after finishing. By default, it will end token minting.
 */
contract MultiCappedCrowdsale is Crowdsale, Ownable {
  using SafeMath for uint256;

  uint256 public softCap;
  uint256 public hardCap = 0;
  bytes32 public hardCapHash;
  uint256 public hardCapTime = 0;
  uint256 public endBuffer;
  event NotFinalized(bytes32 _a, bytes32 _b);

  function MultiCappedCrowdsale(uint256 _softCap, bytes32 _hardCapHash, uint256 _endBuffer) {
    require(_softCap > 0);
    softCap = _softCap;
    hardCapHash = _hardCapHash;
    endBuffer = _endBuffer;
  }

  //
  //  Soft cap logic
  //

  // overriding Crowdsale#validPurchase to add extra cap logic
  // @return true if investors can buy at the moment
  function validPurchase() internal constant returns (bool) {
    if (hardCap > 0) {
      checkHardCap(weiRaised.add(msg.value));
    }
    return super.validPurchase();
  }

  //
  //  Hard cap logic
  //

  function hashHardCap(uint256 _hardCap, uint256 _key) internal constant returns (bytes32) {
    return keccak256(_hardCap, _key);
  }

  function setHardCap(uint256 _hardCap, uint256 _key) external onlyOwner {
    require(hardCap==0);
    if (hardCapHash != hashHardCap(_hardCap, _key)) {
      NotFinalized(hashHardCap(_hardCap, _key), hardCapHash);
      return;
    }
    hardCap = _hardCap;
    checkHardCap(weiRaised);
  }

  function checkHardCap(uint256 totalRaised) internal {
    if (hardCapTime == 0 && totalRaised > hardCap) {
      hardCapTime = block.timestamp;
      endTime = block.timestamp+endBuffer;
    }
  }

}

pragma solidity ^0.4.15;

import 'zeppelin-solidity/contracts/math/SafeMath.sol';
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'zeppelin-solidity/contracts/crowdsale/Crowdsale.sol';

/**
 * @title MultiCappedCrowdsale
 * @dev Extension of Crowsdale with a soft cap and a hard cap.
 * after finishing. By default, it will end token minting.
 */
contract MultiCappedCrowdsale is Crowdsale, Ownable {
  using SafeMath for uint256;

  uint256 public softCap;
  uint256 public hardCap = 0;
  bytes32 public hardCapHash;
  uint256 public hardCapTime = 0;
  uint256 public endBuffer;
  event NotFinalized(bytes32 _a, bytes32 _b);

  function MultiCappedCrowdsale(uint256 _softCap, bytes32 _hardCapHash, uint256 _endBuffer) {
    require(_softCap > 0);
    softCap = _softCap;
    hardCapHash = _hardCapHash;
    endBuffer = _endBuffer;
  }

  //
  //  Soft cap logic
  //

  // overriding Crowdsale#validPurchase to add extra cap logic
  // @return true if investors can buy at the moment
  function validPurchase() internal constant returns (bool) {
    if (hardCap > 0) {
      checkHardCap(weiRaised.add(msg.value));
    }
    return super.validPurchase();
  }

  //
  //  Hard cap logic
  //

  // should be called after crowdsale ends, to do
  // some extra finalization work
  function bytes32ToString (bytes32 data) private returns (string) {
    bytes memory bytesString = new bytes(32);
    for (uint j=0; j<32; j++) {
        byte char = byte(bytes32(uint(data) * 2 ** (8 * j)));
        if (char != 0) {
            bytesString[j] = char;
        }
    }
    return string(bytesString);
  }

  function hashHardCap(uint256 _hardCap) internal constant returns (bytes32) {
    return sha256(bytes32ToString(bytes32(_hardCap)));
  }

 function hashHardCap(uint256 _hardCap, uint256 _key) internal constant returns (bytes32) {
    return keccak256(_hardCap, _key);
  }

  function setHardCap(uint256 _hardCap, uint256 _key) external onlyOwner {
    require(hardCap==0);
    if (hardCapHash != hashHardCap(_hardCap, _key)) {
      NotFinalized(hashHardCap(_hardCap, _key), hardCapHash);
      return;
    }
    hardCap = _hardCap;
    checkHardCap(weiRaised);
  }

  function checkHardCap(uint256 totalRaised) internal {
    if (hardCapTime == 0 && totalRaised > hardCap) {
      hardCapTime = block.timestamp;
      endTime = block.timestamp+endBuffer;
    }
  }

}