From 33a195ab07ab6cf2894d13da9ec1372a5f1d2721 Mon Sep 17 00:00:00 2001 From: Raul Date: Thu, 16 Nov 2023 05:13:53 -0300 Subject: [PATCH] wip testing --- contracts/IPAssetRegistry.sol | 5 +- contracts/StoryProtocol.sol | 54 +++++++++++---- contracts/lib/Errors.sol | 3 + contracts/lib/modules/Licensing.sol | 1 + .../modules/licensing/LicensingModule.sol | 65 ++++++++++++++----- .../modules/licensing/BaseLicensingTest.sol | 12 +--- .../licensing/LicenseCreatorModule.Terms.sol | 35 +++++++++- 7 files changed, 132 insertions(+), 43 deletions(-) diff --git a/contracts/IPAssetRegistry.sol b/contracts/IPAssetRegistry.sol index 199df923..898bbe12 100644 --- a/contracts/IPAssetRegistry.sol +++ b/contracts/IPAssetRegistry.sol @@ -72,12 +72,13 @@ contract IPAssetRegistry is IIPAssetRegistry { } // Crate a new IP asset with the provided IP attributes. - ipAssetId = totalSupply++; + ipAssetId = ++totalSupply; uint64 registrationDate = uint64(block.timestamp); _ipAssets[ipAssetId] = IPA({ name: name_, ipAssetType: ipAssetType_, - status: 0, // TODO(ramarti): Define status types. + // For now, let's assume 0 == unset, 1 is OK. TODO: Add status enum and synch with License status + status: 1, registrant: registrant_, ipOrg: ipOrg_, hash: hash_, diff --git a/contracts/StoryProtocol.sol b/contracts/StoryProtocol.sol index 00275623..2d7cb79d 100644 --- a/contracts/StoryProtocol.sol +++ b/contracts/StoryProtocol.sol @@ -11,6 +11,7 @@ import { LibRelationship } from "contracts/lib/modules/LibRelationship.sol"; import { Registration } from "contracts/lib/modules/Registration.sol"; import { ModuleRegistryKeys } from "contracts/lib/modules/ModuleRegistryKeys.sol"; import { Licensing } from "contracts/lib/modules/Licensing.sol"; +import "forge-std/console2.sol"; contract StoryProtocol { @@ -223,22 +224,24 @@ contract StoryProtocol { bytes[] calldata preHooksData_, bytes[] calldata postHooksData_ ) external returns (uint256) { + console2.log("createLicenseNft"); bytes memory params = abi.encode( params_, Licensing.LicenseeType.LNFTHolder, abi.encode(licensee_) ); - return abi.decode( - MODULE_REGISTRY.execute( - IIPOrg(ipOrg_), - msg.sender, - ModuleRegistryKeys.LICENSING_MODULE, - params, - preHooksData_, - postHooksData_ + bytes memory result = MODULE_REGISTRY.execute( + IIPOrg(ipOrg_), + msg.sender, + ModuleRegistryKeys.LICENSING_MODULE, + abi.encode( + Licensing.CREATE_LICENSE, + params ), - (uint256) + preHooksData_, + postHooksData_ ); + return abi.decode(result, (uint256)); } /// Creates a License bound to a certain IPA. It's not an NFT, the licensee will be the owner of the IPA. @@ -255,26 +258,31 @@ contract StoryProtocol { bytes[] calldata preHooksData_, bytes[] calldata postHooksData_ ) external returns (uint256) { + console2.log("createIpaBoundLicense"); bytes memory params = abi.encode( params_, Licensing.LicenseeType.BoundToIpa, abi.encode(ipaId_) ); - return abi.decode( MODULE_REGISTRY.execute( + bytes memory result = MODULE_REGISTRY.execute( IIPOrg(ipOrg_), msg.sender, ModuleRegistryKeys.LICENSING_MODULE, - params, + abi.encode( + Licensing.CREATE_LICENSE, + params + ), preHooksData_, postHooksData_ - ), - (uint256)); + ); + return abi.decode(result, (uint256)); } function activateLicense( address ipOrg_, uint256 licenseId_ ) external { + console2.log("activateLicense"); MODULE_REGISTRY.execute( IIPOrg(ipOrg_), msg.sender, @@ -287,4 +295,24 @@ contract StoryProtocol { new bytes[](0) ); } + + function boundLnftToIpa( + address ipOrg_, + uint256 licenseId_, + uint256 ipaId_ + ) external { + console2.log("boundLnftToIpa"); + MODULE_REGISTRY.execute( + IIPOrg(ipOrg_), + msg.sender, + ModuleRegistryKeys.LICENSING_MODULE, + abi.encode( + Licensing.BOUND_LNFT_TO_IPA, + abi.encode(licenseId_, ipaId_) + ), + new bytes[](0), + new bytes[](0) + ); + } + } diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index d5fd0150..a70e3f80 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -272,6 +272,8 @@ library Errors { error LicensingModule_LicenseAlreadyActivated(); error LicensingModule_CallerNotLicensor(); error LicensingModule_ParentLicenseNotActive(); + error LicensingModule_InvalidIpa(); + error LicensingModule_CallerNotLicenseOwner(); //////////////////////////////////////////////////////////////////////////// // LicenseRegistry // @@ -289,6 +291,7 @@ library Errors { error LicenseRegistry_CallerNotRevoker(); error LicenseRegistry_LicenseNotPending(); error LicenseRegistry_InvalidLicenseStatus(); + //////////////////////////////////////////////////////////////////////////// // RegistrationModule // diff --git a/contracts/lib/modules/Licensing.sol b/contracts/lib/modules/Licensing.sol index ff0c17c8..b3505118 100644 --- a/contracts/lib/modules/Licensing.sol +++ b/contracts/lib/modules/Licensing.sol @@ -131,4 +131,5 @@ library Licensing { bytes32 constant LICENSING_FRAMEWORK_CONFIG = keccak256("LICENSING_FRAMEWORK_CONFIG"); bytes32 constant CREATE_LICENSE = keccak256("CREATE_LICENSE"); bytes32 constant ACTIVATE_LICENSE = keccak256("ACTIVATE_LICENSE"); + bytes32 constant BOUND_LNFT_TO_IPA = keccak256("BOUND_LNFT_TO_IPA"); } \ No newline at end of file diff --git a/contracts/modules/licensing/LicensingModule.sol b/contracts/modules/licensing/LicensingModule.sol index 55dedbf5..ce5aa70f 100644 --- a/contracts/modules/licensing/LicensingModule.sol +++ b/contracts/modules/licensing/LicensingModule.sol @@ -15,6 +15,8 @@ import { IPAsset } from "contracts/lib/IPAsset.sol"; import { TermIds, TermCategories } from "contracts/lib/modules/ProtocolLicensingTerms.sol"; import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; +import "forge-std/console2.sol"; + /// @title License Creator module /// @notice Story Protocol module that: /// - Enables each IP Org to select a collection of terms from the TermsRepository to form @@ -141,7 +143,7 @@ contract LicensingModule is BaseModule, TermsRepository{ (bytes32 action, bytes memory params) = abi.decode(params_, (bytes32, bytes)); if (action == Licensing.CREATE_LICENSE) { _verifyCreateLicense(ipOrg_, caller_, params); - } if (action == Licensing.ACTIVATE_LICENSE) { + } else if (action == Licensing.ACTIVATE_LICENSE) { _verifyActivateLicense(ipOrg_, caller_, params); } else { revert Errors.LicensingModule_InvalidAction(); @@ -182,20 +184,28 @@ contract LicensingModule is BaseModule, TermsRepository{ revert Errors.LicensingModule_ParentLicenseNotActive(); } // ------ Derivative license checks: Terms ------ - FixedSet.ShortStringSet storage termIds = _getIpOrgTermIds(lParams.isCommercial, address(ipOrg_)); - bytes[] storage termData = _getIpOrgTermData(lParams.isCommercial, address(ipOrg_)); - - // Share Alike ---- - uint256 nftShareAlikeIndex = termIds.indexOf(TermIds.NFT_SHARE_ALIKE.toShortString()); - // If there is no NFT_SHARE_ALIKE term, or if it is false then we cannot have - // a derivative license unless caller owns the parent license - if (nftShareAlikeIndex == FixedSet.INDEX_NOT_FOUND || - !abi.decode(termData[nftShareAlikeIndex], (bool)) - ) { - address parentLicensor = LICENSE_REGISTRY.getLicensor(lParams.parentLicenseId); - if (parentLicensor != caller_) { - revert Errors.LicensingModule_ShareAlikeDisabled(); - } + if (licenseeType == Licensing.LicenseeType.BoundToIpa) { + _verifyShareAlike(lParams, caller_, ipOrg_); + } + + } + } + + function _verifyShareAlike(Licensing.LicenseCreation memory lParams, address caller_, IIPOrg ipOrg_) private { + FixedSet.ShortStringSet storage termIds = _getIpOrgTermIds(lParams.isCommercial, address(ipOrg_)); + bytes[] storage termData = _getIpOrgTermData(lParams.isCommercial, address(ipOrg_)); + + // Share Alike ---- + uint256 nftShareAlikeIndex = termIds.indexOf(TermIds.NFT_SHARE_ALIKE.toShortString()); + // If there is no NFT_SHARE_ALIKE term, or if it is false then we cannot have + // a derivative license unless caller owns the parent license + if ( + nftShareAlikeIndex == FixedSet.INDEX_NOT_FOUND || + !abi.decode(termData[nftShareAlikeIndex], (bool)) + ) { + address parentLicensor = LICENSE_REGISTRY.getLicensor(lParams.parentLicenseId); + if (parentLicensor != caller_) { + revert Errors.LicensingModule_ShareAlikeDisabled(); } } } @@ -216,6 +226,23 @@ contract LicensingModule is BaseModule, TermsRepository{ } + function _verifyBoundNftToIpa( + IIPOrg ipOrg_, + address caller_, + bytes memory params_ + ) private { + (uint256 licenseId, uint256 ipaId) = abi.decode(params_, (uint256, uint256)); + if (caller_ != LICENSE_REGISTRY.ownerOf(licenseId)) { + revert Errors.LicensingModule_CallerNotLicenseOwner(); + } + if (!LICENSE_REGISTRY.isLicenseActive(licenseId)) { + revert Errors.LicensingModule_ParentLicenseNotActive(); + } + if (IPA_REGISTRY.status(ipaId) == 0) { + revert Errors.LicensingModule_InvalidIpa(); + } + } + /// Module entrypoint to create licenses function _performAction( IIPOrg ipOrg_, @@ -225,8 +252,11 @@ contract LicensingModule is BaseModule, TermsRepository{ (bytes32 action, bytes memory params) = abi.decode(params_, (bytes32, bytes)); if (action == Licensing.CREATE_LICENSE) { return _createLicense(ipOrg_, caller_, params); - } if (action == Licensing.ACTIVATE_LICENSE) { + } else if (action == Licensing.ACTIVATE_LICENSE) { return _activateLicense(ipOrg_, caller_, params); + } else if (action == Licensing.BOUND_LNFT_TO_IPA) { + (uint256 licenseId, uint256 ipaId) = abi.decode(params_, (uint256, uint256)); + LICENSE_REGISTRY.boundLnftToIpa(licenseId, ipaId); } else { revert Errors.LicensingModule_InvalidAction(); } @@ -323,6 +353,9 @@ contract LicensingModule is BaseModule, TermsRepository{ uint256 ipaId, address parentLicenseOwner ) private view returns (address) { + console2.log("ipaId", ipaId); + console2.log("parentLicenseOwner", parentLicenseOwner); + console2.log("IPA_REGISTRY", IPA_REGISTRY.ipAssetOwner(ipaId)); // TODO: Check for Licensor term in terms registry. if (parentLicenseOwner != address(0) || ipaId == 0) { return parentLicenseOwner; diff --git a/test/foundry/modules/licensing/BaseLicensingTest.sol b/test/foundry/modules/licensing/BaseLicensingTest.sol index 646b39d1..2648ed66 100644 --- a/test/foundry/modules/licensing/BaseLicensingTest.sol +++ b/test/foundry/modules/licensing/BaseLicensingTest.sol @@ -129,17 +129,7 @@ contract BaseLicensingTest is BaseTest { nonCommTermData = [bytes(""), bytes("")]; commTermIds = [commTextTermId]; commTermData = [bytes("")]; - (uint256 rootIpaId, uint256 ignored) = spg.registerIPAsset( - address(ipOrg), - Registration.RegisterIPAssetParams({ - owner: ipaOwner, - name: "bob", - ipAssetType: 2, - hash: keccak256("test") - }), - new bytes[](0), - new bytes[](0) - ); + rootIpaId = _createIpAsset(ipaOwner, 2, bytes("")); } function getEmptyFramework() public pure returns (Licensing.FrameworkConfig memory) { diff --git a/test/foundry/modules/licensing/LicenseCreatorModule.Terms.sol b/test/foundry/modules/licensing/LicenseCreatorModule.Terms.sol index a0cced1a..5e132429 100644 --- a/test/foundry/modules/licensing/LicenseCreatorModule.Terms.sol +++ b/test/foundry/modules/licensing/LicenseCreatorModule.Terms.sol @@ -51,6 +51,7 @@ contract LicensingCreatorModuleTermsTest is BaseLicensingTest { new bytes[](0) ); Licensing.License memory license = licenseRegistry.getLicense(lId); + assertTrue(licenseRegistry.isLicenseActive(lId)); assertTerms(license); vm.expectRevert(); licenseRegistry.ownerOf(lId); @@ -91,6 +92,8 @@ contract LicensingCreatorModuleTermsTest is BaseLicensingTest { new bytes[](0) ); Licensing.License memory license = licenseRegistry.getLicense(lId); + assertEq(uint8(license.status), uint8(Licensing.LicenseStatus.Pending)); + assertFalse(licenseRegistry.isLicenseActive(lId)); console.log("lId", lId); console.log("licenseeType", uint8(license.licenseeType)); assertEq(licenseRegistry.ownerOf(lId), ipaOwner); @@ -99,8 +102,38 @@ contract LicensingCreatorModuleTermsTest is BaseLicensingTest { vm.prank(ipaOwner); licenseRegistry.transferFrom(ipaOwner, ipaOwner2, lId); // have other guy activate license - + vm.prank(ipaOwner2); + spg.activateLicense(address(ipOrg), lId); + license = licenseRegistry.getLicense(lId); + assertEq(uint8(license.status), uint8(Licensing.LicenseStatus.Pending)); + assertFalse(licenseRegistry.isLicenseActive(lId)); + // Fail to bound if not active + vm.expectRevert(); + spg.boundLnftToIpa( + address(ipOrg), + lId, + 1 + ); + // Bond if active + vm.prank(license.licensor); + spg.activateLicense( + address(ipOrg), + lId + ); + license = licenseRegistry.getLicense(lId); + assertEq(uint8(license.status), uint8(Licensing.LicenseStatus.Active)); + assertTrue(licenseRegistry.isLicenseActive(lId)); + vm.prank(ipaOwner2); + spg.boundLnftToIpa( + address(ipOrg), + lId, + 1 + ); + license = licenseRegistry.getLicense(lId); + assertEq(uint8(license.licenseeType), uint8(Licensing.LicenseeType.BoundToIpa)); + assertEq(licenseRegistry.ownerOf(lId), address(0)); + assertEq(licenseRegistry.getLicensee(lId), ipaOwner2); } }