Skip to content

Commit

Permalink
Remove brownie (#1458)
Browse files Browse the repository at this point in the history
Closes #1460 "Replace brownie with web3.py"
Closes #1451 (build tests failing due to dependency issues)
  • Loading branch information
calina-c authored Aug 28, 2023
1 parent bc34edf commit 303a9a0
Show file tree
Hide file tree
Showing 70 changed files with 960 additions and 687 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/libcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
path: 'barge'
- name: Run Barge
working-directory: ${{ github.workspace }}/barge
env:
GANACHE_HARDFORK: london
run: |
bash -x start_ocean.sh --no-dashboard 2>&1 --with-provider2 > start_ocean.log &
for i in $(seq 1 108); do
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ jobs:
path: 'barge'
- name: Run Barge
working-directory: ${{ github.workspace }}/barge
env:
GANACHE_HARDFORK: london
run: |
bash -x start_ocean.sh --no-dashboard 2>&1 --with-provider2 --with-thegraph --skip-subgraph-deploy > start_ocean.log &
- name: Install dependencies
working-directory: ${{ github.workspace }}
run: |
python -m pip install --upgrade pip
pip install -r requirements_dev.txt
brownie bake github-actions
sed -i 's#https://polygon-mainnet.infura.io/v3/$WEB3_INFURA_PROJECT_ID#https://polygon-rpc.com/#g; s#https://polygon-mumbai.infura.io/v3/$WEB3_INFURA_PROJECT_ID#https://rpc-mumbai.maticvigil.com#g' $HOME/.brownie/network-config.yaml
- name: Wait for contracts deployment
working-directory: ${{ github.workspace }}/barge
run: |
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ With ocean.py, you can:

- **Publish** data services: downloadable files or compute-to-data. Create an ERC721 **data NFT** for each service, and ERC20 **datatoken** for access (1.0 datatokens to access).
- **Sell** datatokens via for a fixed price. Sell data NFTs.
- **Transfer** data NFTs & datatokens to another owner, and **all other ERC721 & ERC20 actions** using [web3.py](https://web3py.readthedocs.io) or [Brownie](https://eth-brownie.readthedocs.io/en/latest/).
- **Transfer** data NFTs & datatokens to another owner, and **all other ERC721 & ERC20 actions** using [web3.py](https://web3py.readthedocs.io).

ocean.py is part of the [Ocean Protocol](https://www.oceanprotocol.com) toolset.

Expand Down Expand Up @@ -64,7 +64,6 @@ Each of the following shows how to publish & consume a particular type of data.
### Learn more
- [Understand config parameters](READMEs/parameters.md) - envvars vs files
- [Learn about off-chain services](READMEs/services.md) - Ocean Provider for data services, Aquarius metadata store
- [Using hardware wallets with ocean.py](READMEs/using-clef.md) - Learn how to use hardware wallets with ocean.py via Brownie and Clef

## 🦑 Development

Expand Down
14 changes: 9 additions & 5 deletions READMEs/custody-light-flow.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<!--
Copyright 2023 Ocean Protocol Foundation
SPDX-License-Identifier: Apache-2.0
-->

# Custody-light flow

Expand Down Expand Up @@ -64,7 +68,7 @@ Bob now has the transaction receipt to prove that he dispensed funds! Time to do
In the same Python console:
```python
# Bob downloads the file. If the connection breaks, Bob can try again
asset_dir = ocean.assets.download_asset(ddo, bob, './', tx.txid)
asset_dir = ocean.assets.download_asset(ddo, bob, './', tx.transactionHash.hex())

import os
file_name = os.path.join(asset_dir, "file0")
Expand All @@ -90,11 +94,11 @@ Steps in this flow:

In the same Python console:
```python
#data info
# data info
name = "Branin dataset"
url = "https://raw.githubusercontent.com/trentmc/branin/main/branin.arff"

#create data asset
# create data asset
from ocean_lib.models.fixed_rate_exchange import ExchangeArguments
from ocean_lib.ocean.util import to_wei

Expand Down Expand Up @@ -143,7 +147,7 @@ Bob now has the transaction receipt to prove that he bought funds from the excha
In the same Python console:
```python
# Bob downloads the file. If the connection breaks, Bob can try again
asset_dir = ocean.assets.download_asset(ddo, bob, './', tx.txid)
asset_dir = ocean.assets.download_asset(ddo, bob, './', tx.transactionHash.hex())

import os
file_name = os.path.join(asset_dir, "file0")
Expand All @@ -154,4 +158,4 @@ Let's check that the file is downloaded. In a new console:
```console
cd my_project/datafile.did:op:0xAf07...
ls branin.arff
```
```
25 changes: 14 additions & 11 deletions READMEs/developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ git clone https://github.com/oceanprotocol/ocean.py
cd ocean.py

# Install OS dependencies
sudo apt-get install -y python3-dev gcc python-pytest
sudo apt-get install -y python3-dev gcc

# Initialize virtual environment and activate it.
# Make sure your Python version inside the venv is >=3.8.
Expand All @@ -49,13 +49,16 @@ pip install -r requirements_dev.txt
In a new console:

```console
#grab repo
# grab repo
git clone https://github.com/oceanprotocol/barge
cd barge

#clean up old containers (to be sure)
# clean up old containers (to be sure)
docker system prune -a --volumes

# for support of type 2 transactions
export GANACHE_HARDFORK=london

# Run barge: start Ganache, Provider, Aquarius; deploy contracts; update ~/.ocean
# The `--with-c2d` option tells barge to include the Compute-to-Data backend
./start_ocean.sh --with-c2d
Expand All @@ -68,17 +71,17 @@ docker system prune -a --volumes
In work console:

```console
#set private keys of two local (ganache) accounts
# set private keys of two local (ganache) accounts
export TEST_PRIVATE_KEY1=0x8467415bb2ba7c91084d932276214b11a3dd9bdb2930fefa194b666dd8020b99
export TEST_PRIVATE_KEY2=0x1d751ded5a32226054cd2e71261039b65afb9ee1c746d055dd699b1150a5befc

#needed to mint fake OCEAN for testing with ganache
# needed to mint fake OCEAN for testing with ganache
export FACTORY_DEPLOYER_PRIVATE_KEY=0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58
```

Some tests run on Mumbai (e.g. test_mumbai.py), which need fake MATIC. So you also need:
```console
#set private keys of two remote accounts
# set private keys of two remote accounts
export REMOTE_TEST_PRIVATE_KEY1=<your remote private key 1>
export REMOTE_TEST_PRIVATE_KEY2=<your remote private key 2>
```
Expand All @@ -89,22 +92,22 @@ These keys aren't public because bots could eat the fake MATIC. You need to gene

In work console:
```console
#run a single test
# run a single test
pytest ocean_lib/models/test/test_data_nft_factory.py::test_start_multiple_order

#run all tests in a file
# run all tests in a file
pytest ocean_lib/models/test/test_data_nft_factory.py

#run all regular tests; see details on pytest markers to select specific suites
# run all regular tests; see details on pytest markers to select specific suites
pytest
```

The README tests are special. Here's how to run them:
```console
#need to auto-generate READMEs first
# need to auto-generate READMEs first
mkcodes --github --output tests/generated-readmes/test_{name}.{ext} READMEs

#then run the tests
# then run the tests
pytest tests/readmes/test_readmes.py
pytest /tests/integration/remote/test_mumbai_readme.py
```
Expand Down
50 changes: 37 additions & 13 deletions READMEs/df.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,39 @@ amt_OCEAN_lock = 10.0

Now, let's lock OCEAN for veOCEAN. In the same Python console:
```python
#simulate passage of time, until next Thursday, the start of DF(X)
from brownie.network import chain
# simulate passage of time, until next Thursday, the start of DF(X)
web3 = ocean.config_dict["web3_instance"]
provider = web3.provider
latest_block = web3.eth.getBlock("latest")

WEEK = 7 * 86400 # seconds in a week
t0 = chain.time()
t1 = t0 // WEEK * WEEK + WEEK #this is a Thursday, because Jan 1 1970 was
t0 = latest_block.timestamp
t1 = t0 // WEEK * WEEK + WEEK # this is a Thursday, because Jan 1 1970 was
t2 = t1 + WEEK
chain.sleep(t1 - t0)
chain.mine()
provider.make_request("evm_increaseTime", [(t1 - t0)])

#we're now at the beginning of the week. So, lock
veOCEAN = ocean.veOCEAN
OCEAN.approve(veOCEAN.address, to_wei(amt_OCEAN_lock), {"from" : alice})
veOCEAN.withdraw({"from": alice}) #withdraw old tokens first
veOCEAN.create_lock(to_wei(amt_OCEAN_lock), t2, {"from": alice})

import math
web3 = ocean.config_dict["web3_instance"]
latest_block = web3.eth.get_block("latest")
veOCEAN.withdraw({
"from": alice,
"gas": latest_block.gasLimit,
"gasPrice": math.ceil(latest_block["baseFeePerGas"] * 1.2),
}) # withdraw old tokens first

latest_block = web3.eth.get_block("latest")
veOCEAN.create_lock(
to_wei(amt_OCEAN_lock),
t2,
{
"from": alice,
"gas": latest_block.gasLimit,
"gasPrice": math.ceil(latest_block["baseFeePerGas"] * 1.2),
})
```


Expand Down Expand Up @@ -83,7 +102,7 @@ DT.approve(exchange.address, to_wei(num_consumes), {"from": alice})
To stake, you allocate veOCEAN to dataset. In the same Python console:
```python
amt_allocate = 100 #total allocation must be <= 10000 (wei)
ocean.ve_allocate.setAllocation(amt_allocate, data_NFT.address, chain.id, {"from": alice})
ocean.ve_allocate.setAllocation(amt_allocate, data_NFT.address, web3.eth.chain_id, {"from": alice})
```

## 5. Fake-consume data
Expand Down Expand Up @@ -119,15 +138,20 @@ In the same Python console:
```python
#simulate passage of time, until next Thursday, which is the start of DF(X+1)
WEEK = 7 * 86400 # seconds in a week
t0 = chain.time()

latest_block = web3.eth.getBlock("latest")
t0 = latest_block.timestamp
t1 = t0 // WEEK * WEEK + WEEK
t2 = t1 + WEEK
chain.sleep(t1 - t0)
chain.mine()
provider.make_request("evm_increaseTime", [(t1 - t0)])

#Rewards can be claimed via code or webapp, at your leisure. Let's do it now.
OCEAN_before = from_wei(OCEAN.balanceOf(alice))
ocean.ve_fee_distributor.claim({"from": alice})
ocean.ve_fee_distributor.claim({
"from": alice,
"gas": latest_block.gasLimit,
"gasPrice": math.ceil(latest_block["baseFeePerGas"] * 1.2),
})
OCEAN_after = from_wei(OCEAN.balanceOf(alice))
print(f"Just claimed {OCEAN_after - OCEAN_before} OCEAN rewards")
```
Expand Down
12 changes: 5 additions & 7 deletions READMEs/gas-strategy-remote.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Ensure that you've already (a) [installed Ocean](install.md), and (b) [set up re

## 2. Define gas strategy

Fees are defined for `polygon-main` & `polygon-test` networks.
Fees are defined for `polygon` & `mumbai` networks.

```python
from ocean_lib.web3_internal.utils import get_gas_fees
Expand All @@ -37,13 +37,11 @@ The gas strategy can be added to any `tx_dict`, and this is just an example of u
#data info
name = "Branin dataset"
url = "https://raw.githubusercontent.com/trentmc/branin/main/branin.arff"
tx_dict = (
{
tx_dict = {
"from": alice,
"priority_fee": priority_fee,
"max_fee": max_fee,
},
)
"maxPriorityFeePerGas": priority_fee,
"maxFeePerGas": max_fee,
}

#create data asset
(data_nft, datatoken, ddo) = ocean.assets.create_url_asset(
Expand Down
12 changes: 1 addition & 11 deletions READMEs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,7 @@ Issue: MacOS "Unsupported Architecture"

Issue: Dependencies and Python 3.11

- ocean.py has two dependencies that don't support Python 3.11 yet. Here's the workaround for each.
- (1) The `eth-brownie` package uses Vyper which doesn't support Python 3.11. The workaround: before installing ocean-lib, run `pip install vyper==0.3.7 --ignore-requires-python` and `sudo apt-get install python3.11-dev`
- (2) The `parsimonious` package depends on `getargsspec`, which doesn't support Python 3.11. The workaround: open the package's expressions.py file (e.g. in ./venv/lib/python3.11/site-packages/parsimonious/expressions.py), and change the line `import getfullargspec as getargsspec` instead of the regular import.

## ocean.py uses Brownie

When you installed Ocean (`ocean-lib` pypi package) above, it included installation of Brownie (`eth-brownie` package).

ocean.py uses Brownie to connect with deployed smart contracts.

Thanks to Brownie, ocean.py treats each Ocean smart contract as a Python class, and each deployed smart contract as a Python object. We love this feature, because it means Python programmers can treat Solidity code as Python code! 🤯
- ocean.py depends on the `parsimonious` package. In turn, `parsimonious` depends on `getargsspec`, which doesn't support Python 3.11. The workaround: open the package's expressions.py file (e.g. in ./venv/lib/python3.11/site-packages/parsimonious/expressions.py), and change the line `import getfullargspec as getargsspec` instead of the regular import.

## Next step

Expand Down
7 changes: 3 additions & 4 deletions READMEs/key-value-private.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ model_value = "<insert MLP weights here>"
# Compute a symmetric key: unique to this (nft, nft field) and (your priv key)
# Therefore you can calculate it anytime
from ocean_lib.ocean import crypto
symkey = crypto.calc_symkey(data_nft.address + model_label + alice.private_key)
symkey = crypto.calc_symkey(data_nft.address + model_label + alice.privateKey.hex())

# Symmetrically encrypt AI model
model_value_symenc = crypto.sym_encrypt(model_value, symkey)
Expand All @@ -77,8 +77,7 @@ There are many possible ways for Alice to share the symkey to Bob. Here, Alice s
In the Python console:
```python
# Get Bob's public key. There are various ways; see appendix.
pubkey = crypto.calc_pubkey(bob.private_key)
assert pubkey == str(bob.public_key)
pubkey = crypto.calc_pubkey(bob.privateKey.hex())

# Asymmetrically encrypt symkey, using Bob's public key
symkey_asymenc = crypto.asym_encrypt(symkey, pubkey)
Expand All @@ -98,7 +97,7 @@ In the Python console:
symkey_asymenc2 = data_nft.get_data("symkey")

# Asymetrically decrypt symkey, with Bob's private key
symkey2 = crypto.asym_decrypt(symkey_asymenc2, bob.private_key)
symkey2 = crypto.asym_decrypt(symkey_asymenc2, bob.privateKey.hex())
```

## 6. Retrieve from chain & decrypt AI model
Expand Down
10 changes: 5 additions & 5 deletions READMEs/main-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Bob now has the datatoken for the dataset! Time to download the dataset and use
In the same Python console:
```python
# Bob sends a datatoken to the service to get access
order_tx_id = ocean.assets.pay_for_access_service(ddo, {"from": bob})
order_tx_id = ocean.assets.pay_for_access_service(ddo, {"from": bob}).hex()

# Bob downloads the file. If the connection breaks, Bob can try again
asset_dir = ocean.assets.download_asset(ddo, bob, './', order_tx_id)
Expand Down Expand Up @@ -170,7 +170,7 @@ ERC725:
- Official spec is at [eips.ethereum.org](https://eips.ethereum.org/EIPS/eip-725)
- Solidity interface: [IERC725X.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/interfaces/IERC725X.sol) (execution) and [IERC725Y.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/interfaces/IERC725Y.sol) (key-value store)

The `data_nft` is a Python object of class [DataNFT](https://github.com/oceanprotocol/ocean.py/blob/main/ocean_lib/models/data_nft.py). Thanks to Brownie, the DataNFT class directly exposes the Solidity ERC721 & ERC725 interfaces. This means your `data_nft` object has a Python method for every Solidity method! Thank you, Brownie :)
The `data_nft` is a Python object of class [DataNFT](https://github.com/oceanprotocol/ocean.py/blob/main/ocean_lib/models/data_nft.py). The DataNFT class directly exposes the Solidity ERC721 & ERC725 interfaces. This means your `data_nft` object has a Python method for every Solidity method!

Besides that, DataNFT implements other Python methods like `create_datatoken()` to improve developer experience. And, [ocean_assets.OceanAssets](https://github.com/oceanprotocol/ocean.py/blob/main/ocean_lib/ocean/ocean_assets.py) and other higher-level Python classes / methods work with DataNFT.

Expand All @@ -191,7 +191,7 @@ Ocean's architecture allows for >1 implementations of ERC20, each with its own "

Template 1:
- Solidity: [ERC20Template.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/templates/ERC20Template.sol)
- Python wrapper: [Datatoken1](https://github.com/oceanprotocol/ocean.py/blob/main/ocean_lib/models/datatoken.py). It has a Python method for every Solidity method, via Brownie.
- Python wrapper: [Datatoken1](https://github.com/oceanprotocol/ocean.py/blob/main/ocean_lib/models/datatoken.py). It has a Python method for every Solidity method.
- Implements methods like `start_order()`, `create_exchange()`, and `create_dispenser()` to enhance developer experience.

Template 2:
Expand Down Expand Up @@ -303,7 +303,7 @@ A given datatoken can create exactly one dispenser for that datatoken.

**Interface via [`Dispenser`](https://github.com/oceanprotocol/ocean.py/blob/main/ocean_lib/models/dispenser.py) Python class:**
- You can access its instance via `ocean.dispenser`.
- `Dispenser` wraps [Dispenser.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/pools/dispenser/Dispenser.sol) Solidity implementation, to expose `Dispenser.sol`'s methods as Python methods (via Brownie).
- `Dispenser` wraps [Dispenser.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/pools/dispenser/Dispenser.sol) Solidity implementation, to expose `Dispenser.sol`'s methods as Python methods.
- Note that `Dispenser` is _global_ across all datatokens. Therefore calls to the Solidity contract - or Python calls that pass through - provide the datatoken address as an argument.
- Example call: `ocean.dispenser.setAllowedSwapper(datatoken_addr, ZERO_ADDRESS, {"from": alice})`

Expand Down Expand Up @@ -377,7 +377,7 @@ While most interactions are with `OneExchange` described above, sometimes we may

**Interface via [`FixedRateExchange`](https://github.com/oceanprotocol/ocean.py/blob/4aa12afd8a933d64bc2ed68d1e5359d0b9ae62f9/ocean_lib/models/fixed_rate_exchange.py#L106) Python class:**
- You can access its instance via `ocean.fixed_rate_exchange`.
- `FixedRateExchange` wraps [FixedRateExchange.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/pools/fixedRate/FixedRateExchange.sol) Solidity implementation, to expose `Dispenser.sol`'s methods as Python methods (via Brownie).
- `FixedRateExchange` wraps [FixedRateExchange.sol](https://github.com/oceanprotocol/contracts/blob/main/contracts/pools/fixedRate/FixedRateExchange.sol) Solidity implementation, to expose `Dispenser.sol`'s methods as Python methods.
- Note that `FixedRateExchange` is _global_ across all datatokens. Therefore calls to the Solidity contract - or Python calls that pass through - provide the exchange id address as an argument. It's exchange id, and not datatoken, because there may be >1 exchange for a given datatoken.
- Example call: `ocean.fixed_rate_exchange.getRate(exchange_id)` returns rate (price).

Expand Down
11 changes: 8 additions & 3 deletions READMEs/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ SPDX-License-Identifier: Apache-2.0

We can set any config parameter using the config dictionary.

An `Ocean` instance will hold a `config_dict` that holds various config parameters. These parameters need to get set. This is set based on what's input to `Ocean` constructor:
An `Ocean` instance will hold a `config_dict` that holds various config parameters. These parameters need to get set using the ExampleConfig class. This is set based on what's input to `Ocean` constructor:

1. dict input: `Ocean({'METADATA_CACHE_URI':..})`
2. use boilerplate from example config
1. dict input: `Ocean({'METADATA_CACHE_URI':..})`, in which case you have to build the web3 instance manually
2. use boilerplate from example config, which also sets the web3 instance to be used by each contract

## Example

Expand All @@ -19,9 +19,14 @@ Here is an example for (1): dict input, filled from envvars
```python
import os
from ocean_lib.ocean.ocean import Ocean
from ocean_lib.example_config import get_web3

network_url = "https://your-rpc.com"

d = {
'METADATA_CACHE_URI': "https://v4.aquarius.oceanprotocol.com",
'PROVIDER_URL' : "https://v4.provider.goerli.oceanprotocol.com",
"web3_instance": get_web3(network_url)
}
ocean = Ocean(d)
```
Expand Down
Loading

0 comments on commit 303a9a0

Please sign in to comment.