Repository: oceanprotocol/ocean.py (language: python)

Issue:
asset.disabled = True when asset is in "unlisted" state (state 5)
**Describe the bug**

I encountered this bug when working on predict-eth judging. [Here's the gh issue](https://github.com/oceanprotocol/predict-eth-judge/issues/1).

The core issue: the asset is in "unlisted" state (state 5) ([see docs](https://docs.oceanprotocol.com/core-concepts/did-ddo#state)). But if it's "unlisted" that doesn't imply that it's disabled. However that's how ocean.py treats it.

Explanation: State 5 is new-ish, and I guess ocean.py wasn't fully updated to handle it yet. No worries, let's fix it!

**To Reproduce - first way**

One way is: like the gh issue above does. It returns ` ocean_lib.agreements.consumable.AssetNotConsumable`. 

But I didn't have insight as to why the asset wasn't consumable.

**To Reproduce - to debug**

This approach facilitates debugging.

First, do step 1 (setup) of [ocean.py developers flow](https://github.com/oceanprotocol/ocean.py/blob/main/READMEs/developers.md).

Then, open a python console and do:
```python
import os

import brownie
from brownie.network import accounts
import hashlib

from ocean_lib.aquarius import Aquarius
from ocean_lib.assets.asset import Asset
from ocean_lib.example_config import ExampleConfig
from ocean_lib.models.datatoken import Datatoken
from ocean_lib.ocean.ocean import Ocean
from ocean_lib.web3_internal.utils import connect_to_network

# from ocean.py/ocean_lib/utils/utilities.py
def create_checksum(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()

# create Ocean instance
connect_to_network("polygon")
config = ExampleConfig.get_config("polygon")
ocean = Ocean(config)

# create alice_wallet
alice_private_key = os.getenv('REMOTE_TEST_PRIVATE_KEY1')
alice_wallet = accounts.add(alice_private_key)
bal = accounts.at(alice_wallet.address).balance()
print(f"alice_wallet.address={alice_wallet.address}. bal={bal/1e18} MATIC")
assert bal > 0, "Alice needs MATIC"

#specify datatoken
dt_addr = "0xdf2857f2f9f1f2a227ca0f00c20190ff7d7a05a0"
#OR: dt_addr = "0x277475e34ce4f787d8c3b1dca98c6407470407a3"

# create asset
dt_addr = brownie.web3.toChecksumAddress(dt_addr)
print(f"dt_addr: {dt_addr}")
dt = Datatoken(ocean.config_dict, dt_addr)

nft_addr = dt.getERC721Address()
nft_addr = brownie.web3.toChecksumAddress(nft_addr)

chainID = 137 #polygon
did = f"did:op:{create_checksum(nft_addr + str(chainID))}"
print(f"did: {did}")

#the following is an unpacking of: asset = ocean.assets.resolve(did)...
aqua = Aquarius.get_instance(ocean.assets._metadata_cache_uri)
query_url = f"{aqua.base_url}/ddo/{did}" # "https://v4.aquarius.oceanprotocol.com/api/aquarius/assets/ddo/did:op:f04ae0cb2bc7d48c0402b79975dcb21703f73b1188379ac890b22d12c012a8a8"
response = aqua.requests_session.get(query_url)
assert response.status_code == 200
response_dict = response.json()
# response_dict["nft"]["state"] = 5. That is: "Asset unlisted." according to docs
asset = Asset.from_dict(response_dict)

assert asset.did == did
assert not asset.is_disabled 

# Alice sends a datatoken to the service, to get access
order_tx_id = ocean.assets.pay_for_access_service(asset, alice_wallet)
```

It will fail on: `assert not asset.is_disabled`.

And if I don't have that line, then it will fail in the very last line, with `ocean_lib.agreements.consumable.AssetNotConsumable: 1`. Where the `1` is error code `ConsumableCodes.ASSET_DISABLED`. If you look at the code, it does a check to `asset.is_disabled`.
asset.disabled = True when asset is in "unlisted" state (state 5)
**Describe the bug**

I encountered this bug when working on predict-eth judging. [Here's the gh issue](https://github.com/oceanprotocol/predict-eth-judge/issues/1).

The core issue: the asset is in "unlisted" state (state 5) ([see docs](https://docs.oceanprotocol.com/core-concepts/did-ddo#state)). But if it's "unlisted" that doesn't imply that it's disabled. However that's how ocean.py treats it.

Explanation: State 5 is new-ish, and I guess ocean.py wasn't fully updated to handle it yet. No worries, let's fix it!

**To Reproduce - first way**

One way is: like the gh issue above does. It returns ` ocean_lib.agreements.consumable.AssetNotConsumable`. 

But I didn't have insight as to why the asset wasn't consumable.

**To Reproduce - to debug**

This approach facilitates debugging.

First, do step 1 (setup) of [ocean.py developers flow](https://github.com/oceanprotocol/ocean.py/blob/main/READMEs/developers.md).

Then, open a python console and do:
```python
import os

import brownie
from brownie.network import accounts
import hashlib

from ocean_lib.aquarius import Aquarius
from ocean_lib.assets.asset import Asset
from ocean_lib.example_config import ExampleConfig
from ocean_lib.models.datatoken import Datatoken
from ocean_lib.ocean.ocean import Ocean
from ocean_lib.web3_internal.utils import connect_to_network

# from ocean.py/ocean_lib/utils/utilities.py
def create_checksum(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()

# create Ocean instance
connect_to_network("polygon")
config = ExampleConfig.get_config("polygon")
ocean = Ocean(config)

# create alice_wallet
alice_private_key = os.getenv('REMOTE_TEST_PRIVATE_KEY1')
alice_wallet = accounts.add(alice_private_key)
bal = accounts.at(alice_wallet.address).balance()
print(f"alice_wallet.address={alice_wallet.address}. bal={bal/1e18} MATIC")
assert bal > 0, "Alice needs MATIC"

#specify datatoken
dt_addr = "0xdf2857f2f9f1f2a227ca0f00c20190ff7d7a05a0"
#OR: dt_addr = "0x277475e34ce4f787d8c3b1dca98c6407470407a3"

# create asset
dt_addr = brownie.web3.toChecksumAddress(dt_addr)
print(f"dt_addr: {dt_addr}")
dt = Datatoken(ocean.config_dict, dt_addr)

nft_addr = dt.getERC721Address()
nft_addr = brownie.web3.toChecksumAddress(nft_addr)

chainID = 137 #polygon
did = f"did:op:{create_checksum(nft_addr + str(chainID))}"
print(f"did: {did}")

#the following is an unpacking of: asset = ocean.assets.resolve(did)...
aqua = Aquarius.get_instance(ocean.assets._metadata_cache_uri)
query_url = f"{aqua.base_url}/ddo/{did}" # "https://v4.aquarius.oceanprotocol.com/api/aquarius/assets/ddo/did:op:f04ae0cb2bc7d48c0402b79975dcb21703f73b1188379ac890b22d12c012a8a8"
response = aqua.requests_session.get(query_url)
assert response.status_code == 200
response_dict = response.json()
# response_dict["nft"]["state"] = 5. That is: "Asset unlisted." according to docs
asset = Asset.from_dict(response_dict)

assert asset.did == did
assert not asset.is_disabled 

# Alice sends a datatoken to the service, to get access
order_tx_id = ocean.assets.pay_for_access_service(asset, alice_wallet)
```

It will fail on: `assert not asset.is_disabled`.

And if I don't have that line, then it will fail in the very last line, with `ocean_lib.agreements.consumable.AssetNotConsumable: 1`. Where the `1` is error code `ConsumableCodes.ASSET_DISABLED`. If you look at the code, it does a check to `asset.is_disabled`.


Discussion / hints:
The fix: in asset.py, change to:

```python
    def is_disabled(self) -> bool:
        return not self.metadata or (self.nft and self.nft["state"] not in [0,5])
```

(Add the 5)

I will submit this, with PR etc.
The fix: in asset.py, change to:

```python
    def is_disabled(self) -> bool:
        return not self.metadata or (self.nft and self.nft["state"] not in [0,5])
```

(Add the 5)

I will submit this, with PR etc.

Fix patch:
diff --git a/ocean_lib/assets/asset.py b/ocean_lib/assets/asset.py
index c993821e1..0d81d5be9 100644
--- a/ocean_lib/assets/asset.py
+++ b/ocean_lib/assets/asset.py
@@ -207,7 +207,7 @@ def generate_trusted_algorithms(self) -> dict:
 
     @property
     def is_disabled(self) -> bool:
-        return not self.metadata or (self.nft and self.nft["state"] != 0)
+        return not self.metadata or (self.nft and self.nft["state"] not in [0, 5])
 
 
 class UnavailableAsset(Asset):


Test patch:
diff --git a/ocean_lib/assets/test/test_asset.py b/ocean_lib/assets/test/test_asset.py
index e2233a433..c61db0bf2 100644
--- a/ocean_lib/assets/test/test_asset.py
+++ b/ocean_lib/assets/test/test_asset.py
@@ -280,3 +280,19 @@ def test_credential_simplification():
     assert (
         simplify_credential_to_address({"type": "address", "value": "0x11"}) == "0x11"
     )
+
+
+@pytest.mark.unit
+def test_is_disabled():
+
+    ddo_dict = get_sample_ddo()
+
+    for state in range(6):
+        ddo_dict["nft"]["state"] = state
+        ddo = Asset.from_dict(ddo_dict)
+
+        # adhere to https://docs.oceanprotocol.com/core-concepts/did-ddo#state
+        if state in [0, 5]:
+            assert not ddo.is_disabled
+        else:
+            assert ddo.is_disabled