graphsense-python 2.12.0__tar.gz → 2.13.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/PKG-INFO +12 -15
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/README.md +11 -14
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/__init__.py +1 -1
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api_client.py +1 -1
- graphsense_python-2.13.2/graphsense/cli/gs.py +139 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/main.py +2 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/configuration.py +2 -2
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/client.py +5 -2
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/io.py +31 -40
- graphsense_python-2.13.2/graphsense/gs_files/__init__.py +69 -0
- graphsense_python-2.13.2/graphsense/gs_files/encoder.py +336 -0
- graphsense_python-2.13.2/graphsense/gs_files/parser.py +440 -0
- graphsense_python-2.13.2/graphsense/gs_files/summary.py +34 -0
- graphsense_python-2.13.2/graphsense/gs_files/writer.py +73 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address.py +3 -1
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense_python.egg-info/PKG-INFO +12 -15
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense_python.egg-info/SOURCES.txt +7 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/pyproject.toml +1 -1
- graphsense_python-2.13.2/tests/test_cli_gs.py +80 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_ext_io.py +24 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/__init__.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/addresses_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/blocks_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/bulk_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/clusters_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/entities_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/general_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/rates_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/tags_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/tokens_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api/txs_api.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/api_response.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/__init__.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/__main__.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/bulk_cmd.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/context.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/convenience.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/errors.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/cli/raw.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/compat.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/exceptions.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/__init__.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/bulk.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/deprecation.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/output.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/ext/selectors.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/__init__.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/actor.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/actor_context.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/address.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/address_tag.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/address_tags.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/address_tx.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/address_tx_utxo.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/address_txs.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/block.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/block_at_date.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/concept.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/currency_stats.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/entity.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/entity_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/external_conversion.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/http_validation_error.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/label_summary.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/labeled_item_ref.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/link.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/link_utxo.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/links.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/links_inner.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/location_inner.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/neighbor_address.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/neighbor_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/neighbor_entities.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/neighbor_entity.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/rate.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/rates.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/related_address.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/related_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_by_currency.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_level1.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_level2.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_level3.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_level4.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_level5.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/search_result_level6.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/stats.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tag.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tag_cloud_entry.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tag_summary.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/taxonomy.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/token_config.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/token_configs.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tx.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tx_account.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tx_ref.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tx_summary.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tx_utxo.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/tx_value.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/user_reported_tag.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/user_tag_report_response.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/validation_error.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/model/values.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/__init__.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/actor.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/actor_context.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address_output.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address_tag.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address_tags.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address_tx.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address_tx_utxo.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/address_txs.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/block.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/block_at_date.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/change_heuristics.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/cluster.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/cluster_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/coin_join_consensus.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/coin_join_heuristics.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/concept.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/consensus_entry.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/currency_stats.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/direct_change_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/entity.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/entity_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/external_conversion.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/http_validation_error.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/join_market_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/label_summary.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/labeled_item_ref.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/link.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/link_utxo.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/links.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/links_inner.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/location_inner.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/multi_input_change_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/neighbor_address.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/neighbor_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/neighbor_cluster.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/neighbor_clusters.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/neighbor_entities.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/neighbor_entity.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/one_time_change_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/rate.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/rates.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/related_address.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/related_addresses.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_by_currency.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_level1.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_level2.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_level3.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_level4.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_level5.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/search_result_level6.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/stats.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tag.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tag_cloud_entry.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tag_summary.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/taxonomy.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/token_config.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/token_configs.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tx.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tx_account.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tx_ref.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tx_summary.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tx_utxo.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/tx_value.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/user_reported_tag.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/user_tag_report_response.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/utxo_heuristics.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/validation_error.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/values.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/wasabi_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/whirlpool_coin_join_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/models/whirlpool_tx0_heuristic.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/py.typed +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense/rest.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense_python.egg-info/dependency_links.txt +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense_python.egg-info/entry_points.txt +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense_python.egg-info/requires.txt +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/graphsense_python.egg-info/top_level.txt +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/setup.cfg +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_cli_bulk.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_cli_convenience.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_cli_errors.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_cli_io_pipes.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_cli_raw_mirror.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_cli_tags_and_dates.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_ext_bulk.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_ext_client.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_ext_output.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_ext_selectors.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_readme_template.py +0 -0
- {graphsense_python-2.12.0 → graphsense_python-2.13.2}/tests/test_regen_survives.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphsense-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.2
|
|
4
4
|
Summary: GraphSense API
|
|
5
5
|
Author-email: Iknaio Cryptoasset Analytics GmbH <contact@iknaio.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -43,22 +43,10 @@ See the [full versioning and deprecation policy](https://github.com/graphsense/g
|
|
|
43
43
|
for details.
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
## Using the high-level wrapper or CLI
|
|
47
|
-
|
|
48
|
-
Two hand-written convenience layers ride on top of the generated client
|
|
49
|
-
(both protected from regeneration via `.openapi-generator-ignore`):
|
|
50
|
-
|
|
51
|
-
- **Python** — [`README_EXT.md`](README_EXT.md) documents
|
|
52
|
-
`graphsense.ext.GraphSense`, a single facade that removes boilerplate and
|
|
53
|
-
bundles commonly-paired calls (ships with the base install).
|
|
54
|
-
- **Shell** — [`README_CLI.md`](README_CLI.md) documents the `graphsense` CLI
|
|
55
|
-
(installed via `uv add 'graphsense-python[cli]'`): pipe-friendly,
|
|
56
|
-
JSON/CSV I/O, auto-bulk.
|
|
57
|
-
|
|
58
46
|
This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
|
59
47
|
|
|
60
|
-
- API version: 2.
|
|
61
|
-
- Package version: 2.
|
|
48
|
+
- API version: 2.13.2
|
|
49
|
+
- Package version: 2.13.2
|
|
62
50
|
- Generator version: 7.19.0
|
|
63
51
|
- Build package: org.openapitools.codegen.languages.PythonClientCodegen
|
|
64
52
|
For more information, please visit [https://www.iknaio.com/](https://www.iknaio.com/)
|
|
@@ -295,3 +283,12 @@ Authentication schemes defined for the API:
|
|
|
295
283
|
|
|
296
284
|
contact@iknaio.com
|
|
297
285
|
|
|
286
|
+
|
|
287
|
+
## Using the high-level wrapper or CLI
|
|
288
|
+
|
|
289
|
+
- **Python** — [`README_EXT.md`](README_EXT.md) documents
|
|
290
|
+
`graphsense.ext.GraphSense`, a single facade that removes boilerplate and
|
|
291
|
+
bundles commonly-paired calls (ships with the base install).
|
|
292
|
+
- **Shell** — [`README_CLI.md`](README_CLI.md) documents the `graphsense` CLI
|
|
293
|
+
(installed via `uv add 'graphsense-python[cli]'`): pipe-friendly,
|
|
294
|
+
JSON/CSV I/O, auto-bulk.
|
|
@@ -21,22 +21,10 @@ See the [full versioning and deprecation policy](https://github.com/graphsense/g
|
|
|
21
21
|
for details.
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
## Using the high-level wrapper or CLI
|
|
25
|
-
|
|
26
|
-
Two hand-written convenience layers ride on top of the generated client
|
|
27
|
-
(both protected from regeneration via `.openapi-generator-ignore`):
|
|
28
|
-
|
|
29
|
-
- **Python** — [`README_EXT.md`](README_EXT.md) documents
|
|
30
|
-
`graphsense.ext.GraphSense`, a single facade that removes boilerplate and
|
|
31
|
-
bundles commonly-paired calls (ships with the base install).
|
|
32
|
-
- **Shell** — [`README_CLI.md`](README_CLI.md) documents the `graphsense` CLI
|
|
33
|
-
(installed via `uv add 'graphsense-python[cli]'`): pipe-friendly,
|
|
34
|
-
JSON/CSV I/O, auto-bulk.
|
|
35
|
-
|
|
36
24
|
This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
|
37
25
|
|
|
38
|
-
- API version: 2.
|
|
39
|
-
- Package version: 2.
|
|
26
|
+
- API version: 2.13.2
|
|
27
|
+
- Package version: 2.13.2
|
|
40
28
|
- Generator version: 7.19.0
|
|
41
29
|
- Build package: org.openapitools.codegen.languages.PythonClientCodegen
|
|
42
30
|
For more information, please visit [https://www.iknaio.com/](https://www.iknaio.com/)
|
|
@@ -273,3 +261,12 @@ Authentication schemes defined for the API:
|
|
|
273
261
|
|
|
274
262
|
contact@iknaio.com
|
|
275
263
|
|
|
264
|
+
|
|
265
|
+
## Using the high-level wrapper or CLI
|
|
266
|
+
|
|
267
|
+
- **Python** — [`README_EXT.md`](README_EXT.md) documents
|
|
268
|
+
`graphsense.ext.GraphSense`, a single facade that removes boilerplate and
|
|
269
|
+
bundles commonly-paired calls (ships with the base install).
|
|
270
|
+
- **Shell** — [`README_CLI.md`](README_CLI.md) documents the `graphsense` CLI
|
|
271
|
+
(installed via `uv add 'graphsense-python[cli]'`): pipe-friendly,
|
|
272
|
+
JSON/CSV I/O, auto-bulk.
|
|
@@ -95,7 +95,7 @@ class ApiClient:
|
|
|
95
95
|
self.default_headers[header_name] = header_value
|
|
96
96
|
self.cookie = cookie
|
|
97
97
|
# Set default User-Agent.
|
|
98
|
-
self.user_agent = 'OpenAPI-Generator/2.
|
|
98
|
+
self.user_agent = 'OpenAPI-Generator/2.13.2/python'
|
|
99
99
|
self.client_side_validation = configuration.client_side_validation
|
|
100
100
|
|
|
101
101
|
def __enter__(self):
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""`graphsense gs ...` — read GraphSense `.gs` save files (graph + pathfinder).
|
|
2
|
+
|
|
3
|
+
The extraction commands `txs` and `addresses` emit a uniform
|
|
4
|
+
`[{"network", "id"}, ...]` shape that feeds directly into `lookup-tx`
|
|
5
|
+
and `lookup-address` via the standard
|
|
6
|
+
`--address-jq '[].id' --network-jq '[].network'` selectors.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
graphsense gs txs graph.gs | \\
|
|
10
|
+
graphsense --address-jq '[].id' --network-jq '[].network' \\
|
|
11
|
+
lookup-tx btc
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
import rich_click as click
|
|
20
|
+
|
|
21
|
+
from graphsense.cli.context import CliContext
|
|
22
|
+
from graphsense.ext import output as out_mod
|
|
23
|
+
from graphsense.gs_files import (
|
|
24
|
+
GraphData,
|
|
25
|
+
PathfinderData,
|
|
26
|
+
decode_gs,
|
|
27
|
+
structure,
|
|
28
|
+
summarize,
|
|
29
|
+
to_jsonable,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
pass_ctx = click.make_pass_decorator(CliContext)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _write(ctx: CliContext, payload: Any) -> None:
|
|
36
|
+
out_mod.write(
|
|
37
|
+
payload,
|
|
38
|
+
output=ctx.output,
|
|
39
|
+
directory=ctx.directory,
|
|
40
|
+
format=ctx.format,
|
|
41
|
+
color=ctx.color,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _load(file: Path) -> PathfinderData | GraphData:
|
|
46
|
+
return structure(decode_gs(file))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _dedupe(records: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
50
|
+
"""Drop duplicate (network, id) pairs, preserving first-seen order."""
|
|
51
|
+
seen: set[tuple[str, str]] = set()
|
|
52
|
+
out: list[dict[str, Any]] = []
|
|
53
|
+
for r in records:
|
|
54
|
+
key = (str(r["network"]), str(r["id"]))
|
|
55
|
+
if key in seen:
|
|
56
|
+
continue
|
|
57
|
+
seen.add(key)
|
|
58
|
+
out.append(r)
|
|
59
|
+
return out
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _txs(data: PathfinderData | GraphData) -> list[dict[str, Any]]:
|
|
63
|
+
if isinstance(data, PathfinderData):
|
|
64
|
+
return [{"network": t.id.currency, "id": t.id.id} for t in data.txs]
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _addresses(data: PathfinderData | GraphData) -> list[dict[str, Any]]:
|
|
69
|
+
if isinstance(data, PathfinderData):
|
|
70
|
+
return [{"network": t.id.currency, "id": t.id.id} for t in data.addresses]
|
|
71
|
+
return [{"network": a.currency, "id": a.address} for a in data.addresses]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@click.group(name="gs")
|
|
75
|
+
def gs_group() -> None:
|
|
76
|
+
"""Read GraphSense `.gs` save files (Pathfinder + Graph dashboards)."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@gs_group.command("txs")
|
|
80
|
+
@click.argument("file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
81
|
+
@click.option(
|
|
82
|
+
"--dedupe/--no-dedupe",
|
|
83
|
+
default=True,
|
|
84
|
+
help="Drop duplicate (network, id) pairs. Default: on.",
|
|
85
|
+
)
|
|
86
|
+
@pass_ctx
|
|
87
|
+
def txs_cmd(ctx: CliContext, file: Path, dedupe: bool) -> None:
|
|
88
|
+
"""Emit `{network, id}` records for every transaction in FILE.
|
|
89
|
+
|
|
90
|
+
Only Pathfinder files contain transactions; for Graph files this emits
|
|
91
|
+
an empty list.
|
|
92
|
+
"""
|
|
93
|
+
data = _load(file)
|
|
94
|
+
records = _txs(data)
|
|
95
|
+
if dedupe:
|
|
96
|
+
records = _dedupe(records)
|
|
97
|
+
_write(ctx, records)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@gs_group.command("addresses")
|
|
101
|
+
@click.argument("file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
102
|
+
@click.option(
|
|
103
|
+
"--dedupe/--no-dedupe",
|
|
104
|
+
default=True,
|
|
105
|
+
help="Drop duplicate (network, id) pairs. Default: on.",
|
|
106
|
+
)
|
|
107
|
+
@pass_ctx
|
|
108
|
+
def addresses_cmd(ctx: CliContext, file: Path, dedupe: bool) -> None:
|
|
109
|
+
"""Emit `{network, id}` records for every address in FILE."""
|
|
110
|
+
data = _load(file)
|
|
111
|
+
records = _addresses(data)
|
|
112
|
+
if dedupe:
|
|
113
|
+
records = _dedupe(records)
|
|
114
|
+
_write(ctx, records)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@gs_group.command("decode")
|
|
118
|
+
@click.argument("file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
119
|
+
@click.option(
|
|
120
|
+
"--raw",
|
|
121
|
+
is_flag=True,
|
|
122
|
+
default=False,
|
|
123
|
+
help="Emit the raw decoded JSON instead of the structured view.",
|
|
124
|
+
)
|
|
125
|
+
@pass_ctx
|
|
126
|
+
def decode_cmd(ctx: CliContext, file: Path, raw: bool) -> None:
|
|
127
|
+
"""Decode FILE to JSON (structured by default)."""
|
|
128
|
+
if raw:
|
|
129
|
+
_write(ctx, decode_gs(file))
|
|
130
|
+
return
|
|
131
|
+
_write(ctx, to_jsonable(structure(decode_gs(file))))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@gs_group.command("summary")
|
|
135
|
+
@click.argument("file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
136
|
+
@pass_ctx
|
|
137
|
+
def summary_cmd(ctx: CliContext, file: Path) -> None:
|
|
138
|
+
"""Emit a short summary (kind, version, counts) for FILE."""
|
|
139
|
+
_write(ctx, summarize(_load(file)))
|
|
@@ -16,6 +16,7 @@ from graphsense.cli.bulk_cmd import bulk_command
|
|
|
16
16
|
from graphsense.cli.context import CliContext
|
|
17
17
|
from graphsense.cli.convenience import register_convenience_commands
|
|
18
18
|
from graphsense.cli.errors import FriendlyErrorGroup
|
|
19
|
+
from graphsense.cli.gs import gs_group
|
|
19
20
|
from graphsense.cli.raw import build_raw_group
|
|
20
21
|
from graphsense.ext.client import API_KEY_ENV_VARS, HOST_ENV_VARS
|
|
21
22
|
|
|
@@ -206,6 +207,7 @@ def cli(
|
|
|
206
207
|
register_convenience_commands(cli)
|
|
207
208
|
cli.add_command(bulk_command, name="bulk")
|
|
208
209
|
cli.add_command(build_raw_group(), name="raw")
|
|
210
|
+
cli.add_command(gs_group, name="gs")
|
|
209
211
|
|
|
210
212
|
|
|
211
213
|
def main() -> None: # pragma: no cover - trivial
|
|
@@ -533,8 +533,8 @@ conf = graphsense.Configuration(
|
|
|
533
533
|
return "Python SDK Debug Report:\n"\
|
|
534
534
|
"OS: {env}\n"\
|
|
535
535
|
"Python Version: {pyversion}\n"\
|
|
536
|
-
"Version of the API: 2.
|
|
537
|
-
"SDK Package Version: 2.
|
|
536
|
+
"Version of the API: 2.13.2\n"\
|
|
537
|
+
"SDK Package Version: 2.13.2".\
|
|
538
538
|
format(env=sys.platform, pyversion=sys.version)
|
|
539
539
|
|
|
540
540
|
def get_host_settings(self) -> List[HostSetting]:
|
|
@@ -280,7 +280,10 @@ class GraphSense:
|
|
|
280
280
|
cluster_id = getattr(base, "cluster", None)
|
|
281
281
|
if cluster_id is not None:
|
|
282
282
|
jobs["cluster"] = pool.submit(
|
|
283
|
-
clusters.get_cluster,
|
|
283
|
+
clusters.get_cluster,
|
|
284
|
+
ccy,
|
|
285
|
+
int(cluster_id),
|
|
286
|
+
exclude_best_address_tag=True,
|
|
284
287
|
)
|
|
285
288
|
results = {k: f.result() for k, f in jobs.items()}
|
|
286
289
|
|
|
@@ -302,7 +305,7 @@ class GraphSense:
|
|
|
302
305
|
ccy = self._currency(currency)
|
|
303
306
|
clusters = self.raw.clusters
|
|
304
307
|
cid = int(cluster_id)
|
|
305
|
-
base = clusters.get_cluster(ccy, cid)
|
|
308
|
+
base = clusters.get_cluster(ccy, cid, exclude_best_address_tag=True)
|
|
306
309
|
|
|
307
310
|
jobs: dict[str, Any] = {}
|
|
308
311
|
with ThreadPoolExecutor(max_workers=self.max_workers) as pool:
|
|
@@ -131,27 +131,32 @@ def _extract_from_csv(
|
|
|
131
131
|
rows = list(reader)
|
|
132
132
|
if not rows:
|
|
133
133
|
return []
|
|
134
|
-
header
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
header = rows[0]
|
|
135
|
+
|
|
136
|
+
# Named --col: row 1 is the header, look the name up directly. This
|
|
137
|
+
# avoids relying on `_looks_like_header` heuristics for the path that
|
|
138
|
+
# users hit most often.
|
|
139
|
+
if col is not None:
|
|
140
|
+
try:
|
|
141
|
+
idx = int(col)
|
|
142
|
+
except ValueError:
|
|
143
|
+
try:
|
|
144
|
+
idx = header.index(col)
|
|
145
|
+
except ValueError as exc:
|
|
139
146
|
raise ValueError(
|
|
140
|
-
"
|
|
141
|
-
)
|
|
142
|
-
idx
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return [_cell(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
idx = _resolve_col_index(col, None)
|
|
154
|
-
return [_cell(row, idx) for row in all_rows if _row_keep(row, idx, skip_empty)]
|
|
147
|
+
f"column {col!r} not found in header {header!r}"
|
|
148
|
+
) from exc
|
|
149
|
+
return [_cell(r, idx) for r in rows[1:] if _row_keep(r, idx, skip_empty)]
|
|
150
|
+
|
|
151
|
+
data_rows = rows[1:] if _looks_like_header(header) else rows
|
|
152
|
+
return [_cell(r, idx) for r in data_rows if _row_keep(r, idx, skip_empty)]
|
|
153
|
+
|
|
154
|
+
# No --col: only single-column input is unambiguous.
|
|
155
|
+
has_header = _looks_like_header(header)
|
|
156
|
+
if len(header) != 1:
|
|
157
|
+
raise ValueError("CSV has multiple columns; specify --col <name_or_index>")
|
|
158
|
+
data_rows = rows[1:] if has_header else rows
|
|
159
|
+
return [_cell(r, 0) for r in data_rows if _row_keep(r, 0, skip_empty)]
|
|
155
160
|
|
|
156
161
|
|
|
157
162
|
def _cell(row: list[str], idx: int) -> str:
|
|
@@ -164,35 +169,21 @@ def _row_keep(row: list[str], idx: int, skip_empty: bool) -> bool:
|
|
|
164
169
|
return True
|
|
165
170
|
|
|
166
171
|
|
|
167
|
-
def _resolve_col_index(col: str, header: Optional[list[str]]) -> int:
|
|
168
|
-
try:
|
|
169
|
-
return int(col)
|
|
170
|
-
except ValueError:
|
|
171
|
-
pass
|
|
172
|
-
if header is None:
|
|
173
|
-
raise ValueError(f"column name {col!r} given but CSV has no header")
|
|
174
|
-
try:
|
|
175
|
-
return header.index(col)
|
|
176
|
-
except ValueError as exc:
|
|
177
|
-
raise ValueError(f"column {col!r} not found in header {header!r}") from exc
|
|
178
|
-
|
|
179
|
-
|
|
180
172
|
def _looks_like_header(row: list[str]) -> bool:
|
|
181
173
|
"""Treat the first row as a header unless every cell looks like an id."""
|
|
182
174
|
if not row:
|
|
183
175
|
return False
|
|
184
|
-
# If any cell contains characters that are common in plain-text headers
|
|
185
|
-
# but unusual in blockchain ids, treat it as a header. Simple heuristic:
|
|
186
|
-
# header cells tend to be short and contain only letters/_.
|
|
187
176
|
for cell in row:
|
|
188
177
|
c = cell.strip()
|
|
189
178
|
if not c:
|
|
190
179
|
return False
|
|
191
|
-
|
|
180
|
+
# Header cells: letters + underscores only, capped length. Digits
|
|
181
|
+
# disqualify (would suggest an id like `1A1z…`, `0xabc…`, or a
|
|
182
|
+
# numeric cluster id).
|
|
183
|
+
stripped = c.replace("_", "")
|
|
184
|
+
if not stripped.isalpha() or len(c) > 32:
|
|
192
185
|
return False
|
|
193
|
-
|
|
194
|
-
# chars, call it a header. Otherwise treat it as data.
|
|
195
|
-
return all(c.strip().isalpha() and len(c.strip()) <= 20 for c in row)
|
|
186
|
+
return True
|
|
196
187
|
|
|
197
188
|
|
|
198
189
|
def _extract_from_lines(text: str, *, skip_empty: bool = True) -> list[str]:
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# AUTO-GENERATED — DO NOT EDIT.
|
|
2
|
+
# Synced from src/graphsenselib/convert/gs_files/__init__.py via
|
|
3
|
+
# clients/python/scripts/sync_gs_files.py. Edit the source and re-run
|
|
4
|
+
# `make -C clients/python sync-gs-files`.
|
|
5
|
+
"""Encode and decode GraphSense `.gs` save files.
|
|
6
|
+
|
|
7
|
+
Decode:
|
|
8
|
+
decode_gs_bytes / decode_gs — bytes/path to raw JSON
|
|
9
|
+
structure — raw JSON to typed dataclasses
|
|
10
|
+
summarize — typed dataclasses to short summary dict
|
|
11
|
+
to_jsonable / write_json — JSON serialization helpers
|
|
12
|
+
|
|
13
|
+
Encode:
|
|
14
|
+
GsBuilder — high-level fluent API for building graphs
|
|
15
|
+
encode_gs_payload — raw payload list to .gs bytes
|
|
16
|
+
builder_from_spec — build a GsBuilder from a JSON spec dict
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .encoder import (
|
|
20
|
+
GsBuilder,
|
|
21
|
+
builder_from_spec,
|
|
22
|
+
encode_gs_payload,
|
|
23
|
+
)
|
|
24
|
+
from .parser import (
|
|
25
|
+
Color,
|
|
26
|
+
GraphAddress,
|
|
27
|
+
GraphCluster,
|
|
28
|
+
GraphData,
|
|
29
|
+
Highlight,
|
|
30
|
+
PathfinderAggEdge,
|
|
31
|
+
PathfinderAnnotation,
|
|
32
|
+
PathfinderData,
|
|
33
|
+
PathfinderId,
|
|
34
|
+
PathfinderThing,
|
|
35
|
+
UserTag,
|
|
36
|
+
decode_gs,
|
|
37
|
+
decode_gs_bytes,
|
|
38
|
+
lzw_pack,
|
|
39
|
+
lzw_unpack,
|
|
40
|
+
structure,
|
|
41
|
+
)
|
|
42
|
+
from .summary import summarize
|
|
43
|
+
from .writer import to_jsonable, write_decoded, write_json
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"Color",
|
|
47
|
+
"GraphAddress",
|
|
48
|
+
"GraphCluster",
|
|
49
|
+
"GraphData",
|
|
50
|
+
"GsBuilder",
|
|
51
|
+
"Highlight",
|
|
52
|
+
"PathfinderAggEdge",
|
|
53
|
+
"PathfinderAnnotation",
|
|
54
|
+
"PathfinderData",
|
|
55
|
+
"PathfinderId",
|
|
56
|
+
"PathfinderThing",
|
|
57
|
+
"UserTag",
|
|
58
|
+
"builder_from_spec",
|
|
59
|
+
"decode_gs",
|
|
60
|
+
"decode_gs_bytes",
|
|
61
|
+
"encode_gs_payload",
|
|
62
|
+
"lzw_pack",
|
|
63
|
+
"lzw_unpack",
|
|
64
|
+
"structure",
|
|
65
|
+
"summarize",
|
|
66
|
+
"to_jsonable",
|
|
67
|
+
"write_decoded",
|
|
68
|
+
"write_json",
|
|
69
|
+
]
|