Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dispute modifications #60

Merged
merged 33 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bef1f35
adjust coverage cmd
Spablob Apr 11, 2024
0cdb6aa
add decimals and fix variable name warnings
Spablob Apr 11, 2024
d893bda
name fix
Spablob Apr 11, 2024
b3314e2
format fix
Spablob Apr 11, 2024
8cb686a
eliminate DataUniqueness
Spablob Apr 11, 2024
6dd054e
eliminate DataUniqueness 2
Spablob Apr 11, 2024
9edc3f0
naming fixes and add zero var restrictions
Spablob Apr 11, 2024
7ebc219
add new tagDerivativeIfParentInfringed function
Spablob Apr 11, 2024
21fc94c
format fix
Spablob Apr 11, 2024
091b199
add onResolveDispute hook for future-proofing
Spablob Apr 11, 2024
afad72a
remove InitParams from royalty tests
Spablob Apr 11, 2024
3528985
format fix
Spablob Apr 11, 2024
96f6f17
increase test coverage RoyaltyModule
Spablob Apr 11, 2024
460c381
increase test coverage ArbitrationPolicySP
Spablob Apr 11, 2024
5c7922a
add disableInitializers() to ArbitrationPolicySP
Spablob Apr 11, 2024
6b89756
increase test coverage in IpRoyaltyVault
Spablob Apr 11, 2024
7487a09
format fix
Spablob Apr 11, 2024
450edf1
delete e2e
Spablob Apr 12, 2024
3c6de20
fix(registry): Abstract for IPAccountRegistry (#56)
jdubpark Apr 11, 2024
df15b09
Unit Tests and Enhancements for Licensing Components (#64)
kingster-will Apr 12, 2024
e88c07c
Feat/add reusable forge code coverage (#72)
AndyBoWu Apr 12, 2024
28d94d5
eliminate DataUniqueness
Spablob Apr 11, 2024
bb51fdb
add new tagDerivativeIfParentInfringed function
Spablob Apr 11, 2024
94b3c64
format fix
Spablob Apr 11, 2024
0f9f98a
rebase: eliminate data uniqueness
Spablob Apr 12, 2024
d0851e4
Merge branch 'main' into dispute-adjustments
Spablob Apr 12, 2024
929e14f
fix rebase duplication
Spablob Apr 12, 2024
1520309
revert royalty related fixes
Spablob Apr 12, 2024
f8e56d3
format fix
Spablob Apr 12, 2024
93902d5
Merge branch 'storyprotocol:main' into dispute-adjustments
Spablob Apr 12, 2024
6581549
add business logic comments
Spablob Apr 12, 2024
3f73ef2
add restriction clarifications
Spablob Apr 12, 2024
b4a5bc6
fix comment size
Spablob Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ format:
coverage:
mkdir -p coverage
forge coverage --report lcov
lcov --remove lcov.info -o coverage/lcov.info 'test/*' 'script/*' --rc branch_coverage=1
Spablob marked this conversation as resolved.
Show resolved Hide resolved
genhtml coverage/lcov.info -o coverage --rc branch_coverage=1 --ignore-errors category
lcov --remove lcov.info -o coverage/lcov.info 'test/*' 'script/*' --rc lcov_branch_coverage=1
genhtml coverage/lcov.info -o coverage --rc lcov_branch_coverage=1

abi:
rm -rf abi
Expand Down
35 changes: 31 additions & 4 deletions contracts/interfaces/modules/dispute/IDisputeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ interface IDisputeModule {
/// @param linkToDisputeEvidence The link of the dispute evidence
/// @param targetTag The target tag of the dispute
/// @param currentTag The current tag of the dispute
/// @param parentDisputeId The parent dispute id
struct Dispute {
address targetIpId;
address disputeInitiator;
address arbitrationPolicy;
bytes32 linkToDisputeEvidence;
bytes32 targetTag;
bytes32 currentTag;
uint256 parentDisputeId;
}

/// @notice Event emitted when a dispute tag whitelist status is updated
Expand Down Expand Up @@ -73,6 +75,18 @@ interface IDisputeModule {
/// @param data Custom data adjusted to each policy
event DisputeCancelled(uint256 disputeId, bytes data);

/// @notice Event emitted when a derivative is tagged on a parent infringement
/// @param parentIpId The parent ipId which infringed
/// @param derivativeIpId The derivative ipId which was tagged
/// @param parentDisputeId The parent dispute id in which infringement was found
/// @param tag The tag of the dispute applied to the derivative
event DerivativeTaggedOnParentInfringement(
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId,
bytes32 tag
);

/// @notice Event emitted when a dispute is resolved
/// @param disputeId The dispute id
event DisputeResolved(uint256 disputeId);
Expand All @@ -94,6 +108,7 @@ interface IDisputeModule {
/// @return linkToDisputeEvidence The link of the dispute summary
/// @return targetTag The target tag of the dispute
/// @return currentTag The current tag of the dispute
/// @return parentDisputeId The parent dispute id
function disputes(
uint256 disputeId
)
Expand All @@ -105,7 +120,8 @@ interface IDisputeModule {
address arbitrationPolicy,
bytes32 linkToDisputeEvidence,
bytes32 targetTag,
bytes32 currentTag
bytes32 currentTag,
uint256 parentDisputeId
);

/// @notice Indicates if a dispute tag is whitelisted
Expand Down Expand Up @@ -161,7 +177,7 @@ interface IDisputeModule {
/// @param targetIpId The ipId that is the target of the dispute
/// @param linkToDisputeEvidence The link of the dispute evidence
/// @param targetTag The target tag of the dispute
/// @param data The data to initialize the policy
/// @param data The data to raise a dispute
/// @return disputeId The id of the newly raised dispute
function raiseDispute(
address targetIpId,
Expand All @@ -181,9 +197,20 @@ interface IDisputeModule {
/// @param data The data to cancel the dispute
function cancelDispute(uint256 disputeId, bytes calldata data) external;

/// @notice Tags a derivative if a parent has been tagged with an infringement tag
/// @param parentIpId The infringing parent ipId
/// @param derivativeIpId The derivative ipId
/// @param parentDisputeId The dispute id that tagged the parent ipId as infringing
function tagDerivativeIfParentInfringed(
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId
) external;

/// @notice Resolves a dispute after it has been judged
/// @param disputeId The dispute id
function resolveDispute(uint256 disputeId) external;
/// @param disputeId The dispute
/// @param data The data to resolve the dispute
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved
function resolveDispute(uint256 disputeId, bytes calldata data) external;
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved

/// @notice Returns true if the ipId is tagged with any tag (meaning at least one dispute went through)
/// @param ipId The ipId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ interface IArbitrationPolicy {
/// @param data The arbitrary data used to cancel the dispute
function onDisputeCancel(address caller, uint256 disputeId, bytes calldata data) external;

/// @notice Executes custom logic on resolving dispute
/// @dev Enforced to be only callable by the DisputeModule
/// @param caller Address of the caller
/// @param disputeId The dispute id
/// @param data The arbitrary data used to resolve the dispute
function onResolveDispute(address caller, uint256 disputeId, bytes calldata data) external;

/// @notice Allows governance address to withdraw
/// @dev Enforced to be only callable by the governance protocol admin.
function governanceWithdraw() external;
Expand Down
6 changes: 6 additions & 0 deletions contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ interface ILicenseRegistry {
/// @return parentIpId The address of the parent IP.
function getParentIp(address childIpId, uint256 index) external view returns (address parentIpId);

/// @notice Checks if an IP is a parent IP.
/// @param parentIpId The address of the parent IP.
/// @param childIpId The address of the child IP.
/// @return Whether the IP is a parent IP.
function isParentIp(address parentIpId, address childIpId) external view returns (bool);

/// @notice Gets the count of parent IPs.
/// @param childIpId The address of the childIP.
/// @return The count o parent IPs.
Expand Down
10 changes: 5 additions & 5 deletions contracts/lib/ArrayUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ pragma solidity 0.8.23;
/// @notice Library for address array operations
library ArrayUtils {
/// @notice Finds the index of the first occurrence of the given element.
/// @param _array The input array to search
/// @param _element The value to find
/// @param array The input array to search
/// @param element The value to find
/// @return Returns (index and isIn) for the first occurrence starting from index 0
function indexOf(address[] memory _array, address _element) internal pure returns (uint32, bool) {
for (uint32 i = 0; i < _array.length; i++) {
if (_array[i] == _element) return (i, true);
function indexOf(address[] memory array, address element) internal pure returns (uint32, bool) {
for (uint32 i = 0; i < array.length; i++) {
if (array[i] == element) return (i, true);
}
return (0, false);
}
Expand Down
12 changes: 11 additions & 1 deletion contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,19 @@ library Errors {
error DisputeModule__NotInDisputeState();
error DisputeModule__NotAbleToResolve();
error DisputeModule__NotRegisteredIpId();
error DisputeModule__ParentIpIdMismatch();
error DisputeModule__ParentNotTagged();
error DisputeModule__NotDerivative();
error DisputeModule__ParentDisputeNotResolved();
error DisputeModule__ZeroLicenseRegistry();
error DisputeModule__ZeroAssetRegistry();
error DisputeModule__ZeroController();
error DisputeModule__ZeroAccessManager();

error ArbitrationPolicySP__ZeroDisputeModule();
error ArbitrationPolicySP__ZeroPaymentToken();
error ArbitrationPolicySP__NotDisputeModule();
error ArbitrationPolicySP__ZeroAccessManager();

////////////////////////////////////////////////////////////////////////////
// Royalty Module //
Expand All @@ -254,6 +263,7 @@ library Errors {
error RoyaltyModule__NoParentsOnLinking();
error RoyaltyModule__ZeroDisputeModule();
error RoyaltyModule__IpIsTagged();
error RoyaltyModule__ZeroAccessManager();

error RoyaltyPolicyLAP__ZeroRoyaltyModule();
error RoyaltyPolicyLAP__ZeroLiquidSplitFactory();
Expand All @@ -270,8 +280,8 @@ library Errors {
error RoyaltyPolicyLAP__UnlinkableToParents();
error RoyaltyPolicyLAP__LastPositionNotAbleToMintLicense();
error RoyaltyPolicyLAP__ZeroIpRoyaltyVaultBeacon();
error RoyaltyPolicyLAP__ZeroAccessManager();

error IpRoyaltyVault__ZeroIpId();
error IpRoyaltyVault__ZeroRoyaltyPolicyLAP();
error IpRoyaltyVault__NotRoyaltyPolicyLAP();
error IpRoyaltyVault__SnapshotIntervalTooShort();
Expand Down
106 changes: 92 additions & 14 deletions contracts/modules/dispute/DisputeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
// solhint-disable-next-line max-line-length
import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";

import { DISPUTE_MODULE_KEY } from "../../lib/modules/Module.sol";
import { BaseModule } from "../../modules/BaseModule.sol";
import { AccessControlled } from "../../access/AccessControlled.sol";
import { IIPAssetRegistry } from "../../interfaces/registries/IIPAssetRegistry.sol";
import { ILicenseRegistry } from "../../interfaces/registries/ILicenseRegistry.sol";
import { IDisputeModule } from "../../interfaces/modules/dispute/IDisputeModule.sol";
import { IArbitrationPolicy } from "../../interfaces/modules/dispute/policies/IArbitrationPolicy.sol";
import { Errors } from "../../lib/Errors.sol";
Expand All @@ -25,7 +27,8 @@ contract DisputeModule is
AccessManagedUpgradeable,
ReentrancyGuardUpgradeable,
AccessControlled,
UUPSUpgradeable
UUPSUpgradeable,
MulticallUpgradeable
{
using EnumerableSet for EnumerableSet.Bytes32Set;

Expand Down Expand Up @@ -64,21 +67,38 @@ contract DisputeModule is
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IIPAssetRegistry public immutable IP_ASSET_REGISTRY;

/// @notice Protocol-wide license registry
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
ILicenseRegistry public immutable LICENSE_REGISTRY;

/// Constructor
/// @param controller The address of the access controller
/// @param assetRegistry The address of the asset registry
/// @param licenseRegistry The address of the license registry
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address controller, address assetRegistry) AccessControlled(controller, assetRegistry) {
constructor(
address controller,
address assetRegistry,
address licenseRegistry
) AccessControlled(controller, assetRegistry) {
if (licenseRegistry == address(0)) revert Errors.DisputeModule__ZeroLicenseRegistry();
if (assetRegistry == address(0)) revert Errors.DisputeModule__ZeroAssetRegistry();
if (controller == address(0)) revert Errors.DisputeModule__ZeroController();

IP_ASSET_REGISTRY = IIPAssetRegistry(assetRegistry);
LICENSE_REGISTRY = ILicenseRegistry(licenseRegistry);
_disableInitializers();
}

/// @notice initializer for this implementation contract
/// @notice Initializer for this implementation contract
/// @param accessManager The address of the protocol admin roles contract
function initialize(address accessManager) external initializer {
if (accessManager == address(0)) revert Errors.DisputeModule__ZeroAccessManager();

__AccessManaged_init(accessManager);
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
__Multicall_init();
}

/// @notice Whitelists a dispute tag
Expand Down Expand Up @@ -178,7 +198,8 @@ contract DisputeModule is
arbitrationPolicy: arbitrationPolicy,
linkToDisputeEvidence: linkToDisputeEvidenceBytes,
targetTag: targetTag,
currentTag: IN_DISPUTE
currentTag: IN_DISPUTE,
parentDisputeId: 0
});

IArbitrationPolicy(arbitrationPolicy).onRaiseDispute(msg.sender, data);
Expand Down Expand Up @@ -239,19 +260,73 @@ contract DisputeModule is
emit DisputeCancelled(disputeId, data);
}

/// @notice Tags a derivative if a parent has been tagged with an infringement tag
/// @param parentIpId The infringing parent ipId
/// @param derivativeIpId The derivative ipId
/// @param parentDisputeId The dispute id that tagged the parent ipId as infringing
function tagDerivativeIfParentInfringed(
jdubpark marked this conversation as resolved.
Show resolved Hide resolved
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId
) external {
DisputeModuleStorage storage $ = _getDisputeModuleStorage();

Dispute memory parentDispute = $.disputes[parentDisputeId];
if (parentDispute.targetIpId != parentIpId) revert Errors.DisputeModule__ParentIpIdMismatch();
if (parentDispute.currentTag == IN_DISPUTE || parentDispute.currentTag == bytes32(0))
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved
revert Errors.DisputeModule__ParentNotTagged();

if (!LICENSE_REGISTRY.isParentIp(parentIpId, derivativeIpId)) revert Errors.DisputeModule__NotDerivative();

address arbitrationPolicy = $.arbitrationPolicies[derivativeIpId];
if (!$.isWhitelistedArbitrationPolicy[arbitrationPolicy]) arbitrationPolicy = $.baseArbitrationPolicy;

uint256 disputeId = ++$.disputeCounter;

$.disputes[disputeId] = Dispute({
targetIpId: derivativeIpId,
disputeInitiator: msg.sender,
arbitrationPolicy: arbitrationPolicy,
linkToDisputeEvidence: "",
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved
targetTag: parentDispute.currentTag,
currentTag: parentDispute.currentTag,
parentDisputeId: parentDisputeId
});

$.successfulDisputesPerIp[derivativeIpId]++;

emit DerivativeTaggedOnParentInfringement(
parentIpId,
derivativeIpId,
parentDisputeId,
parentDispute.currentTag
);
}

/// @notice Resolves a dispute after it has been judged
/// @param disputeId The dispute id
function resolveDispute(uint256 disputeId) external {
/// @param data The data to resolve the dispute
function resolveDispute(uint256 disputeId, bytes calldata data) external {
DisputeModuleStorage storage $ = _getDisputeModuleStorage();
Dispute memory dispute = $.disputes[disputeId];

if (msg.sender != dispute.disputeInitiator) revert Errors.DisputeModule__NotDisputeInitiator();
// there are two types of disputes - those that are subject to judgment and those that are not
// the way to distinguish is by whether dispute.parentDisputeId is 0 or higher than 0
// for the former - only the dispute initiator can resolve
if (dispute.parentDisputeId == 0 && msg.sender != dispute.disputeInitiator)
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved
revert Errors.DisputeModule__NotDisputeInitiator();
// for the latter - resolving is permissionless as long as the parent dispute has been resolved
if (dispute.parentDisputeId > 0 && $.disputes[dispute.parentDisputeId].currentTag != bytes32(0))
revert Errors.DisputeModule__ParentDisputeNotResolved();

if (dispute.currentTag == IN_DISPUTE || dispute.currentTag == bytes32(0))
revert Errors.DisputeModule__NotAbleToResolve();

$.successfulDisputesPerIp[dispute.targetIpId]--;
$.disputes[disputeId].currentTag = bytes32(0);

IArbitrationPolicy(dispute.arbitrationPolicy).onResolveDispute(msg.sender, disputeId, data);

emit DisputeResolved(disputeId);
}

Expand Down Expand Up @@ -283,6 +358,7 @@ contract DisputeModule is
/// @return linkToDisputeEvidence The link of the dispute summary
/// @return targetTag The target tag of the dispute
/// @return currentTag The current tag of the dispute
/// @return parentDisputeId The parent dispute id
function disputes(
uint256 disputeId
)
Expand All @@ -294,17 +370,19 @@ contract DisputeModule is
address arbitrationPolicy,
bytes32 linkToDisputeEvidence,
bytes32 targetTag,
bytes32 currentTag
bytes32 currentTag,
uint256 parentDisputeId
)
{
DisputeModuleStorage storage $ = _getDisputeModuleStorage();
Dispute memory dispute = _getDisputeModuleStorage().disputes[disputeId];
return (
$.disputes[disputeId].targetIpId,
$.disputes[disputeId].disputeInitiator,
$.disputes[disputeId].arbitrationPolicy,
$.disputes[disputeId].linkToDisputeEvidence,
$.disputes[disputeId].targetTag,
$.disputes[disputeId].currentTag
dispute.targetIpId,
dispute.disputeInitiator,
dispute.arbitrationPolicy,
dispute.linkToDisputeEvidence,
dispute.targetTag,
dispute.currentTag,
dispute.parentDisputeId
);
}

Expand Down
Loading
Loading