diff --git a/.github/workflows/main_workflow.yml b/.github/workflows/main_workflow.yml index 79c699a6f8..bfbc1c9a70 100644 --- a/.github/workflows/main_workflow.yml +++ b/.github/workflows/main_workflow.yml @@ -110,8 +110,8 @@ jobs: pip install tomte[tox]==0.2.13 pip install --user --upgrade setuptools # install Protobuf compiler - wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_64.zip - unzip protoc-3.19.4-linux-x86_64.zip -d protoc + wget https://github.com/protocolbuffers/protobuf/releases/download/v24.3/protoc-24.3-linux-x86_64.zip + unzip protoc-24.3-linux-x86_64.zip -d protoc sudo mv protoc/bin/protoc /usr/local/bin/protoc # install IPFS sudo apt-get install -y wget @@ -204,8 +204,8 @@ jobs: pip install --user --upgrade setuptools # install Protobuf compiler - wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_64.zip - unzip protoc-3.19.4-linux-x86_64.zip -d protoc + wget https://github.com/protocolbuffers/protobuf/releases/download/v24.3/protoc-24.3-linux-x86_64.zip + unzip protoc-24.3-linux-x86_64.zip -d protoc sudo mv protoc/bin/protoc /usr/local/bin/protoc # install IPFS @@ -258,8 +258,8 @@ jobs: brew install gcc # brew install protobuf # brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/72457f0166d5619a83f508f2345b22d0617b5021/Formula/protobuf.rb - wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-osx-x86_64.zip - unzip protoc-3.19.4-osx-x86_64.zip -d protoc + wget https://github.com/protocolbuffers/protobuf/releases/download/v24.3/protoc-24.3-osx-x86_64.zip + unzip protoc-24.3-osx-x86_64.zip -d protoc sudo mv protoc/bin/protoc /usr/local/bin/protoc brew tap yoheimuta/protolint brew install protolint @@ -302,14 +302,14 @@ jobs: python -m pip install -U pip echo "::add-path::C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64" choco install wget -y - choco install protoc --version 3.19.4 + choco install protoc --version 24.3 choco install mingw -y choco install make -y # to check make was installed make --version pip install tomte[tox]==0.2.13 - # wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-win64.zip - # unzip protoc-3.19.4-win64.zip -d protoc + # wget https://github.com/protocolbuffers/protobuf/releases/download/v24.3/protoc-24.3-win64.zip + # unzip protoc-24.3-win64.zip -d protoc # sudo mv protoc/bin/protoc /usr/local/bin/protoc # TODO: install protolint @@ -386,8 +386,8 @@ jobs: pip install --user --upgrade setuptools # install Protobuf compiler - wget https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_64.zip - unzip protoc-3.19.4-linux-x86_64.zip -d protoc + wget https://github.com/protocolbuffers/protobuf/releases/download/v24.3/protoc-24.3-linux-x86_64.zip + unzip protoc-24.3-linux-x86_64.zip -d protoc sudo mv protoc/bin/protoc /usr/local/bin/protoc # install IPFS diff --git a/.gitignore b/.gitignore index 24026d4a36..488dae5995 100644 --- a/.gitignore +++ b/.gitignore @@ -403,11 +403,16 @@ packages/valory/protocols/contract_api packages/valory/protocols/http packages/valory/protocols/ledger_api -autonomy/data/contracts/registries_manager -autonomy/data/contracts/component_registry -autonomy/data/contracts/service_registry autonomy/data/contracts/agent_registry +autonomy/data/contracts/component_registry +autonomy/data/contracts/erc20 +autonomy/data/contracts/gnosis_safe +autonomy/data/contracts/gnosis_safe_proxy_factory +autonomy/data/contracts/multisend +autonomy/data/contracts/registries_manager autonomy/data/contracts/service_manager +autonomy/data/contracts/service_registry +autonomy/data/contracts/service_registry_token_utility .vscode/ @@ -419,4 +424,5 @@ leak_report temp/ logs.db +benchmarks.html agent/ \ No newline at end of file diff --git a/.pylintrc b/.pylintrc index 59c6ce472c..245ae911c7 100644 --- a/.pylintrc +++ b/.pylintrc @@ -3,7 +3,7 @@ ignore-patterns=.*_pb2.py,contract_dispatcher.py,test_abci_messages.py,test_tend ignore=packages/valory/protocols/abci,packages/valory/connections/abci/gogoproto [MESSAGES CONTROL] -disable=C0103,R0801,C0301,C0201,C0204,C0209,W1203,C0302,R1735,R1729,W0511 +disable=C0103,R0801,C0301,C0201,C0204,C0209,W1203,C0302,R1735,R1729,W0511,E0611 # See here for more options: https://www.codeac.io/documentation/pylint-configuration.html R1735: use-dict-literal @@ -16,6 +16,7 @@ C0209: consider-using-f-string C0301: http://pylint-messages.wikidot.com/messages:c0301 > Line too long C0302: http://pylint-messages.wikidot.com/messages:c0302 > Too many lines in module R0801: similar lines +E0611: no-name-in-module [IMPORTS] ignored-modules=pandas,numpy,aea_cli_ipfs,compose,multidict diff --git a/.spelling b/.spelling index e03f34190b..1cbf2d227f 100644 --- a/.spelling +++ b/.spelling @@ -420,4 +420,7 @@ selection_key 0.10.10.post1 unbond chiado -0.12.1.post1 \ No newline at end of file +0.12.1.post1 +0.12.1.post2 +0.12.1.post3 +0.12.1.post4 \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index ae008fca2f..fa521e8e73 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,44 @@ # Release History - `open-autonomy` +# 0.13.0 (2023-09-27) + +Autonomy: +- Replaces `open-aea-web3` with `web3py<7,>=6.0.0` +- Bumps `protobuf<5.0.0,>=4.21.6` +- Fixes `protobuf` incompatibility issue when importing hardware wallet plugin +- Refactors autonomy and agent images to + - Include install and build scripts in the base image + - Remove unwanted layers + - Remove unwanted data files + +Packages: +- Generates protocols using the latest compatible `protobuf` compiler +- Compiles the tendermint connection protocol buffers using the latest compatible `protobuf` compiler + +Chores: +- Bumps `protobuf` compiler to `24.3` + +# 0.12.1.post4 (2023-09-25) + +Autonomy: + +- Update the reuse multisig transaction builder to account for services with only one operator + +# 0.12.1.post3 (2023-09-21) + +Autonomy: + +- Pins `jsonschema<=4.19.0,>=4.16.0` + +# 0.12.1.post2 (2023-09-20) + +Autonomy: + +- Adds missing contract packages to the `eject-contracts` make target +- Adds check to make sure service is in `pre-registration` before updating the service hash +- Adds check to make sure all required environment variables are present for on-chain interactions + # 0.12.1.post1 (2023-09-14) Autonomy: diff --git a/Makefile b/Makefile index 49cf4c57bb..e76cf4a92d 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ install: clean .PHONY: eject-contracts eject-contracts: - @for contract in registries_manager service_manager component_registry agent_registry service_registry ; do \ + @for contract in component_registry agent_registry registries_manager service_manager service_registry gnosis_safe gnosis_safe_proxy_factory service_registry_token_utility multisend erc20 ; do \ echo Updating $$contract contract; \ rm -rf autonomy/data/contracts/$$contract ; \ cp -r packages/valory/contracts/$$contract autonomy/data/contracts/$$contract ; \ @@ -235,3 +235,19 @@ fix-abci-app-specs: release-images: skaffold build -p release --cache-artifacts=false && skaffold build -p release-latest + + +# Usage: INCLUDE=PATH_TO_PROTOC_INCLUDE_DIRECTORY make build-proto +.PHONY: build-proto +build-proto: + @protoc -I $$INCLUDE \ + --proto_path=packages/valory/connections/abci/protos/ \ + --python_out=packages/valory/connections/abci/ \ + packages/valory/connections/abci/protos/gogoproto/gogo.proto \ + packages/valory/connections/abci/protos/tendermint/crypto/proof.proto \ + packages/valory/connections/abci/protos/tendermint/crypto/keys.proto \ + packages/valory/connections/abci/protos/tendermint/abci/types.proto \ + packages/valory/connections/abci/protos/tendermint/types/types.proto \ + packages/valory/connections/abci/protos/tendermint/types/validator.proto \ + packages/valory/connections/abci/protos/tendermint/types/params.proto \ + packages/valory/connections/abci/protos/tendermint/version/types.proto \ No newline at end of file diff --git a/Pipfile b/Pipfile index df14a3b354..fb0a2aecf9 100644 --- a/Pipfile +++ b/Pipfile @@ -7,10 +7,10 @@ name = "pypi" aiohttp = "<3.8,>=3.7.4" docker = "==6.1.2" Flask = "==2.0.2" -open-aea = {version = "==1.39.0", extras = ["all"]} -open-aea-ledger-ethereum = "==1.39.0" -open-aea-ledger-ethereum-hwi = "==1.39.0" -open-aea-cli-ipfs = "==1.39.0" +open-aea = {version = "==1.40.0", extras = ["all"]} +open-aea-ledger-ethereum = "==1.40.0" +open-aea-ledger-ethereum-hwi = "==1.40.0" +open-aea-cli-ipfs = "==1.40.0" ipfshttpclient = "==0.8.0a2" Werkzeug= "==2.0.3" watchdog = ">=2.1.6" @@ -23,7 +23,7 @@ valory-docker-compose = "==1.29.3" aiohttp = "<3.8,>=3.7.4" asn1crypto = "<1.5.0,>=1.4.0" ecdsa = ">=0.15" -open-aea-web3 = "==6.0.1" +web3 = "<7,>=6.0.0" certifi = "*" multidict = "*" eth_typing ="*" @@ -32,16 +32,13 @@ typing_extensions = ">=3.10.0.2" hexbytes = "*" packaging = "*" pytest-asyncio = "*" -open-aea-ledger-cosmos = "==1.39.0" +open-aea-ledger-cosmos = "==1.40.0" # we pin this as the range specified in open-aea-ledger-cosmos is wide -open-aea-cosmpy = "==0.6.5" +open-aea-cosmpy = "==0.6.6" grpcio = "==1.53.0" hypothesis = "==6.21.6" # latest supported for Python 3.7 -numpy = ">=1.21.6" -pandas = "==1.5.3" -pandas-stubs = "==1.2.0.62" -protobuf = "<=3.20.1,>=3.19" +protobuf = "<5.0.0,>=4.21.6" pytz = "==2022.2.1" py-ecc = "==6.0.0" python-dotenv = ">=0.14.0,<0.18.0" @@ -53,6 +50,7 @@ toml = "==0.10.2" eth-utils = "==2.2.0" eth-abi = "==4.0.0" pycryptodome = "==3.18.0" +jsonschema = "<=4.19.0,>=4.16.0" [requires] python_version = "3.10" diff --git a/SECURITY.md b/SECURITY.md index 972a3b8f0a..5e81cccbc9 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,8 +8,8 @@ The following table shows which versions of `open-autonomy` are currently being | Version | Supported | | --------- | ------------------ | -| `0.12.1.post1` | :white_check_mark: | -| `< 0.12.x` | :x: | +| `0.13.0` | :white_check_mark: | +| `< 0.13.x` | :x: | ## Reporting a Vulnerability diff --git a/autonomy/__version__.py b/autonomy/__version__.py index 00ad30f0e3..6115b6c35d 100644 --- a/autonomy/__version__.py +++ b/autonomy/__version__.py @@ -22,7 +22,7 @@ __title__ = "open-autonomy" __description__ = "A framework for the creation of autonomous agent services." __url__ = "https://github.com/valory-xyz/open-autonomy.git" -__version__ = "0.12.1.post1" +__version__ = "0.13.0" __author__ = "Valory AG" __license__ = "Apache-2.0" __copyright__ = "2021-2022 Valory AG" diff --git a/autonomy/analyse/abci/app_spec.py b/autonomy/analyse/abci/app_spec.py index 8fbcbe948e..73e8a87d99 100644 --- a/autonomy/analyse/abci/app_spec.py +++ b/autonomy/analyse/abci/app_spec.py @@ -45,6 +45,7 @@ ABCI_APP_CLASS_POST_FIX = "AbciApp" EVENT_PATTERN = re.compile(r"Event\.(\w+)", re.DOTALL) +ROUND_TIMEOUT_EVENTS = {"ROUND_TIMEOUT"} def validate_fsm_spec(data: Dict) -> None: @@ -454,14 +455,11 @@ def check_unreferenced_events(abci_app_cls: Any) -> List[str]: """ error_strings = [] - timeout_events = {k.name for k in abci_app_cls.event_to_timeout.keys()} + abci_app_timeout_events = {k.name for k in abci_app_cls.event_to_timeout.keys()} for round_cls, round_transitions in abci_app_cls.transition_function.items(): - trf_events = { - str(e).rsplit(".", 1)[1] for e in round_transitions - } - timeout_events + round_transition_events = set(map(lambda x: x.name, round_transitions)) referenced_events = set() - for base in filter( lambda x: x.__class__.__module__ != "builtins", inspect.getmro(round_cls), @@ -469,10 +467,21 @@ def check_unreferenced_events(abci_app_cls: Any) -> List[str]: src = textwrap.dedent(inspect.getsource(base)) referenced_events.update(EVENT_PATTERN.findall(src)) - if trf_events.symmetric_difference(referenced_events): + # Referenced in the the class definition, missing from transition func + missing_from_transition_func = referenced_events - round_transition_events + if len(missing_from_transition_func) > 0: + error_strings.append( + f"Events {missing_from_transition_func} are present in the `{round_cls.__name__}` " + f"but missing from transition function" + ) + + # Filter timeout events using referenced events since we don't explicitly return the timeout events + timeout_events = round_transition_events - referenced_events + missing_timeout_events = timeout_events - abci_app_timeout_events + if len(missing_timeout_events) > 0: error_strings.append( - f" - {round_cls.__name__}: transition function events {trf_events} " - f"do not match referenced events {referenced_events}." + f"Events {missing_timeout_events} are defined in the round transitions of `{round_cls.__name__}` " + f"but not in `event_to_timeout`" ) return error_strings diff --git a/autonomy/analyse/benchmark/aggregate.py b/autonomy/analyse/benchmark/aggregate.py index d2c51204e8..7fad25af28 100644 --- a/autonomy/analyse/benchmark/aggregate.py +++ b/autonomy/analyse/benchmark/aggregate.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2021-2022 Valory AG +# Copyright 2021-2023 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,12 +20,25 @@ """Tools for aggregating benchmark results.""" import json +import statistics from pathlib import Path -from typing import Any, Dict, List +from typing import Callable, Dict, List, Tuple, cast -import pandas as pd +from autonomy.analyse.benchmark.html import ( + BLOCK_TEMPLATE, + HTML_TEMPLATE, + TABLE_TEMPLATE, + TD_TEMPLATE, + TH_TEMPLATE, + TROW_TEMPLATE, +) -from autonomy.analyse.benchmark.html import HTML_TEMPLATE, TABLE_TEMPLATE + +STATISTICS = { + "Mean": statistics.mean, + "Maximum": max, + "Minimum": min, +} class BlockTypes: # pylint: disable=too-few-public-methods @@ -39,95 +52,149 @@ class BlockTypes: # pylint: disable=too-few-public-methods types = (LOCAL, CONSENSUS, TOTAL) -def read_benchmark_data(path: Path) -> List[Dict]: +def read_benchmark_data( + path: Path, + block_type: str = BlockTypes.ALL, + period: int = -1, +) -> Dict[str, Dict[str, List[Dict]]]: """Returns logs.""" - benchmark_data = [] + benchmark_data: Dict[str, Dict[str, List[Dict]]] = {} for agent_dir in path.iterdir(): - agent_benchmark_data: Dict[str, Any] = {} - agent_benchmark_data["name"] = agent_dir.name - agent_benchmark_data["data"] = dict( - map( - lambda path: ( - int(path.name.replace(".json", "")), - json.loads(path.read_text()), - ), - agent_dir.glob("*.json"), - ) + benchmark_data[agent_dir.name] = {} + benchmark_data_files = list(agent_dir.glob("*.json")) + periods = sorted( + tuple(map(lambda x: x.name, benchmark_data_files)) + if period == -1 + else (f"{period}.json",) ) - benchmark_data.append(agent_benchmark_data) - return benchmark_data - - -def create_dataframe(data: List[Dict]) -> pd.DataFrame: - """Create pandas.DataFrame object from benchmark data.""" - - rows = [] - behaviours = [behaviour_data["behaviour"] for behaviour_data in data[0]["data"][0]] - cols = ["agent", "period", "block_type", *behaviours] - - for agent in data: - for period_n, period_data in agent["data"].items(): - for block_t in BlockTypes.types: - rows.append( - { - "agent": agent["name"], - "period": period_n, - "block_type": block_t, - **{ - behaviour_data["behaviour"]: behaviour_data["data"].get( - block_t, 0.0 - ) - for behaviour_data in period_data - }, - } - ) + for _period in periods: + period_name, _ = _period.split(".") + period_data = json.loads((agent_dir / _period).read_text()) + if block_type == BlockTypes.ALL: + benchmark_data[agent_dir.name][period_name] = period_data + continue + + benchmark_data[agent_dir.name][period_name] = [ + { + "behaviour": behaviour["behaviour"], + "data": {block_type: behaviour["data"][block_type]}, + } + for behaviour in period_data + ] - return pd.DataFrame( - data=rows, - )[cols] - - -def format_output(df: pd.DataFrame, period: int, block_type: str) -> str: - """Format output from given dataframe and parameters""" - - df = df.copy(deep=True).fillna(value=0.0) - df = df[df["period"] == period] - df = df[df["block_type"] == block_type] + return benchmark_data - del df["period"] - del df["block_type"] - time_df = df[df.columns[1:]] - stats_df = pd.DataFrame( - data=[ - ["mean", *time_df.mean().values], - ["median", *time_df.median().values], - ["std_dev", *time_df.std().values], - ], - columns=df.columns, +def add_statistic( + name: str, + aggregator: Callable, + behaviours: List[str], + behaviour_history: Dict[str, List[float]], +) -> str: + """Add a stastic column.""" + rows = TD_TEMPLATE.format(name) + for behaviour in behaviours: + rows += TD_TEMPLATE.format(aggregator(behaviour_history[behaviour])) + return TROW_TEMPLATE.format(rows) + + +def add_statistics( + behaviours: List[str], + behaviour_history: Dict[str, List[float]], +) -> str: + """Add statistics.""" + tbody = TROW_TEMPLATE.format( + f"""