├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── linters │ ├── .flake8 │ ├── .isort.cfg │ ├── .markdown-lint.yml │ ├── .python-black │ └── .python-lint └── workflows │ ├── build-linux-arm64-installer.yml │ ├── build-linux-installer.yml │ ├── build-macos-installer.yml │ └── build-windows-installer.yml ├── .gitignore ├── .gitmodules ├── .isort.cfg ├── BUILD_TIMELORD.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── build_scripts ├── build_linux.sh ├── build_macos.sh ├── build_windows.ps1 └── installer-version.py ├── equality ├── __init__.py ├── cmds │ ├── __init__.py │ ├── configure.py │ ├── equality.py │ ├── farm.py │ ├── farm_funcs.py │ ├── init.py │ ├── init_funcs.py │ ├── keys.py │ ├── keys_funcs.py │ ├── netspace.py │ ├── netspace_funcs.py │ ├── plots.py │ ├── show.py │ ├── start.py │ ├── start_funcs.py │ ├── stop.py │ ├── units.py │ ├── wallet.py │ └── wallet_funcs.py ├── consensus │ ├── __init__.py │ ├── block_body_validation.py │ ├── block_creation.py │ ├── block_header_validation.py │ ├── block_record.py │ ├── block_rewards.py │ ├── block_root_validation.py │ ├── blockchain.py │ ├── blockchain_interface.py │ ├── coinbase.py │ ├── condition_costs.py │ ├── constants.py │ ├── cost_calculator.py │ ├── default_constants.py │ ├── deficit.py │ ├── difficulty_adjustment.py │ ├── find_fork_point.py │ ├── full_block_to_block_record.py │ ├── get_block_challenge.py │ ├── make_sub_epoch_summary.py │ ├── multiprocess_validation.py │ ├── network_type.py │ ├── pos_quality.py │ ├── pot_iterations.py │ └── vdf_info_computation.py ├── daemon │ ├── __init__.py │ ├── client.py │ ├── server.py │ └── windows_signal.py ├── farmer │ ├── __init__.py │ ├── farmer.py │ └── farmer_api.py ├── full_node │ ├── __init__.py │ ├── block_store.py │ ├── bundle_tools.py │ ├── coin_store.py │ ├── full_node.py │ ├── full_node_api.py │ ├── full_node_store.py │ ├── generator.py │ ├── mempool.py │ ├── mempool_check_conditions.py │ ├── mempool_manager.py │ ├── signage_point.py │ ├── sync_store.py │ └── weight_proof.py ├── harvester │ ├── __init__.py │ ├── harvester.py │ └── harvester_api.py ├── introducer │ ├── __init__.py │ ├── introducer.py │ └── introducer_api.py ├── plotting │ ├── check_plots.py │ ├── create_plots.py │ └── plot_tools.py ├── protocols │ ├── __init__.py │ ├── farmer_protocol.py │ ├── full_node_protocol.py │ ├── harvester_protocol.py │ ├── introducer_protocol.py │ ├── pool_protocol.py │ ├── protocol_message_types.py │ ├── shared_protocol.py │ ├── timelord_protocol.py │ └── wallet_protocol.py ├── pyinstaller.spec ├── rpc │ ├── __init__.py │ ├── farmer_rpc_api.py │ ├── farmer_rpc_client.py │ ├── full_node_rpc_api.py │ ├── full_node_rpc_client.py │ ├── harvester_rpc_api.py │ ├── harvester_rpc_client.py │ ├── rpc_client.py │ ├── rpc_server.py │ ├── wallet_rpc_api.py │ └── wallet_rpc_client.py ├── server │ ├── __init__.py │ ├── address_manager.py │ ├── address_manager_store.py │ ├── connection_utils.py │ ├── introducer_peers.py │ ├── node_discovery.py │ ├── outbound_message.py │ ├── rate_limits.py │ ├── reconnect_task.py │ ├── server.py │ ├── ssl_context.py │ ├── start_farmer.py │ ├── start_full_node.py │ ├── start_harvester.py │ ├── start_introducer.py │ ├── start_service.py │ ├── start_timelord.py │ ├── start_wallet.py │ ├── upnp.py │ └── ws_connection.py ├── simulator │ ├── __init__.py │ ├── full_node_simulator.py │ ├── simulator_constants.py │ ├── simulator_protocol.py │ └── start_simulator.py ├── ssl │ ├── create_ssl.py │ ├── dst_root_ca.pem │ ├── equality_ca.crt │ └── equality_ca.key ├── timelord │ ├── __init__.py │ ├── iters_from_block.py │ ├── timelord.py │ ├── timelord_api.py │ ├── timelord_launcher.py │ ├── timelord_state.py │ └── types.py ├── types │ ├── __init__.py │ ├── announcement.py │ ├── blockchain_format │ │ ├── __init__.py │ │ ├── classgroup.py │ │ ├── coin.py │ │ ├── foliage.py │ │ ├── pool_target.py │ │ ├── program.py │ │ ├── proof_of_space.py │ │ ├── reward_chain_block.py │ │ ├── sized_bytes.py │ │ ├── slots.py │ │ ├── sub_epoch_summary.py │ │ ├── tree_hash.py │ │ └── vdf.py │ ├── coin_record.py │ ├── coin_solution.py │ ├── condition_opcodes.py │ ├── condition_with_args.py │ ├── end_of_slot_bundle.py │ ├── full_block.py │ ├── generator_types.py │ ├── header_block.py │ ├── mempool_inclusion_status.py │ ├── mempool_item.py │ ├── name_puzzle_condition.py │ ├── peer_info.py │ ├── spend_bundle.py │ ├── unfinished_block.py │ ├── unfinished_header_block.py │ └── weight_proof.py ├── util │ ├── __init__.py │ ├── api_decorators.py │ ├── bech32m.py │ ├── block_cache.py │ ├── block_tools.py │ ├── byte_types.py │ ├── chain_utils.py │ ├── clvm.py │ ├── condition_tools.py │ ├── config.py │ ├── create_alert_file.py │ ├── db_wrapper.py │ ├── default_root.py │ ├── english.txt │ ├── equality_logging.py │ ├── errors.py │ ├── generator_tools.py │ ├── hash.py │ ├── initial-config.yaml │ ├── ints.py │ ├── json_util.py │ ├── keychain.py │ ├── lru_cache.py │ ├── make_test_constants.py │ ├── merkle_set.py │ ├── misc.py │ ├── network.py │ ├── partial_func.py │ ├── path.py │ ├── pip_import.py │ ├── prev_transaction_block.py │ ├── profiler.py │ ├── recursive_replace.py │ ├── safe_cancel_task.py │ ├── service_groups.py │ ├── setproctitle.py │ ├── significant_bits.py │ ├── streamable.py │ ├── struct_stream.py │ ├── type_checking.py │ ├── validate_alert.py │ ├── vdf_prover.py │ ├── wallet_tools.py │ └── ws_message.py └── wallet │ ├── __init__.py │ ├── block_record.py │ ├── cc_wallet │ ├── __init__.py │ ├── cc_info.py │ ├── cc_utils.py │ ├── cc_wallet.py │ ├── ccparent.py │ └── debug_spend_bundle.py │ ├── derivation_record.py │ ├── derive_keys.py │ ├── did_wallet │ ├── __init__.py │ ├── did_info.py │ ├── did_wallet.py │ └── did_wallet_puzzles.py │ ├── equalitylisp.py │ ├── key_val_store.py │ ├── puzzles │ ├── __init__.py │ ├── block_program_zero.clvm │ ├── block_program_zero.clvm.hex │ ├── block_program_zero.clvm.hex.sha256tree │ ├── calculate_synthetic_public_key.clvm │ ├── calculate_synthetic_public_key.clvm.hex │ ├── calculate_synthetic_public_key.clvm.hex.sha256tree │ ├── cc.clvm │ ├── cc.clvm.hex │ ├── cc.clvm.hex.sha256tree │ ├── cc_loader.py │ ├── condition_codes.clvm │ ├── create-lock-puzzlehash.clvm │ ├── create-lock-puzzlehash.clvm.hex.sha256tree │ ├── decompress_coin_solution_entry.clvm │ ├── decompress_coin_solution_entry.clvm.hex │ ├── decompress_coin_solution_entry.clvm.hex.sha256tree │ ├── decompress_coin_solution_entry_with_prefix.clvm │ ├── decompress_coin_solution_entry_with_prefix.clvm.hex │ ├── decompress_coin_solution_entry_with_prefix.clvm.hex.sha256tree │ ├── decompress_puzzle.clvm │ ├── decompress_puzzle.clvm.hex │ ├── decompress_puzzle.clvm.hex.sha256tree │ ├── did_innerpuz.clvm │ ├── did_innerpuz.clvm.hex │ ├── did_innerpuz.clvm.hex.sha256tree │ ├── equalitylisp_deserialisation.clvm │ ├── equalitylisp_deserialisation.clvm.hex │ ├── equalitylisp_deserialisation.clvm.hex.sha256tree │ ├── generator_for_single_coin.clvm │ ├── generator_for_single_coin.clvm.hex │ ├── generator_for_single_coin.clvm.hex.sha256tree │ ├── generator_loader.py │ ├── genesis-by-coin-id-with-0.clvm │ ├── genesis-by-coin-id-with-0.clvm.hex │ ├── genesis-by-coin-id-with-0.clvm.hex.sha256tree │ ├── genesis-by-puzzle-hash-with-0.clvm │ ├── genesis-by-puzzle-hash-with-0.clvm.hex │ ├── genesis-by-puzzle-hash-with-0.clvm.hex.sha256tree │ ├── genesis_by_coin_id_with_0.py │ ├── genesis_by_puzzle_hash_with_0.py │ ├── load_clvm.py │ ├── lock.inner.puzzle.clvm │ ├── lock.inner.puzzle.clvm.hex │ ├── lock.inner.puzzle.clvm.hex.sha256tree │ ├── p2_conditions.clvm │ ├── p2_conditions.clvm.hex │ ├── p2_conditions.clvm.hex.sha256tree │ ├── p2_conditions.py │ ├── p2_delegated_conditions.clvm │ ├── p2_delegated_conditions.clvm.hex │ ├── p2_delegated_conditions.clvm.hex.sha256tree │ ├── p2_delegated_conditions.py │ ├── p2_delegated_puzzle.clvm │ ├── p2_delegated_puzzle.clvm.hex │ ├── p2_delegated_puzzle.clvm.hex.sha256tree │ ├── p2_delegated_puzzle.py │ ├── p2_delegated_puzzle_or_hidden_puzzle.clvm │ ├── p2_delegated_puzzle_or_hidden_puzzle.clvm.hex │ ├── p2_delegated_puzzle_or_hidden_puzzle.clvm.hex.sha256tree │ ├── p2_delegated_puzzle_or_hidden_puzzle.py │ ├── p2_m_of_n_delegate_direct.clvm │ ├── p2_m_of_n_delegate_direct.clvm.hex │ ├── p2_m_of_n_delegate_direct.clvm.hex.sha256tree │ ├── p2_m_of_n_delegate_direct.py │ ├── p2_puzzle_hash.clvm │ ├── p2_puzzle_hash.clvm.hex │ ├── p2_puzzle_hash.clvm.hex.sha256tree │ ├── p2_puzzle_hash.py │ ├── prefarm │ │ ├── make_prefarm_ph.py │ │ └── spend_prefarm.py │ ├── puzzle_utils.py │ ├── recompile-all.sh │ ├── rl.clvm │ ├── rl.clvm.hex │ ├── rl.clvm.hex.sha256tree │ ├── rl_aggregation.clvm │ ├── rl_aggregation.clvm.hex │ ├── rl_aggregation.clvm.hex.sha256tree │ ├── rom_bootstrap_generator.clvm │ ├── rom_bootstrap_generator.clvm.hex │ ├── rom_bootstrap_generator.clvm.hex.sha256tree │ ├── rom_bootstrap_generator.py │ ├── sha256tree_module.clvm │ ├── sha256tree_module.clvm.hex │ ├── sha256tree_module.clvm.hex.sha256tree │ ├── singleton_top_layer.clvm │ ├── singleton_top_layer.clvm.hex │ ├── singleton_top_layer.clvm.hex.sha256tree │ ├── test_cc.py │ ├── test_generator_deserialize.clvm │ ├── test_generator_deserialize.clvm.hex │ ├── test_generator_deserialize.clvm.hex.sha256tree │ ├── test_multiple_generator_input_arguments.clvm │ ├── test_multiple_generator_input_arguments.clvm.hex │ └── test_multiple_generator_input_arguments.clvm.hex.sha256tree │ ├── rl_wallet │ ├── __init__.py │ ├── rl_wallet.py │ └── rl_wallet_puzzles.py │ ├── secret_key_store.py │ ├── settings │ ├── default_settings.py │ ├── settings_objects.py │ └── user_settings.py │ ├── sign_coin_solutions.py │ ├── trade_manager.py │ ├── trade_record.py │ ├── trading │ ├── __init__.py │ ├── trade_status.py │ └── trade_store.py │ ├── transaction_record.py │ ├── util │ ├── __init__.py │ ├── backup_utils.py │ ├── trade_utils.py │ ├── transaction_type.py │ └── wallet_types.py │ ├── wallet.py │ ├── wallet_action.py │ ├── wallet_action_store.py │ ├── wallet_block_store.py │ ├── wallet_blockchain.py │ ├── wallet_coin_record.py │ ├── wallet_coin_store.py │ ├── wallet_info.py │ ├── wallet_node.py │ ├── wallet_node_api.py │ ├── wallet_puzzle_store.py │ ├── wallet_state_manager.py │ ├── wallet_sync_store.py │ ├── wallet_transaction_store.py │ └── wallet_user_store.py ├── install-gui.sh ├── install-timelord.sh ├── install.sh ├── mypy.ini ├── pyproject.toml ├── run-py-tests.sh ├── setup.py └── tests ├── README.md ├── __init__.py ├── blockchain ├── __init__.py ├── config.py ├── test_blockchain.py └── test_blockchain_transactions.py ├── build-workflows.py ├── clvm ├── __init__.py ├── coin_store.py ├── config.py ├── test_clvm_compilation.py ├── test_equalitylisp_deserialization.py ├── test_puzzles.py └── test_serialized_program.py ├── connection_utils.py ├── core ├── __init__.py ├── consensus │ ├── __init__.py │ └── test_pot_iterations.py ├── daemon │ └── test_daemon.py ├── fixtures.py ├── full_node │ ├── __init__.py │ ├── config.py │ ├── dos │ │ ├── __init__.py │ │ └── config.py │ ├── full_sync │ │ ├── __init__.py │ │ ├── config.py │ │ └── test_full_sync.py │ ├── ram_db.py │ ├── test_address_manager.py │ ├── test_block_store.py │ ├── test_coin_store.py │ ├── test_conditions.py │ ├── test_full_node.py │ ├── test_full_node_store.py │ ├── test_initial_freeze.py │ ├── test_mempool.py │ ├── test_mempool_performance.py │ ├── test_node_load.py │ ├── test_performance.py │ ├── test_sync_store.py │ └── test_transactions.py ├── make_block_generator.py ├── node_height.py ├── server │ ├── test_dos.py │ └── test_rate_limits.py ├── ssl │ └── test_ssl.py ├── test_cost_calculation.py ├── test_farmer_harvester_rpc.py ├── test_filter.py ├── test_full_node_rpc.py ├── test_merkle_set.py ├── test_setproctitle.py ├── types │ ├── __init__.py │ ├── test_coin.py │ └── test_proof_of_space.py └── util │ ├── __init__.py │ ├── test_keychain.py │ ├── test_lru_cache.py │ ├── test_significant_bits.py │ ├── test_streamable.py │ └── test_type_checking.py ├── equality-start-sim ├── generator ├── test_compression.py ├── test_generator_types.py ├── test_rom.py └── test_scan.py ├── pytest.ini ├── runner-templates ├── build-test-macos ├── build-test-ubuntu ├── checkout-test-plots.include.yml └── install-timelord.include.yml ├── setup_nodes.py ├── simulation ├── __init__.py ├── config.py └── test_simulation.py ├── testconfig.py ├── time_out_assert.py ├── util ├── __init__.py ├── alert_server.py ├── benchmark_cost.py ├── bip39_test_vectors.json ├── config.py ├── generator_tools_testing.py ├── key_tool.py └── misc.py ├── wallet ├── __init__.py ├── cc_wallet │ ├── __init__.py │ ├── test_cc_wallet.py │ └── test_trades.py ├── did_wallet │ └── test_did.py ├── rl_wallet │ ├── __init__.py │ ├── test_rl_rpc.py │ └── test_rl_wallet.py ├── rpc │ ├── __init__.py │ └── test_wallet_rpc.py ├── sync │ ├── __init__.py │ ├── config.py │ └── test_wallet_sync.py ├── test_backup.py ├── test_bech32m.py ├── test_equalitylisp.py ├── test_puzzle_store.py ├── test_singleton.py ├── test_taproot.py ├── test_wallet.py └── test_wallet_store.py └── weight_proof └── test_weight_proof.py /.flake8: -------------------------------------------------------------------------------- 1 | .github/linters/.flake8 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. (If what you are experiencing is NOT a bug but instead a support issue, please open a Discussion instead!) 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Desktop (please complete the following information):** 28 | 29 | - OS: [e.g. Linux] 30 | - OS Version/Flavor: [e.g. CentOS 7.2] 31 | - CPU: [e.g. Intel Xeon 8175M] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - about: Ask a question or request support here 3 | name: Ask for Support 4 | url: >- 5 | https://github.com/Chia-Network/chia-blockchain/discussions/new?category=support 6 | - about: Request a new feature or idea here 7 | name: Make a Request 8 | url: >- 9 | https://github.com/Chia-Network/chia-blockchain/discussions/new?category=ideas 10 | - about: Get support on the Chia Keybase chat channels. 11 | name: Join the Keybase.io support chat 12 | url: 'https://keybase.io/team/chia_network.public' 13 | -------------------------------------------------------------------------------- /.github/linters/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = ./typings/**/* 4 | ignore = E203,W503 5 | -------------------------------------------------------------------------------- /.github/linters/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile= 3 | 4 | ; vertical hanging indent mode also used in black configuration 5 | multi_line_output = 3 6 | 7 | ; necessary because black expect the trailing comma 8 | include_trailing_comma = true 9 | 10 | ; black compatibility 11 | force_grid_wrap = 0 12 | 13 | ; black compatibility 14 | use_parentheses = True 15 | 16 | ; black compatibility 17 | ensure_newline_before_comments = True 18 | 19 | ; we chose 120 as line length 20 | line_length = 120 21 | -------------------------------------------------------------------------------- /.github/linters/.markdown-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ########################### 3 | ########################### 4 | ## Markdown Linter rules ## 5 | ########################### 6 | ########################### 7 | 8 | # Linter rules doc: 9 | # - https://github.com/DavidAnson/markdownlint 10 | # 11 | # Note: 12 | # To comment out a single error: 13 | # 14 | # any violations you want 15 | # 16 | # 17 | 18 | ############### 19 | # Rules by id # 20 | ############### 21 | MD004: false # Unordered list style 22 | MD007: 23 | indent: 2 # Unordered list indentation 24 | MD013: 25 | line_length: 808 # Line length 26 | MD024: 27 | allow_different_nesting: true 28 | MD026: 29 | punctuation: ".,;:!。,;:" # List of not allowed 30 | MD029: false # Ordered list item prefix 31 | MD033: false # Allow inline HTML 32 | MD036: false # Emphasis used instead of a heading 33 | MD041: false # Allow file to start without h1 34 | 35 | ################# 36 | # Rules by tags # 37 | ################# 38 | blank_lines: false # Error on blank lines 39 | -------------------------------------------------------------------------------- /.github/linters/.python-black: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line_length = 120 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | **/*.o 9 | **/*.DS_Store 10 | 11 | # Database 12 | nohup.out 13 | mongod.log* 14 | fndb_test* 15 | blockchain_test* 16 | *.db 17 | *.db-journal 18 | 19 | # Logs 20 | *.log 21 | *.out 22 | 23 | # Keys and plot files 24 | config/keys.yaml 25 | config/plots.yaml 26 | 27 | # Bundled code 28 | equality-blockchain.tar.gz.tar.gz 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | #*.spec 35 | build_scripts/*.dmg 36 | build_scripts/build 37 | 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | **/*.egg-info 43 | 44 | # Unit test / coverage reports 45 | .cache 46 | .pytest_cache/ 47 | 48 | # PoSpace plots 49 | **/*.dat 50 | **/*.dat.tmp 51 | *.mo 52 | *.pot 53 | 54 | # pyenv 55 | .python-version 56 | .eggs 57 | .venv 58 | venv 59 | activate 60 | 61 | # mypy 62 | .mypy_cache/ 63 | 64 | # Editors 65 | .vscode 66 | .idea 67 | 68 | # Packaging 69 | equality-blockchain.tar.gz 70 | 71 | # Timelord utilities 72 | vdf_bench 73 | 74 | # Node modules 75 | **/node_modules 76 | 77 | # Offer Files 78 | *.offer 79 | 80 | # Backup Files 81 | *.backup 82 | 83 | # Attest Files 84 | *.attest 85 | 86 | # Compiled CLVM 87 | main.sym 88 | *.recompiled 89 | 90 | # Dev config react 91 | # equality-blockchain-gui/src/dev_config.js 92 | # React built app 93 | equality-blockchain-gui/.eslintcache 94 | equality-blockchain-gui/build 95 | build_scripts/dist 96 | build_scripts/*.Dmg 97 | equality-blockchain-gui/src/locales/_build 98 | build_scripts\win_build 99 | build_scripts/win_build 100 | win_code_sign_cert.p12 101 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "equality-blockchain-gui"] 2 | path = equality-blockchain-gui 3 | url = https://github.com/Equality-Network/equality-blockchain-gui.git 4 | branch = master 5 | [submodule "mozilla-ca"] 6 | path = mozilla-ca 7 | url = https://github.com/Equality-Network/mozilla-ca.git 8 | branch = main 9 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | .github/linters/.isort.cfg -------------------------------------------------------------------------------- /BUILD_TIMELORD.md: -------------------------------------------------------------------------------- 1 | # Building timelords 2 | 3 | The Linux and MacOS chiavdf binary wheels currently exclude an executable 4 | required to run a [Timelord](https://github.com/Equality-Network/equality-blockchain/wiki/Timelords). 5 | If you want to run a Timelord on Linux or MacOS, you must install the wheel 6 | from source (which may require some additional development packages) while in 7 | the virtual environment. 8 | 9 | ```bash 10 | . ./activate 11 | 12 | sh install-timelord.sh 13 | ``` 14 | 15 | If the compile fails, it's likely due to a missing dependency. The script 16 | [install-timelord.sh](https://github.com/Equality-Network/equality-blockchain/blob/main/install-timelord.sh) 17 | attempts to install required build dependencies for Linux and MacOS before 18 | invoking pip to build from the source python distribution of chiavdf. 19 | 20 | The `install-timelord.sh` install script leverages two environmental variables 21 | that the chiavdf wheels can use to specify how to build. The service that the 22 | Timelord uses to run the VDF and prove the Proof of Time is `vdf_client` and 23 | `vdf_bench` is a utility to get a sense of a given CPU's iterations per second. 24 | 25 | - To build vdf_client set the environment variable BUILD_VDF_CLIENT to "Y". 26 | `export BUILD_VDF_CLIENT=Y`. 27 | - Similarly, to build vdf_bench set the environment variable BUILD_VDF_BENCH 28 | to "Y". `export BUILD_VDF_BENCH=Y`. 29 | 30 | Building and running Timelords in Windows x86-64 is not yet supported. 31 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Install instructions have been moved to the [INSTALL](https://github.com/Equality-Network/equality-blockchain/wiki/INSTALL) section of the repository [Wiki](https://github.com/Equality-Network/equality-blockchain/wiki). 4 | 5 | After installing, follow the remaining instructions in the 6 | [Quick Start Guide](https://github.com/Equality-Network/equality-blockchain/wiki/Quick-Start-Guide) 7 | to run the software. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # equality-blockchain 2 | 3 | | Current Release/main | 4 | | :---: 5 | | [![Ubuntu Core Tests](https://github.com/Equality-Network/Equality-blockchain/actions/workflows/build-linux-installer.yml/badge.svg)](https://github.com/Equality-Network/Equality-blockchain/actions/workflows/build-linux-installer.yml) [![MacOS Core Tests](https://github.com/Equality-Network/Equality-blockchain/actions/workflows/build-macos-installer.yml/badge.svg)](https://github.com/Equality-Network/Equality-blockchain/actions/workflows/build-macos-installer.yml) [![Windows Installer on Windows 10 and Python 3.7](https://github.com/Equality-Network/Equality-blockchain/actions/workflows/build-windows-installer.yml/badge.svg)](https://github.com/Equality-Network/Equality-blockchain/actions/workflows/build-windows-installer.yml) | 6 | 7 | ![GitHub contributors](https://img.shields.io/github/contributors/Equality-Network/Equality-blockchain?logo=GitHub) 8 | 9 | **Equality** is a modern community-centric green cryptocurrency based on a proof-of-space-and-time consensus algorithm. It is a community-supported fork of the [Chia Network](https://github.com/Chia-Network/chia-blockchain) codebase. 10 | 11 | For more information, see our website and downloads at [Equality Network](https://www.equalitychain.org.) 12 | 13 | 14 | https://discord.gg/HJ4N7zZN 15 | -------------------------------------------------------------------------------- /equality/__init__.py: -------------------------------------------------------------------------------- 1 | from pkg_resources import DistributionNotFound, get_distribution, resource_filename 2 | 3 | try: 4 | __version__ = get_distribution("equality-blockchain").version 5 | except DistributionNotFound: 6 | # package is not installed 7 | __version__ = "unknown" 8 | 9 | PYINSTALLER_SPEC_PATH = resource_filename("equality", "pyinstaller.spec") 10 | -------------------------------------------------------------------------------- /equality/cmds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/cmds/__init__.py -------------------------------------------------------------------------------- /equality/cmds/init.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | 4 | @click.command("init", short_help="Create or migrate the configuration") 5 | @click.option( 6 | "--create-certs", 7 | "-c", 8 | default=None, 9 | help="Create new SSL certificates based on CA in [directory]", 10 | type=click.Path(), 11 | ) 12 | @click.pass_context 13 | def init_cmd(ctx: click.Context, create_certs: str): 14 | """ 15 | Create a new configuration or migrate from previous versions to current 16 | 17 | \b 18 | Follow these steps to create new certificates for a remote harvester: 19 | - Make a copy of your Farming Machine CA directory: ~/.equality/[version]/config/ssl/ca 20 | - Shut down all equality daemon processes with `equality stop all -d` 21 | - Run `equality init -c [directory]` on your remote harvester, 22 | where [directory] is the the copy of your Farming Machine CA directory 23 | - Get more details on remote harvester on Equality wiki: 24 | https://github.com/Equality-Network/equality-blockchain/wiki/Farming-on-many-machines 25 | """ 26 | from pathlib import Path 27 | from .init_funcs import init 28 | 29 | init(Path(create_certs) if create_certs is not None else None, ctx.obj["root_path"]) 30 | 31 | 32 | if __name__ == "__main__": 33 | from .init_funcs import equality_init 34 | from equality.util.default_root import DEFAULT_ROOT_PATH 35 | 36 | equality_init(DEFAULT_ROOT_PATH) 37 | -------------------------------------------------------------------------------- /equality/cmds/netspace.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | 4 | @click.command("netspace", short_help="Estimate total farmed space on the network") 5 | @click.option( 6 | "-p", 7 | "--rpc-port", 8 | help=( 9 | "Set the port where the Full Node is hosting the RPC interface. " 10 | "See the rpc_port under full_node in config.yaml. " 11 | "[default: 8555]" 12 | ), 13 | type=int, 14 | default=None, 15 | ) 16 | @click.option( 17 | "-d", 18 | "--delta-block-height", 19 | help=( 20 | "Compare a block X blocks older to estimate total network space. " 21 | "Defaults to 4608 blocks (~1 day) and Peak block as the starting block. " 22 | "Use --start BLOCK_HEIGHT to specify starting block. " 23 | "Use 192 blocks to estimate over the last hour." 24 | ), 25 | type=str, 26 | default="4608", 27 | ) 28 | @click.option( 29 | "-s", 30 | "--start", 31 | help="Newest block used to calculate estimated total network space. Defaults to Peak block.", 32 | type=str, 33 | default="", 34 | ) 35 | def netspace_cmd(rpc_port: int, delta_block_height: str, start: str) -> None: 36 | """ 37 | Calculates the estimated space on the network given two block header hashes. 38 | """ 39 | import asyncio 40 | from .netspace_funcs import netstorge_async 41 | 42 | asyncio.run(netstorge_async(rpc_port, delta_block_height, start)) 43 | -------------------------------------------------------------------------------- /equality/cmds/start.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from equality.util.service_groups import all_groups 4 | 5 | 6 | @click.command("start", short_help="Start service groups") 7 | @click.option("-r", "--restart", is_flag=True, type=bool, help="Restart running services") 8 | @click.argument("group", type=click.Choice(all_groups()), nargs=-1, required=True) 9 | @click.pass_context 10 | def start_cmd(ctx: click.Context, restart: bool, group: str) -> None: 11 | import asyncio 12 | from .start_funcs import async_start 13 | 14 | asyncio.get_event_loop().run_until_complete(async_start(ctx.obj["root_path"], group, restart)) 15 | -------------------------------------------------------------------------------- /equality/cmds/stop.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import click 5 | 6 | from equality.util.service_groups import all_groups, services_for_groups 7 | 8 | 9 | async def async_stop(root_path: Path, group: str, stop_daemon: bool) -> int: 10 | from equality.daemon.client import connect_to_daemon_and_validate 11 | 12 | daemon = await connect_to_daemon_and_validate(root_path) 13 | if daemon is None: 14 | print("Couldn't connect to equality daemon") 15 | return 1 16 | 17 | if stop_daemon: 18 | r = await daemon.exit() 19 | await daemon.close() 20 | print(f"daemon: {r}") 21 | return 0 22 | 23 | return_val = 0 24 | 25 | for service in services_for_groups(group): 26 | print(f"{service}: ", end="", flush=True) 27 | if not await daemon.is_running(service_name=service): 28 | print("Not running") 29 | elif await daemon.stop_service(service_name=service): 30 | print("Stopped") 31 | else: 32 | print("Stop failed") 33 | return_val = 1 34 | 35 | await daemon.close() 36 | return return_val 37 | 38 | 39 | @click.command("stop", short_help="Stop services") 40 | @click.option("-d", "--daemon", is_flag=True, type=bool, help="Stop daemon") 41 | @click.argument("group", type=click.Choice(all_groups()), nargs=-1, required=True) 42 | @click.pass_context 43 | def stop_cmd(ctx: click.Context, daemon: bool, group: str) -> None: 44 | import asyncio 45 | 46 | sys.exit(asyncio.get_event_loop().run_until_complete(async_stop(ctx.obj["root_path"], group, daemon))) 47 | -------------------------------------------------------------------------------- /equality/cmds/units.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | # The rest of the codebase uses mojos everywhere. Only uses these units 4 | # for user facing interfaces 5 | units: Dict[str, int] = { 6 | "equality": 10 ** 12, # 1 equality (XEQ) is 1,000,000,000,000 mojo (1 Trillion) 7 | "mojo:": 1, 8 | "colouredcoin": 10 ** 3, # 1 coloured coin is 1000 colouredcoin mojos 9 | } 10 | -------------------------------------------------------------------------------- /equality/consensus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/consensus/__init__.py -------------------------------------------------------------------------------- /equality/consensus/block_root_validation.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Optional 2 | 3 | from equality.types.blockchain_format.coin import Coin, hash_coin_list 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.errors import Err 6 | from equality.util.merkle_set import MerkleSet 7 | 8 | 9 | def validate_block_merkle_roots( 10 | block_additions_root: bytes32, 11 | block_removals_root: bytes32, 12 | tx_additions: List[Coin] = None, 13 | tx_removals: List[bytes32] = None, 14 | ) -> Optional[Err]: 15 | if tx_removals is None: 16 | tx_removals = [] 17 | if tx_additions is None: 18 | tx_additions = [] 19 | removal_merkle_set = MerkleSet() 20 | addition_merkle_set = MerkleSet() 21 | 22 | # Create removal Merkle set 23 | for coin_name in tx_removals: 24 | removal_merkle_set.add_already_hashed(coin_name) 25 | 26 | # Create addition Merkle set 27 | puzzlehash_coins_map: Dict[bytes32, List[Coin]] = {} 28 | 29 | for coin in tx_additions: 30 | if coin.puzzle_hash in puzzlehash_coins_map: 31 | puzzlehash_coins_map[coin.puzzle_hash].append(coin) 32 | else: 33 | puzzlehash_coins_map[coin.puzzle_hash] = [coin] 34 | 35 | # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash 36 | for puzzle, coins in puzzlehash_coins_map.items(): 37 | addition_merkle_set.add_already_hashed(puzzle) 38 | addition_merkle_set.add_already_hashed(hash_coin_list(coins)) 39 | 40 | additions_root = addition_merkle_set.get_root() 41 | removals_root = removal_merkle_set.get_root() 42 | 43 | if block_additions_root != additions_root: 44 | return Err.BAD_ADDITION_ROOT 45 | if block_removals_root != removals_root: 46 | return Err.BAD_REMOVAL_ROOT 47 | 48 | return None 49 | -------------------------------------------------------------------------------- /equality/consensus/coinbase.py: -------------------------------------------------------------------------------- 1 | from blspy import G1Element 2 | 3 | from equality.types.blockchain_format.coin import Coin 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.ints import uint32, uint64 6 | from equality.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_for_pk 7 | 8 | 9 | def create_puzzlehash_for_pk(pub_key: G1Element) -> bytes32: 10 | return puzzle_for_pk(pub_key).get_tree_hash() 11 | 12 | 13 | def pool_parent_id(block_height: uint32, genesis_challenge: bytes32) -> uint32: 14 | return bytes32(genesis_challenge[:16] + block_height.to_bytes(16, "big")) 15 | 16 | 17 | def farmer_parent_id(block_height: uint32, genesis_challenge: bytes32) -> uint32: 18 | return bytes32(genesis_challenge[16:] + block_height.to_bytes(16, "big")) 19 | 20 | 21 | def create_pool_coin(block_height: uint32, puzzle_hash: bytes32, reward: uint64, genesis_challenge: bytes32): 22 | parent_id = pool_parent_id(block_height, genesis_challenge) 23 | return Coin(parent_id, puzzle_hash, reward) 24 | 25 | 26 | def create_farmer_coin(block_height: uint32, puzzle_hash: bytes32, reward: uint64, genesis_challenge: bytes32): 27 | parent_id = farmer_parent_id(block_height, genesis_challenge) 28 | return Coin(parent_id, puzzle_hash, reward) 29 | -------------------------------------------------------------------------------- /equality/consensus/condition_costs.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class ConditionCost(Enum): 5 | # Condition Costs 6 | AGG_SIG = 1200000 # the cost of one G1 subgroup check + aggregated signature validation 7 | CREATE_COIN = 1800000 8 | ASSERT_MY_COIN_ID = 0 9 | ASSERT_SECONDS_RELATIVE = 0 10 | ASSERT_SECONDS_ABSOLUTE = 0 11 | ASSERT_HEIGHT_RELATIVE = 0 12 | ASSERT_HEIGHT_ABSOLUTE = 0 13 | RESERVE_FEE = 0 14 | CREATE_COIN_ANNOUNCEMENT = 0 15 | ASSERT_COIN_ANNOUNCEMENT = 0 16 | CREATE_PUZZLE_ANNOUNCEMENT = 0 17 | ASSERT_PUZZLE_ANNOUNCEMENT = 0 18 | -------------------------------------------------------------------------------- /equality/consensus/find_fork_point.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from equality.consensus.block_record import BlockRecord 4 | from equality.consensus.blockchain_interface import BlockchainInterface 5 | from equality.types.header_block import HeaderBlock 6 | 7 | 8 | def find_fork_point_in_chain( 9 | blocks: BlockchainInterface, 10 | block_1: Union[BlockRecord, HeaderBlock], 11 | block_2: Union[BlockRecord, HeaderBlock], 12 | ) -> int: 13 | """Tries to find height where new chain (block_2) diverged from block_1 (assuming prev blocks 14 | are all included in chain) 15 | Returns -1 if chains have no common ancestor 16 | * assumes the fork point is loaded in blocks 17 | """ 18 | while block_2.height > 0 or block_1.height > 0: 19 | if block_2.height > block_1.height: 20 | block_2 = blocks.block_record(block_2.prev_hash) 21 | elif block_1.height > block_2.height: 22 | block_1 = blocks.block_record(block_1.prev_hash) 23 | else: 24 | if block_2.header_hash == block_1.header_hash: 25 | return block_2.height 26 | block_2 = blocks.block_record(block_2.prev_hash) 27 | block_1 = blocks.block_record(block_1.prev_hash) 28 | if block_2 != block_1: 29 | # All blocks are different 30 | return -1 31 | 32 | # First block is the same 33 | return 0 34 | -------------------------------------------------------------------------------- /equality/consensus/network_type.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | 4 | class NetworkType(IntEnum): 5 | MAINNET = 0 6 | TESTNET = 1 7 | -------------------------------------------------------------------------------- /equality/consensus/pos_quality.py: -------------------------------------------------------------------------------- 1 | from equality.util.ints import uint64 2 | 3 | # The actual space in bytes of a plot, is _expected_plot_size(k) * UI_ACTUAL_SPACE_CONSTANT_FACTO 4 | # This is not used in consensus, only for display purposes 5 | UI_ACTUAL_SPACE_CONSTANT_FACTOR = 0.762 6 | 7 | 8 | def _expected_plot_size(k: int) -> uint64: 9 | """ 10 | Given the plot size parameter k (which is between 32 and 59), computes the 11 | expected size of the plot in bytes (times a constant factor). This is based on efficient encoding 12 | of the plot, and aims to be scale agnostic, so larger plots don't 13 | necessarily get more rewards per byte. The +1 is added to give half a bit more space per entry, which 14 | is necessary to store the entries in the plot. 15 | """ 16 | 17 | return ((2 * k) + 1) * (2 ** (k - 1)) 18 | -------------------------------------------------------------------------------- /equality/daemon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/daemon/__init__.py -------------------------------------------------------------------------------- /equality/daemon/windows_signal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Code taken from Stack Overflow Eryk Sun. 3 | https://stackoverflow.com/questions/35772001/how-to-handle-the-signal-in-python-on-windows-machine 4 | """ 5 | 6 | import os 7 | import signal 8 | import sys 9 | 10 | if sys.platform != "win32" and sys.platform != "cygwin": 11 | kill = os.kill 12 | else: 13 | # adapt the conflated API on Windows. 14 | import threading 15 | 16 | sigmap = { 17 | signal.SIGINT: signal.CTRL_C_EVENT, # pylint: disable=E1101 18 | signal.SIGBREAK: signal.CTRL_BREAK_EVENT, # pylint: disable=E1101 19 | } 20 | 21 | def kill(pid, signum): 22 | if signum in sigmap and pid == os.getpid(): 23 | # we don't know if the current process is a 24 | # process group leader, so just broadcast 25 | # to all processes attached to this console. 26 | pid = 0 27 | thread = threading.current_thread() 28 | handler = signal.getsignal(signum) 29 | # work around the synchronization problem when calling 30 | # kill from the main thread. 31 | if signum in sigmap and thread.name == "MainThread" and callable(handler) and pid == 0: 32 | event = threading.Event() 33 | 34 | def handler_set_event(signum, frame): 35 | event.set() 36 | return handler(signum, frame) 37 | 38 | signal.signal(signum, handler_set_event) 39 | try: 40 | os.kill(pid, sigmap[signum]) 41 | # busy wait because we can't block in the main 42 | # thread, else the signal handler can't execute. 43 | while not event.is_set(): 44 | pass 45 | finally: 46 | signal.signal(signum, handler) 47 | else: 48 | os.kill(pid, sigmap.get(signum, signum)) 49 | -------------------------------------------------------------------------------- /equality/farmer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/farmer/__init__.py -------------------------------------------------------------------------------- /equality/full_node/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/full_node/__init__.py -------------------------------------------------------------------------------- /equality/full_node/signage_point.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from equality.types.blockchain_format.vdf import VDFInfo, VDFProof 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | @dataclass(frozen=True) 9 | @streamable 10 | class SignagePoint(Streamable): 11 | cc_vdf: Optional[VDFInfo] 12 | cc_proof: Optional[VDFProof] 13 | rc_vdf: Optional[VDFInfo] 14 | rc_proof: Optional[VDFProof] 15 | -------------------------------------------------------------------------------- /equality/harvester/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/harvester/__init__.py -------------------------------------------------------------------------------- /equality/introducer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/introducer/__init__.py -------------------------------------------------------------------------------- /equality/introducer/introducer_api.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Optional 2 | 3 | from equality.introducer.introducer import Introducer 4 | from equality.protocols.introducer_protocol import RequestPeersIntroducer, RespondPeersIntroducer 5 | from equality.protocols.protocol_message_types import ProtocolMessageTypes 6 | from equality.server.outbound_message import Message, make_msg 7 | from equality.server.ws_connection import WSEqualityConnection 8 | from equality.types.peer_info import TimestampedPeerInfo 9 | from equality.util.api_decorators import api_request, peer_required 10 | from equality.util.ints import uint64 11 | 12 | 13 | class IntroducerAPI: 14 | introducer: Introducer 15 | 16 | def __init__(self, introducer) -> None: 17 | self.introducer = introducer 18 | 19 | def _set_state_changed_callback(self, callback: Callable): 20 | pass 21 | 22 | @peer_required 23 | @api_request 24 | async def request_peers_introducer( 25 | self, 26 | request: RequestPeersIntroducer, 27 | peer: WSEqualityConnection, 28 | ) -> Optional[Message]: 29 | max_peers = self.introducer.max_peers_to_send 30 | if self.introducer.server is None or self.introducer.server.introducer_peers is None: 31 | return None 32 | rawpeers = self.introducer.server.introducer_peers.get_peers( 33 | max_peers * 5, True, self.introducer.recent_peer_threshold 34 | ) 35 | 36 | peers = [] 37 | for r_peer in rawpeers: 38 | if r_peer.vetted <= 0: 39 | continue 40 | 41 | if r_peer.host == peer.peer_host and r_peer.port == peer.peer_server_port: 42 | continue 43 | peer_without_timestamp = TimestampedPeerInfo( 44 | r_peer.host, 45 | r_peer.port, 46 | uint64(0), 47 | ) 48 | peers.append(peer_without_timestamp) 49 | 50 | if len(peers) >= max_peers: 51 | break 52 | 53 | self.introducer.log.info(f"Sending vetted {peers}") 54 | 55 | msg = make_msg(ProtocolMessageTypes.respond_peers_introducer, RespondPeersIntroducer(peers)) 56 | return msg 57 | -------------------------------------------------------------------------------- /equality/protocols/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/protocols/__init__.py -------------------------------------------------------------------------------- /equality/protocols/farmer_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from blspy import G2Element 5 | 6 | from equality.types.blockchain_format.pool_target import PoolTarget 7 | from equality.types.blockchain_format.proof_of_space import ProofOfSpace 8 | from equality.types.blockchain_format.sized_bytes import bytes32 9 | from equality.util.ints import uint8, uint32, uint64 10 | from equality.util.streamable import Streamable, streamable 11 | 12 | """ 13 | Protocol between farmer and full node. 14 | Note: When changing this file, also change protocol_message_types.py, and the protocol version in shared_protocol.py 15 | """ 16 | 17 | 18 | @dataclass(frozen=True) 19 | @streamable 20 | class NewSignagePoint(Streamable): 21 | challenge_hash: bytes32 22 | challenge_chain_sp: bytes32 23 | reward_chain_sp: bytes32 24 | difficulty: uint64 25 | sub_slot_iters: uint64 26 | signage_point_index: uint8 27 | 28 | 29 | @dataclass(frozen=True) 30 | @streamable 31 | class DeclareProofOfSpace(Streamable): 32 | challenge_hash: bytes32 33 | challenge_chain_sp: bytes32 34 | signage_point_index: uint8 35 | reward_chain_sp: bytes32 36 | proof_of_space: ProofOfSpace 37 | challenge_chain_sp_signature: G2Element 38 | reward_chain_sp_signature: G2Element 39 | farmer_puzzle_hash: bytes32 40 | pool_target: Optional[PoolTarget] 41 | pool_signature: Optional[G2Element] 42 | 43 | 44 | @dataclass(frozen=True) 45 | @streamable 46 | class RequestSignedValues(Streamable): 47 | quality_string: bytes32 48 | foliage_block_data_hash: bytes32 49 | foliage_transaction_block_hash: bytes32 50 | 51 | 52 | @dataclass(frozen=True) 53 | @streamable 54 | class FarmingInfo(Streamable): 55 | challenge_hash: bytes32 56 | sp_hash: bytes32 57 | timestamp: uint64 58 | passed: uint32 59 | proofs: uint32 60 | total_plots: uint32 61 | 62 | 63 | @dataclass(frozen=True) 64 | @streamable 65 | class SignedValues(Streamable): 66 | quality_string: bytes32 67 | foliage_block_data_signature: G2Element 68 | foliage_transaction_block_signature: G2Element 69 | -------------------------------------------------------------------------------- /equality/protocols/harvester_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Tuple 3 | 4 | from blspy import G1Element, G2Element 5 | 6 | from equality.types.blockchain_format.proof_of_space import ProofOfSpace 7 | from equality.types.blockchain_format.sized_bytes import bytes32 8 | from equality.util.ints import uint8, uint64 9 | from equality.util.streamable import Streamable, streamable 10 | 11 | """ 12 | Protocol between harvester and farmer. 13 | Note: When changing this file, also change protocol_message_types.py, and the protocol version in shared_protocol.py 14 | """ 15 | 16 | 17 | @dataclass(frozen=True) 18 | @streamable 19 | class HarvesterHandshake(Streamable): 20 | farmer_public_keys: List[G1Element] 21 | pool_public_keys: List[G1Element] 22 | 23 | 24 | @dataclass(frozen=True) 25 | @streamable 26 | class NewSignagePointHarvester(Streamable): 27 | challenge_hash: bytes32 28 | difficulty: uint64 29 | sub_slot_iters: uint64 30 | signage_point_index: uint8 31 | sp_hash: bytes32 32 | 33 | 34 | @dataclass(frozen=True) 35 | @streamable 36 | class NewProofOfSpace(Streamable): 37 | challenge_hash: bytes32 38 | sp_hash: bytes32 39 | plot_identifier: str 40 | proof: ProofOfSpace 41 | signage_point_index: uint8 42 | 43 | 44 | @dataclass(frozen=True) 45 | @streamable 46 | class RequestSignatures(Streamable): 47 | plot_identifier: str 48 | challenge_hash: bytes32 49 | sp_hash: bytes32 50 | messages: List[bytes32] 51 | 52 | 53 | @dataclass(frozen=True) 54 | @streamable 55 | class RespondSignatures(Streamable): 56 | plot_identifier: str 57 | challenge_hash: bytes32 58 | sp_hash: bytes32 59 | local_pk: G1Element 60 | farmer_pk: G1Element 61 | message_signatures: List[Tuple[bytes32, G2Element]] 62 | -------------------------------------------------------------------------------- /equality/protocols/introducer_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from equality.types.peer_info import TimestampedPeerInfo 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | """ 8 | Protocol to introducer 9 | Note: When changing this file, also change protocol_message_types.py, and the protocol version in shared_protocol.py 10 | """ 11 | 12 | 13 | @dataclass(frozen=True) 14 | @streamable 15 | class RequestPeersIntroducer(Streamable): 16 | """ 17 | Return full list of peers 18 | """ 19 | 20 | 21 | @dataclass(frozen=True) 22 | @streamable 23 | class RespondPeersIntroducer(Streamable): 24 | peer_list: List[TimestampedPeerInfo] 25 | -------------------------------------------------------------------------------- /equality/protocols/pool_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional 3 | 4 | from equality.types.blockchain_format.proof_of_space import ProofOfSpace 5 | from equality.util.ints import uint32, uint64 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | """ 9 | Protocol between farmer and pool. 10 | Note: When changing this file, also change protocol_message_types.py, and the protocol version in shared_protocol.py 11 | """ 12 | 13 | 14 | @dataclass(frozen=True) 15 | @streamable 16 | class SignedCoinbase(Streamable): 17 | pass 18 | # coinbase_signature: PrependSignature 19 | 20 | 21 | @dataclass(frozen=True) 22 | @streamable 23 | class RequestData(Streamable): 24 | min_height: Optional[uint32] 25 | farmer_id: Optional[str] 26 | 27 | 28 | @dataclass(frozen=True) 29 | @streamable 30 | class RespondData(Streamable): 31 | posting_url: str 32 | # pool_public_key: PublicKey 33 | partials_threshold: uint64 34 | coinbase_info: List[SignedCoinbase] 35 | 36 | 37 | @dataclass(frozen=True) 38 | @streamable 39 | class Partial(Streamable): 40 | # challenge: Challenge 41 | proof_of_space: ProofOfSpace 42 | farmer_target: str 43 | # Signature of the challenge + farmer target hash 44 | # signature: PrependSignature 45 | 46 | 47 | @dataclass(frozen=True) 48 | @streamable 49 | class PartialAck(Streamable): 50 | pass 51 | -------------------------------------------------------------------------------- /equality/protocols/shared_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import IntEnum 3 | from typing import List, Tuple 4 | 5 | from equality.util.ints import uint8, uint16 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | protocol_version = "0.0.32" 9 | 10 | """ 11 | Handshake when establishing a connection between two servers. 12 | Note: When changing this file, also change protocol_message_types.py 13 | """ 14 | 15 | 16 | # Capabilities can be added here when new features are added to the protocol 17 | # These are passed in as uint16 into the Handshake 18 | class Capability(IntEnum): 19 | BASE = 1 # Base capability just means it supports the equality protocol at mainnet 20 | 21 | 22 | @dataclass(frozen=True) 23 | @streamable 24 | class Handshake(Streamable): 25 | network_id: str 26 | protocol_version: str 27 | software_version: str 28 | server_port: uint16 29 | node_type: uint8 30 | capabilities: List[Tuple[uint16, str]] 31 | -------------------------------------------------------------------------------- /equality/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/rpc/__init__.py -------------------------------------------------------------------------------- /equality/rpc/farmer_rpc_client.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Optional 2 | 3 | from equality.rpc.rpc_client import RpcClient 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | 6 | 7 | class FarmerRpcClient(RpcClient): 8 | """ 9 | Client to Equality RPC, connects to a local farmer. Uses HTTP/JSON, and converts back from 10 | JSON into native python objects before returning. All api calls use POST requests. 11 | Note that this is not the same as the peer protocol, or wallet protocol (which run Equality's 12 | protocol on top of TCP), it's a separate protocol on top of HTTP that provides easy access 13 | to the full node. 14 | """ 15 | 16 | async def get_signage_point(self, sp_hash: bytes32) -> Optional[Dict]: 17 | try: 18 | return await self.fetch("get_signage_point", {"sp_hash": sp_hash.hex()}) 19 | except ValueError: 20 | return None 21 | 22 | async def get_signage_points(self) -> List[Dict]: 23 | return (await self.fetch("get_signage_points", {}))["signage_points"] 24 | 25 | async def get_reward_targets(self, search_for_private_key: bool) -> Dict: 26 | response = await self.fetch("get_reward_targets", {"search_for_private_key": search_for_private_key}) 27 | return_dict = { 28 | "farmer_target": response["farmer_target"], 29 | "pool_target": response["pool_target"], 30 | } 31 | if "have_pool_sk" in response: 32 | return_dict["have_pool_sk"] = response["have_pool_sk"] 33 | if "have_farmer_sk" in response: 34 | return_dict["have_farmer_sk"] = response["have_farmer_sk"] 35 | return return_dict 36 | 37 | async def set_reward_targets(self, farmer_target: Optional[str] = None, pool_target: Optional[str] = None) -> Dict: 38 | request = {} 39 | if farmer_target is not None: 40 | request["farmer_target"] = farmer_target 41 | if pool_target is not None: 42 | request["pool_target"] = pool_target 43 | return await self.fetch("set_reward_targets", request) 44 | -------------------------------------------------------------------------------- /equality/rpc/harvester_rpc_client.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List 2 | 3 | from equality.rpc.rpc_client import RpcClient 4 | 5 | 6 | class HarvesterRpcClient(RpcClient): 7 | """ 8 | Client to Equality RPC, connects to a local harvester. Uses HTTP/JSON, and converts back from 9 | JSON into native python objects before returning. All api calls use POST requests. 10 | Note that this is not the same as the peer protocol, or wallet protocol (which run Equality's 11 | protocol on top of TCP), it's a separate protocol on top of HTTP thats provides easy access 12 | to the full node. 13 | """ 14 | 15 | async def get_plots(self) -> Dict[str, Any]: 16 | return await self.fetch("get_plots", {}) 17 | 18 | async def refresh_plots(self) -> None: 19 | await self.fetch("refresh_plots", {}) 20 | 21 | async def delete_plot(self, filename: str) -> bool: 22 | return await self.fetch("delete_plot", {"filename": filename}) 23 | 24 | async def add_plot_directory(self, dirname: str) -> bool: 25 | return (await self.fetch("add_plot_directory", {"dirname": dirname}))["success"] 26 | 27 | async def get_plot_directories(self) -> List[str]: 28 | return (await self.fetch("get_plot_directories", {}))["directories"] 29 | 30 | async def remove_plot_directory(self, dirname: str) -> bool: 31 | return (await self.fetch("remove_plot_directory", {"dirname": dirname}))["success"] 32 | -------------------------------------------------------------------------------- /equality/server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/server/__init__.py -------------------------------------------------------------------------------- /equality/server/outbound_message.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import IntEnum 3 | from typing import Any, Optional 4 | 5 | from equality.protocols.protocol_message_types import ProtocolMessageTypes 6 | from equality.util.ints import uint8, uint16 7 | from equality.util.streamable import Streamable, streamable 8 | 9 | 10 | class NodeType(IntEnum): 11 | FULL_NODE = 1 12 | HARVESTER = 2 13 | FARMER = 3 14 | TIMELORD = 4 15 | INTRODUCER = 5 16 | WALLET = 6 17 | 18 | 19 | class Delivery(IntEnum): 20 | # A message is sent to the same peer that we received a message from 21 | RESPOND = 1 22 | # A message is sent to all peers 23 | BROADCAST = 2 24 | # A message is sent to all peers except the one from which we received the API call 25 | BROADCAST_TO_OTHERS = 3 26 | # A message is sent to a random peer 27 | RANDOM = 4 28 | # Pseudo-message to close the current connection 29 | CLOSE = 5 30 | # A message is sent to a speicific peer 31 | SPECIFIC = 6 32 | 33 | 34 | @dataclass(frozen=True) 35 | @streamable 36 | class Message(Streamable): 37 | type: uint8 # one of ProtocolMessageTypes 38 | # message id 39 | id: Optional[uint16] 40 | # Message data for that type 41 | data: bytes 42 | 43 | 44 | def make_msg(msg_type: ProtocolMessageTypes, data: Any) -> Message: 45 | return Message(uint8(msg_type.value), None, bytes(data)) 46 | -------------------------------------------------------------------------------- /equality/server/reconnect_task.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import socket 3 | 4 | from equality.server.server import EqualityServer 5 | from equality.types.peer_info import PeerInfo 6 | 7 | 8 | def start_reconnect_task(server: EqualityServer, peer_info_arg: PeerInfo, log, auth: bool): 9 | """ 10 | Start a background task that checks connection and reconnects periodically to a peer. 11 | """ 12 | peer_info = PeerInfo(socket.gethostbyname(peer_info_arg.host), peer_info_arg.port) 13 | 14 | async def connection_check(): 15 | while True: 16 | peer_retry = True 17 | for _, connection in server.all_connections.items(): 18 | if connection.get_peer_info() == peer_info or connection.get_peer_info() == peer_info_arg: 19 | peer_retry = False 20 | if peer_retry: 21 | log.info(f"Reconnecting to peer {peer_info}") 22 | try: 23 | await server.start_client(peer_info, None, auth=auth) 24 | except Exception as e: 25 | log.info(f"Failed to connect to {peer_info} {e}") 26 | await asyncio.sleep(3) 27 | 28 | return asyncio.create_task(connection_check()) 29 | -------------------------------------------------------------------------------- /equality/server/ssl_context.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Dict 3 | 4 | 5 | def public_ssl_paths(path: Path, config: Dict): 6 | return ( 7 | path / config["ssl"]["public_crt"], 8 | path / config["ssl"]["public_key"], 9 | ) 10 | 11 | 12 | def private_ssl_paths(path: Path, config: Dict): 13 | return ( 14 | path / config["ssl"]["private_crt"], 15 | path / config["ssl"]["private_key"], 16 | ) 17 | 18 | 19 | def private_ssl_ca_paths(path: Path, config: Dict): 20 | return ( 21 | path / config["private_ssl_ca"]["crt"], 22 | path / config["private_ssl_ca"]["key"], 23 | ) 24 | 25 | 26 | def equality_ssl_ca_paths(path: Path, config: Dict): 27 | return ( 28 | path / config["equality_ssl_ca"]["crt"], 29 | path / config["equality_ssl_ca"]["key"], 30 | ) 31 | -------------------------------------------------------------------------------- /equality/server/start_harvester.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from typing import Dict 3 | 4 | from equality.consensus.constants import ConsensusConstants 5 | from equality.consensus.default_constants import DEFAULT_CONSTANTS 6 | from equality.harvester.harvester import Harvester 7 | from equality.harvester.harvester_api import HarvesterAPI 8 | from equality.rpc.harvester_rpc_api import HarvesterRpcApi 9 | from equality.server.outbound_message import NodeType 10 | from equality.server.start_service import run_service 11 | from equality.types.peer_info import PeerInfo 12 | from equality.util.config import load_config_cli 13 | from equality.util.default_root import DEFAULT_ROOT_PATH 14 | 15 | # See: https://bugs.python.org/issue29288 16 | "".encode("idna") 17 | 18 | SERVICE_NAME = "harvester" 19 | 20 | 21 | def service_kwargs_for_harvester( 22 | root_path: pathlib.Path, 23 | config: Dict, 24 | consensus_constants: ConsensusConstants, 25 | ) -> Dict: 26 | connect_peers = [PeerInfo(config["farmer_peer"]["host"], config["farmer_peer"]["port"])] 27 | overrides = config["network_overrides"]["constants"][config["selected_network"]] 28 | updated_constants = consensus_constants.replace_str_to_bytes(**overrides) 29 | 30 | harvester = Harvester(root_path, config, updated_constants) 31 | peer_api = HarvesterAPI(harvester) 32 | network_id = config["selected_network"] 33 | kwargs = dict( 34 | root_path=root_path, 35 | node=harvester, 36 | peer_api=peer_api, 37 | node_type=NodeType.HARVESTER, 38 | advertised_port=config["port"], 39 | service_name=SERVICE_NAME, 40 | server_listen_ports=[config["port"]], 41 | connect_peers=connect_peers, 42 | auth_connect_peers=True, 43 | network_id=network_id, 44 | ) 45 | if config["start_rpc_server"]: 46 | kwargs["rpc_info"] = (HarvesterRpcApi, config["rpc_port"]) 47 | return kwargs 48 | 49 | 50 | def main() -> None: 51 | config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME) 52 | kwargs = service_kwargs_for_harvester(DEFAULT_ROOT_PATH, config, DEFAULT_CONSTANTS) 53 | return run_service(**kwargs) 54 | 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /equality/server/start_introducer.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from typing import Dict 3 | 4 | from equality.introducer.introducer import Introducer 5 | from equality.introducer.introducer_api import IntroducerAPI 6 | from equality.server.outbound_message import NodeType 7 | from equality.server.start_service import run_service 8 | from equality.util.config import load_config_cli 9 | from equality.util.default_root import DEFAULT_ROOT_PATH 10 | 11 | # See: https://bugs.python.org/issue29288 12 | "".encode("idna") 13 | 14 | SERVICE_NAME = "introducer" 15 | 16 | 17 | def service_kwargs_for_introducer( 18 | root_path: pathlib.Path, 19 | config: Dict, 20 | ) -> Dict: 21 | introducer = Introducer(config["max_peers_to_send"], config["recent_peer_threshold"]) 22 | node__api = IntroducerAPI(introducer) 23 | network_id = config["selected_network"] 24 | kwargs = dict( 25 | root_path=root_path, 26 | node=introducer, 27 | peer_api=node__api, 28 | node_type=NodeType.INTRODUCER, 29 | advertised_port=config["port"], 30 | service_name=SERVICE_NAME, 31 | server_listen_ports=[config["port"]], 32 | network_id=network_id, 33 | ) 34 | return kwargs 35 | 36 | 37 | def main() -> None: 38 | config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME) 39 | kwargs = service_kwargs_for_introducer(DEFAULT_ROOT_PATH, config) 40 | return run_service(**kwargs) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /equality/server/start_timelord.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pathlib 3 | from typing import Dict 4 | 5 | from equality.consensus.constants import ConsensusConstants 6 | from equality.consensus.default_constants import DEFAULT_CONSTANTS 7 | from equality.server.outbound_message import NodeType 8 | from equality.server.start_service import run_service 9 | from equality.timelord.timelord import Timelord 10 | from equality.timelord.timelord_api import TimelordAPI 11 | from equality.types.peer_info import PeerInfo 12 | from equality.util.config import load_config_cli 13 | from equality.util.default_root import DEFAULT_ROOT_PATH 14 | 15 | # See: https://bugs.python.org/issue29288 16 | "".encode("idna") 17 | 18 | SERVICE_NAME = "timelord" 19 | 20 | 21 | log = logging.getLogger(__name__) 22 | 23 | 24 | def service_kwargs_for_timelord( 25 | root_path: pathlib.Path, 26 | config: Dict, 27 | constants: ConsensusConstants, 28 | ) -> Dict: 29 | 30 | connect_peers = [PeerInfo(config["full_node_peer"]["host"], config["full_node_peer"]["port"])] 31 | overrides = config["network_overrides"]["constants"][config["selected_network"]] 32 | updated_constants = constants.replace_str_to_bytes(**overrides) 33 | 34 | node = Timelord(root_path, config, updated_constants) 35 | peer_api = TimelordAPI(node) 36 | network_id = config["selected_network"] 37 | kwargs = dict( 38 | root_path=root_path, 39 | peer_api=peer_api, 40 | node=node, 41 | node_type=NodeType.TIMELORD, 42 | advertised_port=config["port"], 43 | service_name=SERVICE_NAME, 44 | server_listen_ports=[config["port"]], 45 | connect_peers=connect_peers, 46 | auth_connect_peers=False, 47 | network_id=network_id, 48 | ) 49 | return kwargs 50 | 51 | 52 | def main() -> None: 53 | config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME) 54 | kwargs = service_kwargs_for_timelord(DEFAULT_ROOT_PATH, config, DEFAULT_CONSTANTS) 55 | return run_service(**kwargs) 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /equality/server/upnp.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import threading 3 | from queue import Queue 4 | 5 | try: 6 | import miniupnpc 7 | except ImportError: 8 | pass 9 | 10 | 11 | log = logging.getLogger(__name__) 12 | 13 | 14 | class UPnP: 15 | def __init__(self): 16 | self.queue = Queue() 17 | 18 | def run(): 19 | try: 20 | self.upnp = miniupnpc.UPnP() 21 | self.upnp.discoverdelay = 30 22 | self.upnp.discover() 23 | self.upnp.selectigd() 24 | keep_going = True 25 | while keep_going: 26 | msg = self.queue.get() 27 | if msg[0] == "remap": 28 | port = msg[1] 29 | log.info(f"Attempting to enable UPnP (open up port {port})") 30 | self.upnp.deleteportmapping(port, "TCP") 31 | self.upnp.addportmapping(port, "TCP", self.upnp.lanaddr, port, "equality", "") 32 | log.info( 33 | f"Port {port} opened with UPnP. lanaddr {self.upnp.lanaddr} " 34 | f"external: {self.upnp.externalipaddress()}" 35 | ) 36 | elif msg[0] == "release": 37 | port = msg[1] 38 | self.upnp.deleteportmapping(port, "TCP") 39 | log.info(f"Port {port} closed with UPnP") 40 | elif msg[0] == "shutdown": 41 | keep_going = False 42 | except Exception as e: 43 | log.info( 44 | "UPnP failed. This is not required to run equality, it allows incoming connections from other peers." 45 | ) 46 | log.info(e) 47 | 48 | self.thread = threading.Thread(target=run) 49 | self.thread.start() 50 | 51 | def remap(self, port): 52 | self.queue.put(("remap", port)) 53 | 54 | def release(self, port): 55 | self.queue.put(("release", port)) 56 | 57 | def shutdown(self): 58 | self.queue.put(("shutdown",)) 59 | self.thread.join() 60 | 61 | def __del__(self): 62 | self.shutdown() 63 | -------------------------------------------------------------------------------- /equality/simulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/simulator/__init__.py -------------------------------------------------------------------------------- /equality/simulator/simulator_constants.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from equality.util.block_tools import BlockTools, test_constants 3 | from equality.util.default_root import DEFAULT_ROOT_PATH 4 | 5 | # TODO: mariano: fix this with new consensus 6 | bt = BlockTools(root_path=DEFAULT_ROOT_PATH) 7 | new_genesis_block = bt.create_genesis_block(test_constants, b"0") 8 | 9 | print(bytes(new_genesis_block)) 10 | -------------------------------------------------------------------------------- /equality/simulator/simulator_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.types.blockchain_format.sized_bytes import bytes32 4 | from equality.util.ints import uint32 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | @dataclass(frozen=True) 9 | @streamable 10 | class FarmNewBlockProtocol(Streamable): 11 | puzzle_hash: bytes32 12 | 13 | 14 | @dataclass(frozen=True) 15 | @streamable 16 | class ReorgProtocol(Streamable): 17 | old_index: uint32 18 | new_index: uint32 19 | puzzle_hash: bytes32 20 | -------------------------------------------------------------------------------- /equality/ssl/dst_root_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ 3 | MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT 4 | DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow 5 | PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD 6 | Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 7 | AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O 8 | rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq 9 | OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b 10 | xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw 11 | 7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD 12 | aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV 13 | HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG 14 | SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 15 | ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr 16 | AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz 17 | R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 18 | JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo 19 | Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /equality/ssl/equality_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDOTCCAiGgAwIBAgIUb3pVIN5ET3BHmXOA+265PuqK5wkwDQYJKoZIhvcNAQEL 3 | BQAwTDERMA8GA1UECgwIRXF1YWxpdHkxFDASBgNVBAMMC0VxdWFsaXR5IENBMSEw 4 | HwYDVQQLDBhPcmdhbmljIEZhcm1pbmcgRGl2aXNpb24wHhcNMjEwNjIzMDUyMDE1 5 | WhcNMzEwNjIxMDUyMDE1WjBMMREwDwYDVQQKDAhFcXVhbGl0eTEUMBIGA1UEAwwL 6 | RXF1YWxpdHkgQ0ExITAfBgNVBAsMGE9yZ2FuaWMgRmFybWluZyBEaXZpc2lvbjCC 7 | ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALaK4NHj72/Libq7bF9d60Rs 8 | 67A2yy0LX0prbnJGPaKHkj6tQ5UznfwkXJrjq4wp0drIeNQfreNZfiHilgdSlkUI 9 | Fpg+gGeKtvfHKQjKyIpenu9JjXu8lukOihvPhZLWTjcCnH+DGhyEv+LsgPWgvOYn 10 | tyfVkvF6iZ5TGgcYMwC2PHZX5LYRznJTL0tyYC0n5GPSB2ID30DWmgH8dZk0Q/qt 11 | lC1M0h1i17w2GQs8/9jYCdoyKxSWSKoOMIRjNNrK5AxcAuFUqPfvYFXbKt2MQrrF 12 | goiKisLrGrLQrzvBAG9pKAgrJ0Mhiu0JVjfyaOgNSFHHdsppiJLh4duCy9dPyT0C 13 | AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAr+rD 14 | ZYZaJrztg+EPao8/xkmxWiM6wqTN9LQ/mUxQEE929jVCOY7ZCHHIohUgObFi0gzg 15 | pWUHQmm5oj5QfwaXNuxx6V26QAW9WH4QFzIA0gAUgrJPDeavh2tS/KlzPWAlJhJf 16 | IxFh9ZpaVSaquxery71QFRTRlZ/nUQ5hWLmeozJGtvjCjrib2x37GFoRDH3jNzeD 17 | ivzatazvERMvqRO2gomt9fBqBJpgRnPrzCq/5szvPZTzGOSAz6GU3xI2TukbdmR6 18 | vxNR/hGe04I2a9Yp3HhQbIkyfuc31q/pQmXnO34qxfoGIwlt6AjnAqrOK8y5kPR7 19 | icDlhBXPDgcCP2nzzQ== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /equality/ssl/equality_ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAtorg0ePvb8uJurtsX13rRGzrsDbLLQtfSmtuckY9ooeSPq1D 3 | lTOd/CRcmuOrjCnR2sh41B+t41l+IeKWB1KWRQgWmD6AZ4q298cpCMrIil6e70mN 4 | e7yW6Q6KG8+FktZONwKcf4MaHIS/4uyA9aC85ie3J9WS8XqJnlMaBxgzALY8dlfk 5 | thHOclMvS3JgLSfkY9IHYgPfQNaaAfx1mTRD+q2ULUzSHWLXvDYZCzz/2NgJ2jIr 6 | FJZIqg4whGM02srkDFwC4VSo9+9gVdsq3YxCusWCiIqKwusastCvO8EAb2koCCsn 7 | QyGK7QlWN/Jo6A1IUcd2ymmIkuHh24LL10/JPQIDAQABAoIBAH9mtjpHR1lTSTEW 8 | ljCJMCZ1jplR5MPvZ/ZcvJztAR9qV1t/OefsOps8FZXUyGnu1hRITmxeH9LJJwsn 9 | RWae8VoEpyBinuOlIMYWLkGApi5Mk8iWITT3DqxiBWobkDJHeBdUrz0flx3IWNRc 10 | FDValfxcQ6x9ly0KRPpHLac98HrRh2qCOOBMkO2rH5IWoBjhWhet0PJUP7v2+Hzh 11 | YE3xsHbmT1/LuZS5b2gKZSOpN671n7iJje3AYgrKV0qep6O2t+qdZPwlF/mFf+I5 12 | RXBd6bdsqPpxGOoo2gsJXVh4fYGBlSf8gvvVak40KV/y2/x8ECJ8EUOLQ5DPnjPa 13 | Nmk7WIECgYEA5OoZrIzaHfZFqvR4uWrkfULAjU1NjpQxQVZ2KVb9tTBp9p8bK8t8 14 | InNUOWKv4R1wD5Lr72yt6luWJLKVg+R2UtMPOpeIVdKLCDuToTHP/r8aGTHs4ulz 15 | FU1xsRiemLAqQ40ARh+XmM/3HUFlPsRU2MhYbLmjElWEjZiaNlpFSBkCgYEAzCQn 16 | okkcjIpQM70BNMPSgkeCCYnhlBTpSiQ0ZesdbPWc7bDJm5JK+wkc5xRbFn4UabA9 17 | 5CAA0QvucQcTGhpFdTWwL0i7+zdQ522qg68qEGrawjLw6wQP6yLchEtxn4w1Hd+n 18 | vWJbDfczRMh0Vlu7xJhAydDbMpWkhtCpzAb7fsUCgYEAvjBJ2HTGG7KrO1gWnWbU 19 | I8Y8GKorT9SL/88IKVyfOdTZlQ+oEYDkx/Kt/Wei3RQKMMZOxAO1k0s4uZCubIB6 20 | VmII+xM/MJ7W6KYnB/xM5Uh9NIWoXPa8qOxJE2xzCEUFOE6D9b71wv62P1Y/+iBA 21 | pkP+Q4cQl4D5unMZXSMvALECgYAvf8YdE6fp55DryPGL5niHyb2vHH75Zrg1gSEU 22 | CdXp8WhXQghXA34g1Sj9d5YOML3tLY3pE6089uq/L+c47p30vM5G4kSjGmbfwnun 23 | ck12TnaiMxothN9zVcQ7oBZLCmOdlNnBzi3jnP435fBz25pgDxEcJFWsencyy0CG 24 | 8aT5BQKBgGd8q4DpDvk6yv2YpEYujiyCX0Cv9vk6bgzjuX6o5K3uC3V+G9r723JC 25 | 3sGt/Vdf3N3nMJJFTImEXSVNY7KOn4E7QkTYmdkrogYSWGyQQEteHqnM/lZxsTqT 26 | rvZwpujOnTfqrwne/+WHZ48i5nbZ0vB6SGBE9GjeE+PlxerucoSS 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /equality/timelord/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/timelord/__init__.py -------------------------------------------------------------------------------- /equality/timelord/iters_from_block.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Tuple, Union 2 | 3 | from equality.consensus.pot_iterations import calculate_ip_iters, calculate_iterations_quality, calculate_sp_iters 4 | from equality.types.blockchain_format.reward_chain_block import RewardChainBlock, RewardChainBlockUnfinished 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.util.ints import uint64 7 | 8 | 9 | def iters_from_block( 10 | constants, 11 | reward_chain_block: Union[RewardChainBlock, RewardChainBlockUnfinished], 12 | sub_slot_iters: uint64, 13 | difficulty: uint64, 14 | ) -> Tuple[uint64, uint64]: 15 | if reward_chain_block.challenge_chain_sp_vdf is None: 16 | assert reward_chain_block.signage_point_index == 0 17 | cc_sp: bytes32 = reward_chain_block.pos_ss_cc_challenge_hash 18 | else: 19 | cc_sp = reward_chain_block.challenge_chain_sp_vdf.output.get_hash() 20 | 21 | quality_string: Optional[bytes32] = reward_chain_block.proof_of_space.verify_and_get_quality_string( 22 | constants, 23 | reward_chain_block.pos_ss_cc_challenge_hash, 24 | cc_sp, 25 | ) 26 | assert quality_string is not None 27 | 28 | required_iters: uint64 = calculate_iterations_quality( 29 | constants.DIFFICULTY_CONSTANT_FACTOR, 30 | quality_string, 31 | reward_chain_block.proof_of_space.size, 32 | difficulty, 33 | cc_sp, 34 | ) 35 | return ( 36 | calculate_sp_iters(constants, sub_slot_iters, reward_chain_block.signage_point_index), 37 | calculate_ip_iters( 38 | constants, 39 | sub_slot_iters, 40 | reward_chain_block.signage_point_index, 41 | required_iters, 42 | ), 43 | ) 44 | -------------------------------------------------------------------------------- /equality/timelord/types.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Chain(Enum): 5 | CHALLENGE_CHAIN = 1 6 | REWARD_CHAIN = 2 7 | INFUSED_CHALLENGE_CHAIN = 3 8 | BLUEBOX = 4 9 | 10 | 11 | class IterationType(Enum): 12 | SIGNAGE_POINT = 1 13 | INFUSION_POINT = 2 14 | END_OF_SUBSLOT = 3 15 | 16 | 17 | class StateType(Enum): 18 | PEAK = 1 19 | END_OF_SUB_SLOT = 2 20 | FIRST_SUB_SLOT = 3 21 | -------------------------------------------------------------------------------- /equality/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/types/__init__.py -------------------------------------------------------------------------------- /equality/types/announcement.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.types.blockchain_format.sized_bytes import bytes32 4 | from equality.util.hash import std_hash 5 | 6 | 7 | @dataclass(frozen=True) 8 | class Announcement: 9 | origin_info: bytes32 10 | message: bytes 11 | 12 | def name(self) -> bytes32: 13 | return std_hash(bytes(self.origin_info + self.message)) 14 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/types/blockchain_format/__init__.py -------------------------------------------------------------------------------- /equality/types/blockchain_format/classgroup.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.consensus.constants import ConsensusConstants 4 | from equality.types.blockchain_format.sized_bytes import bytes100 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | @dataclass(frozen=True) 9 | @streamable 10 | class ClassgroupElement(Streamable): 11 | """ 12 | Represents a classgroup element (a,b,c) where a, b, and c are 512 bit signed integers. However this is using 13 | a compressed representation. VDF outputs are a single classgroup element. VDF proofs can also be one classgroup 14 | element (or multiple). 15 | """ 16 | 17 | data: bytes100 18 | 19 | @staticmethod 20 | def from_bytes(data) -> "ClassgroupElement": 21 | if len(data) < 100: 22 | data += b"\x00" * (100 - len(data)) 23 | return ClassgroupElement(bytes100(data)) 24 | 25 | @staticmethod 26 | def get_default_element() -> "ClassgroupElement": 27 | # Bit 3 in the first byte of serialized compressed form indicates if 28 | # it's the default generator element. 29 | return ClassgroupElement.from_bytes(b"\x08") 30 | 31 | @staticmethod 32 | def get_size(constants: ConsensusConstants): 33 | return 100 34 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/pool_target.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.types.blockchain_format.sized_bytes import bytes32 4 | from equality.util.ints import uint32 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | @dataclass(frozen=True) 9 | @streamable 10 | class PoolTarget(Streamable): 11 | puzzle_hash: bytes32 12 | max_height: uint32 # A max height of 0 means it is valid forever 13 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/reward_chain_block.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from blspy import G2Element 5 | 6 | from equality.types.blockchain_format.proof_of_space import ProofOfSpace 7 | from equality.types.blockchain_format.sized_bytes import bytes32 8 | from equality.types.blockchain_format.vdf import VDFInfo 9 | from equality.util.ints import uint8, uint32, uint128 10 | from equality.util.streamable import Streamable, streamable 11 | 12 | 13 | @dataclass(frozen=True) 14 | @streamable 15 | class RewardChainBlockUnfinished(Streamable): 16 | total_iters: uint128 17 | signage_point_index: uint8 18 | pos_ss_cc_challenge_hash: bytes32 19 | proof_of_space: ProofOfSpace 20 | challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot 21 | challenge_chain_sp_signature: G2Element 22 | reward_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot 23 | reward_chain_sp_signature: G2Element 24 | 25 | 26 | @dataclass(frozen=True) 27 | @streamable 28 | class RewardChainBlock(Streamable): 29 | weight: uint128 30 | height: uint32 31 | total_iters: uint128 32 | signage_point_index: uint8 33 | pos_ss_cc_challenge_hash: bytes32 34 | proof_of_space: ProofOfSpace 35 | challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot 36 | challenge_chain_sp_signature: G2Element 37 | challenge_chain_ip_vdf: VDFInfo 38 | reward_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot 39 | reward_chain_sp_signature: G2Element 40 | reward_chain_ip_vdf: VDFInfo 41 | infused_challenge_chain_ip_vdf: Optional[VDFInfo] # Iff deficit < 16 42 | is_transaction_block: bool 43 | 44 | def get_unfinished(self) -> RewardChainBlockUnfinished: 45 | return RewardChainBlockUnfinished( 46 | self.total_iters, 47 | self.signage_point_index, 48 | self.pos_ss_cc_challenge_hash, 49 | self.proof_of_space, 50 | self.challenge_chain_sp_vdf, 51 | self.challenge_chain_sp_signature, 52 | self.reward_chain_sp_vdf, 53 | self.reward_chain_sp_signature, 54 | ) 55 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/sized_bytes.py: -------------------------------------------------------------------------------- 1 | from equality.util.byte_types import make_sized_bytes 2 | 3 | bytes4 = make_sized_bytes(4) 4 | bytes8 = make_sized_bytes(8) 5 | bytes32 = make_sized_bytes(32) 6 | bytes48 = make_sized_bytes(48) 7 | bytes96 = make_sized_bytes(96) 8 | bytes100 = make_sized_bytes(100) 9 | bytes480 = make_sized_bytes(480) 10 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/slots.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from blspy import G2Element 5 | 6 | from equality.types.blockchain_format.proof_of_space import ProofOfSpace 7 | from equality.types.blockchain_format.sized_bytes import bytes32 8 | from equality.types.blockchain_format.vdf import VDFInfo, VDFProof 9 | from equality.util.ints import uint8, uint64 10 | from equality.util.streamable import Streamable, streamable 11 | 12 | 13 | @dataclass(frozen=True) 14 | @streamable 15 | class ChallengeBlockInfo(Streamable): # The hash of this is used as the challenge_hash for the ICC VDF 16 | proof_of_space: ProofOfSpace 17 | challenge_chain_sp_vdf: Optional[VDFInfo] # Only present if not the first sp 18 | challenge_chain_sp_signature: G2Element 19 | challenge_chain_ip_vdf: VDFInfo 20 | 21 | 22 | @dataclass(frozen=True) 23 | @streamable 24 | class ChallengeChainSubSlot(Streamable): 25 | challenge_chain_end_of_slot_vdf: VDFInfo 26 | infused_challenge_chain_sub_slot_hash: Optional[bytes32] # Only at the end of a slot 27 | subepoch_summary_hash: Optional[bytes32] # Only once per sub-epoch, and one sub-epoch delayed 28 | new_sub_slot_iters: Optional[uint64] # Only at the end of epoch, sub-epoch, and slot 29 | new_difficulty: Optional[uint64] # Only at the end of epoch, sub-epoch, and slot 30 | 31 | 32 | @dataclass(frozen=True) 33 | @streamable 34 | class InfusedChallengeChainSubSlot(Streamable): 35 | infused_challenge_chain_end_of_slot_vdf: VDFInfo 36 | 37 | 38 | @dataclass(frozen=True) 39 | @streamable 40 | class RewardChainSubSlot(Streamable): 41 | end_of_slot_vdf: VDFInfo 42 | challenge_chain_sub_slot_hash: bytes32 43 | infused_challenge_chain_sub_slot_hash: Optional[bytes32] 44 | deficit: uint8 # 16 or less. usually zero 45 | 46 | 47 | @dataclass(frozen=True) 48 | @streamable 49 | class SubSlotProofs(Streamable): 50 | challenge_chain_slot_proof: VDFProof 51 | infused_challenge_chain_slot_proof: Optional[VDFProof] 52 | reward_chain_slot_proof: VDFProof 53 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/sub_epoch_summary.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.ints import uint8, uint64 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class SubEpochSummary(Streamable): 12 | prev_subepoch_summary_hash: bytes32 13 | reward_chain_hash: bytes32 # hash of reward chain at end of last segment 14 | num_blocks_overflow: uint8 # How many more blocks than 384*(N-1) 15 | new_difficulty: Optional[uint64] # Only once per epoch (diff adjustment) 16 | new_sub_slot_iters: Optional[uint64] # Only once per epoch (diff adjustment) 17 | -------------------------------------------------------------------------------- /equality/types/blockchain_format/tree_hash.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an implementation of `sha256_treehash`, used to calculate 3 | puzzle hashes in clvm. 4 | 5 | This implementation goes to great pains to be non-recursive so we don't 6 | have to worry about blowing out the python stack. 7 | """ 8 | 9 | from typing import Optional, Set 10 | 11 | from clvm import CLVMObject 12 | 13 | from equality.types.blockchain_format.sized_bytes import bytes32 14 | from equality.util.hash import std_hash 15 | 16 | 17 | def sha256_treehash(sexp: CLVMObject, precalculated: Optional[Set[bytes32]] = None) -> bytes32: 18 | """ 19 | Hash values in `precalculated` are presumed to have been hashed already. 20 | """ 21 | 22 | if precalculated is None: 23 | precalculated = set() 24 | 25 | def handle_sexp(sexp_stack, op_stack, precalculated: Set[bytes32]) -> None: 26 | sexp = sexp_stack.pop() 27 | if sexp.pair: 28 | p0, p1 = sexp.pair 29 | sexp_stack.append(p0) 30 | sexp_stack.append(p1) 31 | op_stack.append(handle_pair) 32 | op_stack.append(handle_sexp) 33 | op_stack.append(roll) 34 | op_stack.append(handle_sexp) 35 | else: 36 | if sexp.atom in precalculated: 37 | r = sexp.atom 38 | else: 39 | r = std_hash(b"\1" + sexp.atom) 40 | sexp_stack.append(r) 41 | 42 | def handle_pair(sexp_stack, op_stack, precalculated) -> None: 43 | p0 = sexp_stack.pop() 44 | p1 = sexp_stack.pop() 45 | sexp_stack.append(std_hash(b"\2" + p0 + p1)) 46 | 47 | def roll(sexp_stack, op_stack, precalculated) -> None: 48 | p0 = sexp_stack.pop() 49 | p1 = sexp_stack.pop() 50 | sexp_stack.append(p0) 51 | sexp_stack.append(p1) 52 | 53 | sexp_stack = [sexp] 54 | op_stack = [handle_sexp] 55 | while len(op_stack) > 0: 56 | op = op_stack.pop() 57 | op(sexp_stack, op_stack, precalculated) 58 | return bytes32(sexp_stack[0]) 59 | -------------------------------------------------------------------------------- /equality/types/coin_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.types.blockchain_format.coin import Coin 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.ints import uint32, uint64 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class CoinRecord(Streamable): 12 | """ 13 | These are values that correspond to a CoinName that are used 14 | in keeping track of the unspent database. 15 | """ 16 | 17 | coin: Coin 18 | confirmed_block_index: uint32 19 | spent_block_index: uint32 20 | spent: bool 21 | coinbase: bool 22 | timestamp: uint64 # Timestamp of the block at height confirmed_block_index 23 | 24 | @property 25 | def name(self) -> bytes32: 26 | return self.coin.name() 27 | -------------------------------------------------------------------------------- /equality/types/coin_solution.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from equality.types.blockchain_format.coin import Coin 5 | from equality.types.blockchain_format.program import SerializedProgram, INFINITE_COST 6 | from equality.util.chain_utils import additions_for_solution 7 | from equality.util.streamable import Streamable, streamable 8 | 9 | 10 | @dataclass(frozen=True) 11 | @streamable 12 | class CoinSolution(Streamable): 13 | """ 14 | This is a rather disparate data structure that validates coin transfers. It's generally populated 15 | with data from different sources, since burned coins are identified by name, so it is built up 16 | more often that it is streamed. 17 | """ 18 | 19 | coin: Coin 20 | puzzle_reveal: SerializedProgram 21 | solution: SerializedProgram 22 | 23 | def additions(self) -> List[Coin]: 24 | return additions_for_solution(self.coin.name(), self.puzzle_reveal, self.solution, INFINITE_COST) 25 | -------------------------------------------------------------------------------- /equality/types/condition_opcodes.py: -------------------------------------------------------------------------------- 1 | import enum 2 | from typing import Any 3 | 4 | 5 | # See equality/wallet/puzzles/condition_codes.clvm 6 | class ConditionOpcode(bytes, enum.Enum): 7 | # UNKNOWN is ascii "0" 8 | UNKNOWN = bytes([48]) 9 | 10 | # AGG_SIG is ascii "1" 11 | 12 | # the conditions below require bls12-381 signatures 13 | 14 | AGG_SIG_UNSAFE = bytes([49]) 15 | AGG_SIG_ME = bytes([50]) 16 | 17 | # the conditions below reserve coin amounts and have to be accounted for in output totals 18 | 19 | CREATE_COIN = bytes([51]) 20 | RESERVE_FEE = bytes([52]) 21 | 22 | # the conditions below deal with announcements, for inter-coin communication 23 | 24 | CREATE_COIN_ANNOUNCEMENT = bytes([60]) 25 | ASSERT_COIN_ANNOUNCEMENT = bytes([61]) 26 | CREATE_PUZZLE_ANNOUNCEMENT = bytes([62]) 27 | ASSERT_PUZZLE_ANNOUNCEMENT = bytes([63]) 28 | 29 | # the conditions below let coins inquire about themselves 30 | 31 | ASSERT_MY_COIN_ID = bytes([70]) 32 | ASSERT_MY_PARENT_ID = bytes([71]) 33 | ASSERT_MY_PUZZLEHASH = bytes([72]) 34 | ASSERT_MY_AMOUNT = bytes([73]) 35 | 36 | # the conditions below ensure that we're "far enough" in the future 37 | 38 | # wall-clock time 39 | ASSERT_SECONDS_RELATIVE = bytes([80]) 40 | ASSERT_SECONDS_ABSOLUTE = bytes([81]) 41 | 42 | # block index 43 | ASSERT_HEIGHT_RELATIVE = bytes([82]) 44 | ASSERT_HEIGHT_ABSOLUTE = bytes([83]) 45 | 46 | def __bytes__(self) -> bytes: 47 | return bytes(self.value) 48 | 49 | @classmethod 50 | def from_bytes(cls: Any, blob: bytes) -> Any: 51 | assert len(blob) == 1 52 | return cls(blob) 53 | -------------------------------------------------------------------------------- /equality/types/condition_with_args.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from equality.types.condition_opcodes import ConditionOpcode 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | @dataclass(frozen=True) 9 | @streamable 10 | class ConditionWithArgs(Streamable): 11 | """ 12 | This structure is used to store parsed CLVM conditions 13 | Conditions in CLVM have either format of (opcode, var1) or (opcode, var1, var2) 14 | """ 15 | 16 | opcode: ConditionOpcode 17 | vars: List[bytes] 18 | -------------------------------------------------------------------------------- /equality/types/end_of_slot_bundle.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from equality.types.blockchain_format.slots import ( 5 | ChallengeChainSubSlot, 6 | InfusedChallengeChainSubSlot, 7 | RewardChainSubSlot, 8 | SubSlotProofs, 9 | ) 10 | from equality.util.streamable import Streamable, streamable 11 | 12 | 13 | @dataclass(frozen=True) 14 | @streamable 15 | class EndOfSubSlotBundle(Streamable): 16 | challenge_chain: ChallengeChainSubSlot 17 | infused_challenge_chain: Optional[InfusedChallengeChainSubSlot] 18 | reward_chain: RewardChainSubSlot 19 | proofs: SubSlotProofs 20 | -------------------------------------------------------------------------------- /equality/types/generator_types.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | from equality.types.blockchain_format.program import SerializedProgram 4 | from equality.util.ints import uint32 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | class GeneratorBlockCacheInterface: 9 | def get_generator_for_block_height(self, height: uint32) -> SerializedProgram: 10 | # Requested block must be a transaction block 11 | pass 12 | 13 | 14 | @dataclass(frozen=True) 15 | @streamable 16 | class GeneratorArg(Streamable): 17 | """`GeneratorArg` contains data from already-buried blocks in the blockchain""" 18 | 19 | block_height: uint32 20 | generator: SerializedProgram 21 | 22 | 23 | @dataclass(frozen=True) 24 | class CompressorArg: 25 | """`CompressorArg` is used as input to the Block Compressor""" 26 | 27 | block_height: uint32 28 | generator: SerializedProgram 29 | start: int 30 | end: int 31 | 32 | 33 | @dataclass(frozen=True) 34 | @streamable 35 | class BlockGenerator(Streamable): 36 | program: SerializedProgram 37 | generator_args: List[GeneratorArg] 38 | 39 | def block_height_list(self) -> List[uint32]: 40 | return [a.block_height for a in self.generator_args] 41 | 42 | def generator_refs(self) -> List[SerializedProgram]: 43 | return [a.generator for a in self.generator_args] 44 | -------------------------------------------------------------------------------- /equality/types/mempool_inclusion_status.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | 4 | class MempoolInclusionStatus(IntEnum): 5 | SUCCESS = 1 # Transaction added to mempool 6 | PENDING = 2 # Transaction not yet added to mempool 7 | FAILED = 3 # Transaction was invalid and dropped 8 | -------------------------------------------------------------------------------- /equality/types/mempool_item.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from equality.consensus.cost_calculator import NPCResult 5 | from equality.types.blockchain_format.coin import Coin 6 | from equality.types.blockchain_format.program import SerializedProgram 7 | from equality.types.blockchain_format.sized_bytes import bytes32 8 | from equality.types.spend_bundle import SpendBundle 9 | from equality.util.ints import uint64 10 | from equality.util.streamable import Streamable, streamable 11 | 12 | 13 | @dataclass(frozen=True) 14 | @streamable 15 | class MempoolItem(Streamable): 16 | spend_bundle: SpendBundle 17 | fee: uint64 18 | npc_result: NPCResult 19 | cost: uint64 20 | spend_bundle_name: bytes32 21 | additions: List[Coin] 22 | removals: List[Coin] 23 | program: SerializedProgram 24 | 25 | def __lt__(self, other): 26 | return self.fee_per_cost < other.fee_per_cost 27 | 28 | @property 29 | def fee_per_cost(self) -> float: 30 | return int(self.fee) / int(self.cost) 31 | 32 | @property 33 | def name(self) -> bytes32: 34 | return self.spend_bundle_name 35 | -------------------------------------------------------------------------------- /equality/types/name_puzzle_condition.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Dict, List, Tuple 3 | 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.types.condition_with_args import ConditionWithArgs 6 | from equality.util.condition_tools import ConditionOpcode 7 | from equality.util.streamable import Streamable, streamable 8 | 9 | 10 | @dataclass(frozen=True) 11 | @streamable 12 | class NPC(Streamable): 13 | coin_name: bytes32 14 | puzzle_hash: bytes32 15 | conditions: List[Tuple[ConditionOpcode, List[ConditionWithArgs]]] 16 | 17 | @property 18 | def condition_dict(self): 19 | d: Dict[ConditionOpcode, List[ConditionWithArgs]] = {} 20 | for opcode, l in self.conditions: 21 | d[opcode] = l 22 | return d 23 | -------------------------------------------------------------------------------- /equality/types/peer_info.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | from dataclasses import dataclass 3 | from typing import Optional, Union 4 | 5 | from equality.util.ints import uint16, uint64 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class PeerInfo(Streamable): 12 | host: str 13 | port: uint16 14 | 15 | def is_valid(self, allow_private_subnets=False) -> bool: 16 | ip: Optional[Union[ipaddress.IPv6Address, ipaddress.IPv4Address]] = None 17 | try: 18 | ip = ipaddress.IPv6Address(self.host) 19 | except ValueError: 20 | ip = None 21 | if ip is not None: 22 | if ip.is_private and not allow_private_subnets: 23 | return False 24 | return True 25 | 26 | try: 27 | ip = ipaddress.IPv4Address(self.host) 28 | except ValueError: 29 | ip = None 30 | if ip is not None: 31 | if ip.is_private and not allow_private_subnets: 32 | return False 33 | return True 34 | return False 35 | 36 | # Functions related to peer bucketing in new/tried tables. 37 | def get_key(self): 38 | try: 39 | ip = ipaddress.IPv6Address(self.host) 40 | except ValueError: 41 | ip_v4 = ipaddress.IPv4Address(self.host) 42 | ip = ipaddress.IPv6Address(int(ipaddress.IPv6Address("2002::")) | (int(ip_v4) << 80)) 43 | key = ip.packed 44 | key += bytes([self.port // 0x100, self.port & 0x0FF]) 45 | return key 46 | 47 | def get_group(self): 48 | # TODO: Port everything from Bitcoin. 49 | ipv4 = 1 50 | try: 51 | ip = ipaddress.IPv4Address(self.host) 52 | except ValueError: 53 | ip = ipaddress.IPv6Address(self.host) 54 | ipv4 = 0 55 | if ipv4: 56 | group = bytes([1]) + ip.packed[:2] 57 | else: 58 | group = bytes([0]) + ip.packed[:4] 59 | return group 60 | 61 | 62 | @dataclass(frozen=True) 63 | @streamable 64 | class TimestampedPeerInfo(Streamable): 65 | host: str 66 | port: uint16 67 | timestamp: uint64 68 | -------------------------------------------------------------------------------- /equality/types/unfinished_block.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional 3 | 4 | from equality.types.blockchain_format.foliage import Foliage, FoliageTransactionBlock, TransactionsInfo 5 | from equality.types.blockchain_format.program import SerializedProgram 6 | from equality.types.blockchain_format.reward_chain_block import RewardChainBlockUnfinished 7 | from equality.types.blockchain_format.vdf import VDFProof 8 | from equality.types.end_of_slot_bundle import EndOfSubSlotBundle 9 | from equality.util.ints import uint32 10 | from equality.util.streamable import Streamable, streamable 11 | 12 | 13 | @dataclass(frozen=True) 14 | @streamable 15 | class UnfinishedBlock(Streamable): 16 | # Full block, without the final VDFs 17 | finished_sub_slots: List[EndOfSubSlotBundle] # If first sb 18 | reward_chain_block: RewardChainBlockUnfinished # Reward chain trunk data 19 | challenge_chain_sp_proof: Optional[VDFProof] # If not first sp in sub-slot 20 | reward_chain_sp_proof: Optional[VDFProof] # If not first sp in sub-slot 21 | foliage: Foliage # Reward chain foliage data 22 | foliage_transaction_block: Optional[FoliageTransactionBlock] # Reward chain foliage data (tx block) 23 | transactions_info: Optional[TransactionsInfo] # Reward chain foliage data (tx block additional) 24 | transactions_generator: Optional[SerializedProgram] # Program that generates transactions 25 | transactions_generator_ref_list: List[ 26 | uint32 27 | ] # List of block heights of previous generators referenced in this block 28 | 29 | @property 30 | def prev_header_hash(self): 31 | return self.foliage.prev_block_hash 32 | 33 | @property 34 | def partial_hash(self): 35 | return self.reward_chain_block.get_hash() 36 | 37 | def is_transaction_block(self) -> bool: 38 | return self.foliage.foliage_transaction_block_hash is not None 39 | 40 | @property 41 | def total_iters(self): 42 | return self.reward_chain_block.total_iters 43 | -------------------------------------------------------------------------------- /equality/types/unfinished_header_block.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional 3 | 4 | from equality.types.blockchain_format.foliage import Foliage, FoliageTransactionBlock 5 | from equality.types.blockchain_format.reward_chain_block import RewardChainBlockUnfinished 6 | from equality.types.blockchain_format.vdf import VDFProof 7 | from equality.types.end_of_slot_bundle import EndOfSubSlotBundle 8 | from equality.util.streamable import Streamable, streamable 9 | 10 | 11 | @dataclass(frozen=True) 12 | @streamable 13 | class UnfinishedHeaderBlock(Streamable): 14 | # Same as a FullBlock but without TransactionInfo and Generator, used by light clients 15 | finished_sub_slots: List[EndOfSubSlotBundle] # If first sb 16 | reward_chain_block: RewardChainBlockUnfinished # Reward chain trunk data 17 | challenge_chain_sp_proof: Optional[VDFProof] # If not first sp in sub-slot 18 | reward_chain_sp_proof: Optional[VDFProof] # If not first sp in sub-slot 19 | foliage: Foliage # Reward chain foliage data 20 | foliage_transaction_block: Optional[FoliageTransactionBlock] # Reward chain foliage data (tx block) 21 | transactions_filter: bytes # Filter for block transactions 22 | 23 | @property 24 | def prev_header_hash(self): 25 | return self.foliage.prev_block_hash 26 | 27 | @property 28 | def header_hash(self): 29 | return self.foliage.get_hash() 30 | 31 | @property 32 | def total_iters(self): 33 | return self.reward_chain_block.total_iters 34 | -------------------------------------------------------------------------------- /equality/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/util/__init__.py -------------------------------------------------------------------------------- /equality/util/api_decorators.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import logging 3 | from inspect import signature 4 | 5 | from equality.util.streamable import Streamable 6 | 7 | log = logging.getLogger(__name__) 8 | 9 | 10 | def api_request(f): 11 | @functools.wraps(f) 12 | def f_substitute(*args, **kwargs): 13 | sig = signature(f) 14 | binding = sig.bind(*args, **kwargs) 15 | binding.apply_defaults() 16 | inter = dict(binding.arguments) 17 | 18 | # Converts each parameter from a Python dictionary, into an instance of the object 19 | # specified by the type annotation (signature) of the function that is being called (f) 20 | # The method can also be called with the target type instead of a dictionary. 21 | for param_name, param_class in f.__annotations__.items(): 22 | if param_name != "return" and isinstance(inter[param_name], Streamable): 23 | if param_class.__name__ == "bytes": 24 | continue 25 | if hasattr(f, "bytes_required"): 26 | inter[f"{param_name}_bytes"] = bytes(inter[param_name]) 27 | continue 28 | if param_name != "return" and isinstance(inter[param_name], bytes): 29 | if param_class.__name__ == "bytes": 30 | continue 31 | if hasattr(f, "bytes_required"): 32 | inter[f"{param_name}_bytes"] = inter[param_name] 33 | inter[param_name] = param_class.from_bytes(inter[param_name]) 34 | return f(**inter) 35 | 36 | setattr(f_substitute, "api_function", True) 37 | return f_substitute 38 | 39 | 40 | def peer_required(func): 41 | def inner(): 42 | setattr(func, "peer_required", True) 43 | return func 44 | 45 | return inner() 46 | 47 | 48 | def bytes_required(func): 49 | def inner(): 50 | setattr(func, "bytes_required", True) 51 | return func 52 | 53 | return inner() 54 | 55 | 56 | def execute_task(func): 57 | def inner(): 58 | setattr(func, "execute_task", True) 59 | return func 60 | 61 | return inner() 62 | -------------------------------------------------------------------------------- /equality/util/byte_types.py: -------------------------------------------------------------------------------- 1 | import io 2 | from typing import Any, BinaryIO 3 | 4 | 5 | def hexstr_to_bytes(input_str: str) -> bytes: 6 | """ 7 | Converts a hex string into bytes, removing the 0x if it's present. 8 | """ 9 | if input_str.startswith("0x") or input_str.startswith("0X"): 10 | return bytes.fromhex(input_str[2:]) 11 | return bytes.fromhex(input_str) 12 | 13 | 14 | def make_sized_bytes(size: int): 15 | """ 16 | Create a streamable type that subclasses "bytes" but requires instances 17 | to be a certain, fixed size. 18 | """ 19 | name = "bytes%d" % size 20 | 21 | def __new__(cls, v): 22 | v = bytes(v) 23 | if not isinstance(v, bytes) or len(v) != size: 24 | raise ValueError("bad %s initializer %s" % (name, v)) 25 | return bytes.__new__(cls, v) # type: ignore 26 | 27 | @classmethod # type: ignore 28 | def parse(cls, f: BinaryIO) -> Any: 29 | b = f.read(size) 30 | assert len(b) == size 31 | return cls(b) 32 | 33 | def stream(self, f): 34 | f.write(self) 35 | 36 | @classmethod # type: ignore 37 | def from_bytes(cls: Any, blob: bytes) -> Any: 38 | # pylint: disable=no-member 39 | f = io.BytesIO(blob) 40 | result = cls.parse(f) 41 | assert f.read() == b"" 42 | return result 43 | 44 | def __bytes__(self: Any) -> bytes: 45 | f = io.BytesIO() 46 | self.stream(f) 47 | return bytes(f.getvalue()) 48 | 49 | def __str__(self): 50 | return self.hex() 51 | 52 | def __repr__(self): 53 | return "<%s: %s>" % (self.__class__.__name__, str(self)) 54 | 55 | namespace = dict( 56 | __new__=__new__, 57 | parse=parse, 58 | stream=stream, 59 | from_bytes=from_bytes, 60 | __bytes__=__bytes__, 61 | __str__=__str__, 62 | __repr__=__repr__, 63 | ) 64 | 65 | return type(name, (bytes,), namespace) 66 | -------------------------------------------------------------------------------- /equality/util/chain_utils.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from equality.types.blockchain_format.coin import Coin 4 | from equality.types.blockchain_format.program import SerializedProgram 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.util.condition_tools import ( 7 | conditions_dict_for_solution, 8 | created_outputs_for_conditions_dict, 9 | ) 10 | 11 | 12 | def additions_for_solution( 13 | coin_name: bytes32, puzzle_reveal: SerializedProgram, solution: SerializedProgram, max_cost: int 14 | ) -> List[Coin]: 15 | """ 16 | Checks the conditions created by CoinSolution and returns the list of all coins created 17 | """ 18 | err, dic, cost = conditions_dict_for_solution(puzzle_reveal, solution, max_cost) 19 | if err or dic is None: 20 | return [] 21 | return created_outputs_for_conditions_dict(dic, coin_name) 22 | -------------------------------------------------------------------------------- /equality/util/clvm.py: -------------------------------------------------------------------------------- 1 | from clvm.casts import int_from_bytes, int_to_bytes # noqa 2 | -------------------------------------------------------------------------------- /equality/util/db_wrapper.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | import aiosqlite 4 | 5 | 6 | class DBWrapper: 7 | """ 8 | This object handles HeaderBlocks and Blocks stored in DB used by wallet. 9 | """ 10 | 11 | db: aiosqlite.Connection 12 | lock: asyncio.Lock 13 | 14 | def __init__(self, connection: aiosqlite.Connection): 15 | self.db = connection 16 | self.lock = asyncio.Lock() 17 | 18 | async def begin_transaction(self): 19 | cursor = await self.db.execute("BEGIN TRANSACTION") 20 | await cursor.close() 21 | 22 | async def rollback_transaction(self): 23 | # Also rolls back the coin store, since both stores must be updated at once 24 | if self.db.in_transaction: 25 | cursor = await self.db.execute("ROLLBACK") 26 | await cursor.close() 27 | 28 | async def commit_transaction(self): 29 | await self.db.commit() 30 | -------------------------------------------------------------------------------- /equality/util/default_root.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | DEFAULT_ROOT_PATH = Path(os.path.expanduser(os.getenv("EQUALITY_ROOT", "~/.equality/mainnet"))).resolve() 5 | -------------------------------------------------------------------------------- /equality/util/hash.py: -------------------------------------------------------------------------------- 1 | import blspy 2 | 3 | from equality.types.blockchain_format.sized_bytes import bytes32 4 | 5 | 6 | def std_hash(b) -> bytes32: 7 | """ 8 | The standard hash used in many places. 9 | """ 10 | return bytes32(blspy.Util.hash256(bytes(b))) 11 | -------------------------------------------------------------------------------- /equality/util/ints.py: -------------------------------------------------------------------------------- 1 | from typing import Any, BinaryIO 2 | 3 | from equality.util.struct_stream import StructStream 4 | 5 | 6 | class int8(StructStream): 7 | PACK = "!b" 8 | 9 | 10 | class uint8(StructStream): 11 | PACK = "!B" 12 | 13 | 14 | class int16(StructStream): 15 | PACK = "!h" 16 | 17 | 18 | class uint16(StructStream): 19 | PACK = "!H" 20 | 21 | 22 | class int32(StructStream): 23 | PACK = "!l" 24 | 25 | 26 | class uint32(StructStream): 27 | PACK = "!L" 28 | 29 | 30 | class int64(StructStream): 31 | PACK = "!q" 32 | 33 | 34 | class uint64(StructStream): 35 | PACK = "!Q" 36 | 37 | 38 | class uint128(int): 39 | @classmethod 40 | def parse(cls, f: BinaryIO) -> Any: 41 | read_bytes = f.read(16) 42 | assert len(read_bytes) == 16 43 | n = int.from_bytes(read_bytes, "big", signed=False) 44 | assert n <= (2 ** 128) - 1 and n >= 0 45 | return cls(n) 46 | 47 | def stream(self, f): 48 | assert self <= (2 ** 128) - 1 and self >= 0 49 | f.write(self.to_bytes(16, "big", signed=False)) 50 | 51 | 52 | class int512(int): 53 | # Uses 65 bytes to fit in the sign bit 54 | @classmethod 55 | def parse(cls, f: BinaryIO) -> Any: 56 | read_bytes = f.read(65) 57 | assert len(read_bytes) == 65 58 | n = int.from_bytes(read_bytes, "big", signed=True) 59 | assert n <= (2 ** 512) - 1 and n >= -(2 ** 512) 60 | return cls(n) 61 | 62 | def stream(self, f): 63 | assert self <= (2 ** 512) - 1 and self >= -(2 ** 512) 64 | f.write(self.to_bytes(65, "big", signed=True)) 65 | -------------------------------------------------------------------------------- /equality/util/json_util.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | import json 3 | from typing import Any 4 | 5 | from aiohttp import web 6 | 7 | from equality.wallet.util.wallet_types import WalletType 8 | 9 | 10 | class EnhancedJSONEncoder(json.JSONEncoder): 11 | """ 12 | Encodes bytes as hex strings with 0x, and converts all dataclasses to json. 13 | """ 14 | 15 | def default(self, o: Any): 16 | if dataclasses.is_dataclass(o): 17 | return o.to_json_dict() 18 | elif isinstance(o, WalletType): 19 | return o.name 20 | elif hasattr(type(o), "__bytes__"): 21 | return f"0x{bytes(o).hex()}" 22 | elif isinstance(o, bytes): 23 | return f"0x{o.hex()}" 24 | return super().default(o) 25 | 26 | 27 | def dict_to_json_str(o: Any) -> str: 28 | """ 29 | Converts a python object into json. 30 | """ 31 | json_str = json.dumps(o, cls=EnhancedJSONEncoder, sort_keys=True) 32 | return json_str 33 | 34 | 35 | def obj_to_response(o: Any) -> web.Response: 36 | """ 37 | Converts a python object into json. Used for RPC server which returns JSON. 38 | """ 39 | json_str = dict_to_json_str(o) 40 | return web.Response(body=json_str, content_type="application/json") 41 | -------------------------------------------------------------------------------- /equality/util/lru_cache.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from typing import Any, Optional 3 | 4 | 5 | class LRUCache: 6 | def __init__(self, capacity: int): 7 | self.cache: OrderedDict = OrderedDict() 8 | self.capacity = capacity 9 | 10 | def get(self, key: Any) -> Optional[Any]: 11 | if key not in self.cache: 12 | return None 13 | else: 14 | self.cache.move_to_end(key) 15 | return self.cache[key] 16 | 17 | def put(self, key: Any, value: Any) -> None: 18 | self.cache[key] = value 19 | self.cache.move_to_end(key) 20 | if len(self.cache) > self.capacity: 21 | self.cache.popitem(last=False) 22 | 23 | def remove(self, key: Any) -> None: 24 | self.cache.pop(key) 25 | -------------------------------------------------------------------------------- /equality/util/make_test_constants.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from equality.consensus.default_constants import DEFAULT_CONSTANTS, ConsensusConstants 4 | 5 | 6 | def make_test_constants(test_constants_overrides: Dict) -> ConsensusConstants: 7 | return DEFAULT_CONSTANTS.replace(**test_constants_overrides) 8 | -------------------------------------------------------------------------------- /equality/util/misc.py: -------------------------------------------------------------------------------- 1 | def format_minutes(minutes: int) -> str: 2 | 3 | if not isinstance(minutes, int): 4 | return "Invalid" 5 | 6 | if minutes == 0: 7 | return "Now" 8 | 9 | hour_minutes = 60 10 | day_minutes = 24 * hour_minutes 11 | week_minutes = 7 * day_minutes 12 | months_minutes = 43800 13 | year_minutes = 12 * months_minutes 14 | 15 | years = int(minutes / year_minutes) 16 | months = int(minutes / months_minutes) 17 | weeks = int(minutes / week_minutes) 18 | days = int(minutes / day_minutes) 19 | hours = int(minutes / hour_minutes) 20 | 21 | def format_unit_string(str_unit: str, count: int) -> str: 22 | return f"{count} {str_unit}{('s' if count > 1 else '')}" 23 | 24 | def format_unit(unit: str, count: int, unit_minutes: int, next_unit: str, next_unit_minutes: int) -> str: 25 | formatted = format_unit_string(unit, count) 26 | minutes_left = minutes % unit_minutes 27 | if minutes_left >= next_unit_minutes: 28 | formatted += " and " + format_unit_string(next_unit, int(minutes_left / next_unit_minutes)) 29 | return formatted 30 | 31 | if years > 0: 32 | return format_unit("year", years, year_minutes, "month", months_minutes) 33 | if months > 0: 34 | return format_unit("month", months, months_minutes, "week", week_minutes) 35 | if weeks > 0: 36 | return format_unit("week", weeks, week_minutes, "day", day_minutes) 37 | if days > 0: 38 | return format_unit("day", days, day_minutes, "hour", hour_minutes) 39 | if hours > 0: 40 | return format_unit("hour", hours, hour_minutes, "minute", 1) 41 | if minutes > 0: 42 | return format_unit_string("minute", minutes) 43 | 44 | return "Unknown" 45 | -------------------------------------------------------------------------------- /equality/util/network.py: -------------------------------------------------------------------------------- 1 | from ipaddress import ip_address, IPv4Network, IPv6Network 2 | from typing import Iterable, Union, Any 3 | from equality.server.outbound_message import NodeType 4 | 5 | 6 | def is_in_network(peer_host: str, networks: Iterable[Union[IPv4Network, IPv6Network]]) -> bool: 7 | try: 8 | peer_host_ip = ip_address(peer_host) 9 | return any(peer_host_ip in network for network in networks) 10 | except ValueError: 11 | return False 12 | 13 | 14 | def is_localhost(peer_host: str) -> bool: 15 | return peer_host == "127.0.0.1" or peer_host == "localhost" or peer_host == "::1" or peer_host == "0:0:0:0:0:0:0:1" 16 | 17 | 18 | def class_for_type(type: NodeType) -> Any: 19 | if type is NodeType.FULL_NODE: 20 | from equality.full_node.full_node_api import FullNodeAPI 21 | 22 | return FullNodeAPI 23 | elif type is NodeType.WALLET: 24 | from equality.wallet.wallet_node_api import WalletNodeAPI 25 | 26 | return WalletNodeAPI 27 | elif type is NodeType.INTRODUCER: 28 | from equality.introducer.introducer_api import IntroducerAPI 29 | 30 | return IntroducerAPI 31 | elif type is NodeType.TIMELORD: 32 | from equality.timelord.timelord_api import TimelordAPI 33 | 34 | return TimelordAPI 35 | elif type is NodeType.FARMER: 36 | from equality.farmer.farmer_api import FarmerAPI 37 | 38 | return FarmerAPI 39 | elif type is NodeType.HARVESTER: 40 | from equality.harvester.harvester_api import HarvesterAPI 41 | 42 | return HarvesterAPI 43 | raise ValueError("No class for type") 44 | -------------------------------------------------------------------------------- /equality/util/partial_func.py: -------------------------------------------------------------------------------- 1 | def partial_async_gen(f, *args): 2 | """ 3 | Returns an async generator function which is equalivalent to the passed in function, 4 | but only takes in one parameter (the first one). 5 | """ 6 | 7 | async def inner(first_param): 8 | async for x in f(first_param, *args): 9 | yield x 10 | 11 | return inner 12 | 13 | 14 | def partial_async(f, *args): 15 | """ 16 | Returns an async function which is equalivalent to the passed in function, 17 | but only takes in one parameter (the first one). 18 | """ 19 | 20 | async def inner(first_param): 21 | return await f(first_param, *args) 22 | 23 | return inner 24 | -------------------------------------------------------------------------------- /equality/util/path.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from typing import Union 4 | 5 | 6 | def path_from_root(root: Path, path_str: Union[str, Path]) -> Path: 7 | """ 8 | If path is relative, prepend root 9 | If path is absolute, return it directly. 10 | """ 11 | root = Path(os.path.expanduser(str(root))) 12 | path = Path(path_str) 13 | if not path.is_absolute(): 14 | path = root / path 15 | return path.resolve() 16 | 17 | 18 | def mkdir(path_str: Union[str, Path]) -> None: 19 | """ 20 | Create the existing directory (and its parents) if necessary. 21 | """ 22 | path = Path(path_str) 23 | path.mkdir(parents=True, exist_ok=True) 24 | 25 | 26 | def make_path_relative(path_str: Union[str, Path], root: Path) -> Path: 27 | """ 28 | Try to make the given path relative, given the default root. 29 | """ 30 | path = Path(path_str) 31 | try: 32 | path = path.relative_to(root) 33 | except ValueError: 34 | pass 35 | return path 36 | -------------------------------------------------------------------------------- /equality/util/pip_import.py: -------------------------------------------------------------------------------- 1 | "Import a package and install it with PIP if it doesn't exist." 2 | 3 | import subprocess 4 | import sys 5 | 6 | 7 | def pip_import(module, pypi_name=None): 8 | """ 9 | Return None if we can't import or install it. 10 | """ 11 | try: 12 | return __import__(module) 13 | except ImportError: 14 | pass 15 | 16 | subprocess.call([sys.executable, "-m", "pip", "install", pypi_name or module]) 17 | return __import__(module) 18 | -------------------------------------------------------------------------------- /equality/util/prev_transaction_block.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from equality.consensus.block_record import BlockRecord 4 | from equality.consensus.blockchain_interface import BlockchainInterface 5 | from equality.util.ints import uint128 6 | 7 | 8 | def get_prev_transaction_block( 9 | curr: BlockRecord, 10 | blocks: BlockchainInterface, 11 | total_iters_sp: uint128, 12 | ) -> Tuple[bool, BlockRecord]: 13 | prev_transaction_block = curr 14 | while not curr.is_transaction_block: 15 | curr = blocks.block_record(curr.prev_hash) 16 | if total_iters_sp > curr.total_iters: 17 | prev_transaction_block = curr 18 | is_transaction_block = True 19 | else: 20 | is_transaction_block = False 21 | return is_transaction_block, prev_transaction_block 22 | -------------------------------------------------------------------------------- /equality/util/recursive_replace.py: -------------------------------------------------------------------------------- 1 | from dataclasses import replace 2 | from typing import Any 3 | 4 | 5 | def recursive_replace(root_obj: Any, replace_str: str, replace_with: Any) -> Any: 6 | split_str = replace_str.split(".") 7 | if len(split_str) == 1: 8 | return replace(root_obj, **{split_str[0]: replace_with}) 9 | sub_obj = recursive_replace(getattr(root_obj, split_str[0]), ".".join(split_str[1:]), replace_with) 10 | return replace(root_obj, **{split_str[0]: sub_obj}) 11 | -------------------------------------------------------------------------------- /equality/util/safe_cancel_task.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | from typing import Optional 4 | 5 | 6 | def cancel_task_safe(task: Optional[asyncio.Task], log: Optional[logging.Logger] = None): 7 | if task is not None: 8 | try: 9 | task.cancel() 10 | except Exception as e: 11 | if log is not None: 12 | log.error(f"Error while canceling task.{e} {task}") 13 | -------------------------------------------------------------------------------- /equality/util/service_groups.py: -------------------------------------------------------------------------------- 1 | from typing import KeysView, Generator 2 | 3 | SERVICES_FOR_GROUP = { 4 | "all": "equality_harvester equality_timelord_launcher equality_timelord equality_farmer equality_full_node equality_wallet".split(), 5 | "node": "equality_full_node".split(), 6 | "harvester": "equality_harvester".split(), 7 | "farmer": "equality_harvester equality_farmer equality_full_node equality_wallet".split(), 8 | "farmer-no-wallet": "equality_harvester equality_farmer equality_full_node".split(), 9 | "farmer-only": "equality_farmer".split(), 10 | "timelord": "equality_timelord_launcher equality_timelord equality_full_node".split(), 11 | "timelord-only": "equality_timelord".split(), 12 | "timelord-launcher-only": "equality_timelord_launcher".split(), 13 | "wallet": "equality_wallet equality_full_node".split(), 14 | "wallet-only": "equality_wallet".split(), 15 | "introducer": "equality_introducer".split(), 16 | "simulator": "equality_full_node_simulator".split(), 17 | } 18 | 19 | 20 | def all_groups() -> KeysView[str]: 21 | return SERVICES_FOR_GROUP.keys() 22 | 23 | 24 | def services_for_groups(groups) -> Generator[str, None, None]: 25 | for group in groups: 26 | for service in SERVICES_FOR_GROUP[group]: 27 | yield service 28 | 29 | 30 | def validate_service(service: str) -> bool: 31 | return any(service in _ for _ in SERVICES_FOR_GROUP.values()) 32 | -------------------------------------------------------------------------------- /equality/util/setproctitle.py: -------------------------------------------------------------------------------- 1 | try: 2 | import setproctitle as pysetproctitle 3 | 4 | no_setproctitle = False 5 | except Exception: 6 | no_setproctitle = True 7 | 8 | 9 | def setproctitle(ps_name: str) -> None: 10 | if no_setproctitle is False: 11 | pysetproctitle.setproctitle(ps_name) 12 | -------------------------------------------------------------------------------- /equality/util/significant_bits.py: -------------------------------------------------------------------------------- 1 | def truncate_to_significant_bits(input_x: int, num_significant_bits: int) -> int: 2 | """ 3 | Truncates the number such that only the top num_significant_bits contain 1s. 4 | and the rest of the number is 0s (in binary). Ignores decimals and leading 5 | zeroes. For example, -0b011110101 and 2, returns -0b11000000. 6 | """ 7 | x = abs(input_x) 8 | if num_significant_bits > x.bit_length(): 9 | return x 10 | lower = x.bit_length() - num_significant_bits 11 | mask = (1 << (x.bit_length())) - 1 - ((1 << lower) - 1) 12 | if input_x < 0: 13 | return -(x & mask) 14 | else: 15 | return x & mask 16 | 17 | 18 | def count_significant_bits(input_x: int) -> int: 19 | """ 20 | Counts the number of significant bits of an integer, ignoring negative signs 21 | and leading zeroes. For example, for -0b000110010000, returns 5. 22 | """ 23 | x = input_x 24 | for i in range(x.bit_length()): 25 | if x & (1 << i) > 0: 26 | return x.bit_length() - i 27 | return 0 28 | -------------------------------------------------------------------------------- /equality/util/struct_stream.py: -------------------------------------------------------------------------------- 1 | import io 2 | import struct 3 | from typing import Any, BinaryIO 4 | 5 | 6 | class StructStream(int): 7 | PACK = "" 8 | 9 | """ 10 | Create a class that can parse and stream itself based on a struct.pack template string. 11 | """ 12 | 13 | def __new__(cls: Any, value: int): 14 | bits = struct.calcsize(cls.PACK) * 8 15 | value = int(value) 16 | if value.bit_length() > bits: 17 | raise ValueError( 18 | f"Value {value} of size {value.bit_length()} does not fit into " f"{cls.__name__} of size {bits}" 19 | ) 20 | return int.__new__(cls, value) # type: ignore 21 | 22 | @classmethod 23 | def parse(cls: Any, f: BinaryIO) -> Any: 24 | bytes_to_read = struct.calcsize(cls.PACK) 25 | read_bytes = f.read(bytes_to_read) 26 | assert read_bytes is not None and len(read_bytes) == bytes_to_read 27 | return cls(*struct.unpack(cls.PACK, read_bytes)) 28 | 29 | def stream(self, f): 30 | f.write(struct.pack(self.PACK, self)) 31 | 32 | @classmethod 33 | def from_bytes(cls: Any, blob: bytes) -> Any: # type: ignore 34 | f = io.BytesIO(blob) 35 | result = cls.parse(f) 36 | assert f.read() == b"" 37 | return result 38 | 39 | def __bytes__(self: Any) -> bytes: 40 | f = io.BytesIO() 41 | self.stream(f) 42 | return bytes(f.getvalue()) 43 | -------------------------------------------------------------------------------- /equality/util/validate_alert.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | from blspy import AugSchemeMPL, PublicKeyMPL, SignatureMPL 5 | 6 | from equality.util.byte_types import hexstr_to_bytes 7 | from equality.util.hash import std_hash 8 | 9 | 10 | def validate_alert_file(file_path: Path, pubkey: str) -> bool: 11 | text = file_path.read_text() 12 | validated = validate_alert(text, pubkey) 13 | return validated 14 | 15 | 16 | def validate_alert(text: str, pubkey: str) -> bool: 17 | json_obj = json.loads(text) 18 | data = json_obj["data"] 19 | message = bytes(data, "UTF-8") 20 | signature = json_obj["signature"] 21 | signature = SignatureMPL.from_bytes(hexstr_to_bytes(signature)) 22 | pubkey_bls = PublicKeyMPL.from_bytes(hexstr_to_bytes(pubkey)) 23 | sig_match_my = AugSchemeMPL.verify(pubkey_bls, message, signature) 24 | 25 | return sig_match_my 26 | 27 | 28 | def create_alert_file(alert_file_path: Path, key, genesis_challenge_preimage: str): 29 | bytes_preimage = bytes(genesis_challenge_preimage, "UTF-8") 30 | genesis_challenge = std_hash(bytes_preimage) 31 | file_dict = { 32 | "ready": True, 33 | "genesis_challenge": genesis_challenge.hex(), 34 | "genesis_challenge_preimage": genesis_challenge_preimage, 35 | } 36 | data: str = json.dumps(file_dict) 37 | signature = AugSchemeMPL.sign(key, bytes(data, "utf-8")) 38 | file_data = {"data": data, "signature": f"{signature}"} 39 | file_data_json = json.dumps(file_data) 40 | alert_file_path.write_text(file_data_json) 41 | 42 | 43 | def create_not_ready_alert_file(alert_file_path: Path, key): 44 | file_dict = { 45 | "ready": False, 46 | } 47 | data: str = json.dumps(file_dict) 48 | signature = AugSchemeMPL.sign(key, bytes(data, "utf-8")) 49 | file_data = {"data": data, "signature": f"{signature}"} 50 | file_data_json = json.dumps(file_data) 51 | alert_file_path.write_text(file_data_json) 52 | -------------------------------------------------------------------------------- /equality/util/vdf_prover.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from chiavdf import prove 4 | 5 | from equality.consensus.constants import ConsensusConstants 6 | from equality.types.blockchain_format.classgroup import ClassgroupElement 7 | from equality.types.blockchain_format.sized_bytes import bytes32 8 | from equality.types.blockchain_format.vdf import VDFInfo, VDFProof 9 | from equality.util.ints import uint8, uint64 10 | 11 | 12 | def get_vdf_info_and_proof( 13 | constants: ConsensusConstants, 14 | vdf_input: ClassgroupElement, 15 | challenge_hash: bytes32, 16 | number_iters: uint64, 17 | normalized_to_identity: bool = False, 18 | ) -> Tuple[VDFInfo, VDFProof]: 19 | form_size = ClassgroupElement.get_size(constants) 20 | result: bytes = prove( 21 | bytes(challenge_hash), 22 | vdf_input.data, 23 | constants.DISCRIMINANT_SIZE_BITS, 24 | number_iters, 25 | ) 26 | 27 | output = ClassgroupElement.from_bytes(result[:form_size]) 28 | proof_bytes = result[form_size : 2 * form_size] 29 | return VDFInfo(challenge_hash, number_iters, output), VDFProof(uint8(0), proof_bytes, normalized_to_identity) 30 | -------------------------------------------------------------------------------- /equality/util/ws_message.py: -------------------------------------------------------------------------------- 1 | from secrets import token_bytes 2 | from typing import Any, Dict 3 | 4 | from equality.util.json_util import dict_to_json_str 5 | 6 | try: 7 | from typings import TypedDict 8 | except ImportError: 9 | from typing_extensions import TypedDict 10 | 11 | 12 | # Messages must follow this format 13 | # Message = { "command" "command_name", 14 | # "data" : {...}, 15 | # "request_id": "bytes_32", 16 | # "destination": "service_name", 17 | # "origin": "service_name" 18 | # } 19 | 20 | 21 | class WsRpcMessage(TypedDict): 22 | command: str 23 | ack: bool 24 | data: Dict[str, Any] 25 | request_id: str 26 | destination: str 27 | origin: str 28 | 29 | 30 | def format_response(incoming_msg: WsRpcMessage, response_data: Dict[str, Any]) -> str: 31 | """ 32 | Formats the response into standard format. 33 | """ 34 | response = { 35 | "command": incoming_msg["command"], 36 | "ack": True, 37 | "data": response_data, 38 | "request_id": incoming_msg["request_id"], 39 | "destination": incoming_msg["origin"], 40 | "origin": incoming_msg["destination"], 41 | } 42 | 43 | json_str = dict_to_json_str(response) 44 | return json_str 45 | 46 | 47 | def create_payload(command: str, data: Dict[str, Any], origin: str, destination: str) -> str: 48 | response = create_payload_dict(command, data, origin, destination) 49 | return dict_to_json_str(response) 50 | 51 | 52 | def create_payload_dict(command: str, data: Dict[str, Any], origin: str, destination: str) -> WsRpcMessage: 53 | return WsRpcMessage( 54 | command=command, 55 | ack=False, 56 | data=data, 57 | request_id=token_bytes().hex(), 58 | destination=destination, 59 | origin=origin, 60 | ) 61 | 62 | 63 | def pong() -> Dict[str, Any]: 64 | response = {"success": True} 65 | return response 66 | -------------------------------------------------------------------------------- /equality/wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/__init__.py -------------------------------------------------------------------------------- /equality/wallet/block_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from equality.types.blockchain_format.coin import Coin 5 | from equality.types.header_block import HeaderBlock 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class HeaderBlockRecord(Streamable): 12 | """ 13 | These are values that are stored in the wallet database, corresponding to information 14 | that the wallet cares about in each block 15 | """ 16 | 17 | header: HeaderBlock 18 | additions: List[Coin] # A block record without additions is not finished 19 | removals: List[Coin] # A block record without removals is not finished 20 | 21 | @property 22 | def header_hash(self): 23 | return self.header.header_hash 24 | 25 | @property 26 | def prev_header_hash(self): 27 | return self.header.prev_header_hash 28 | 29 | @property 30 | def height(self): 31 | return self.header.height 32 | 33 | @property 34 | def transactions_filter(self): 35 | return self.header.transactions_filter 36 | -------------------------------------------------------------------------------- /equality/wallet/cc_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/cc_wallet/__init__.py -------------------------------------------------------------------------------- /equality/wallet/cc_wallet/cc_info.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional, Tuple 3 | 4 | from equality.types.blockchain_format.program import Program 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class CCInfo(Streamable): 12 | my_genesis_checker: Optional[Program] # this is the program 13 | lineage_proofs: List[Tuple[bytes32, Optional[Program]]] # {coin.name(): lineage_proof} 14 | -------------------------------------------------------------------------------- /equality/wallet/cc_wallet/ccparent.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.ints import uint64 6 | from equality.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class CCParent(Streamable): 12 | parent_name: bytes32 13 | inner_puzzle_hash: Optional[bytes32] 14 | amount: uint64 15 | -------------------------------------------------------------------------------- /equality/wallet/derivation_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from blspy import G1Element 4 | 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.util.ints import uint32 7 | from equality.wallet.util.wallet_types import WalletType 8 | 9 | 10 | @dataclass(frozen=True) 11 | class DerivationRecord: 12 | """ 13 | These are records representing a puzzle hash, which is generated from a 14 | public key, derivation index, and wallet type. Stored in the puzzle_store. 15 | """ 16 | 17 | index: uint32 18 | puzzle_hash: bytes32 19 | pubkey: G1Element 20 | wallet_type: WalletType 21 | wallet_id: uint32 22 | -------------------------------------------------------------------------------- /equality/wallet/derive_keys.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from blspy import AugSchemeMPL, PrivateKey 4 | 5 | from equality.util.ints import uint32 6 | 7 | # EIP 2334 bls key derivation 8 | # https://eips.ethereum.org/EIPS/eip-2334 9 | # 12381 = bls spec number 10 | # 8444 = Chia blockchain number and port number for compatibility 11 | # 0, 1, 2, 3, 4, farmer, pool, wallet, local, backup key numbers 12 | 13 | 14 | def _derive_path(sk: PrivateKey, path: List[int]) -> PrivateKey: 15 | for index in path: 16 | sk = AugSchemeMPL.derive_child_sk(sk, index) 17 | return sk 18 | 19 | 20 | def master_sk_to_farmer_sk(master: PrivateKey) -> PrivateKey: 21 | return _derive_path(master, [12381, 8444, 0, 0]) 22 | 23 | 24 | def master_sk_to_pool_sk(master: PrivateKey) -> PrivateKey: 25 | return _derive_path(master, [12381, 8444, 1, 0]) 26 | 27 | 28 | def master_sk_to_wallet_sk(master: PrivateKey, index: uint32) -> PrivateKey: 29 | return _derive_path(master, [12381, 8444, 2, index]) 30 | 31 | 32 | def master_sk_to_local_sk(master: PrivateKey) -> PrivateKey: 33 | return _derive_path(master, [12381, 8444, 3, 0]) 34 | 35 | 36 | def master_sk_to_backup_sk(master: PrivateKey) -> PrivateKey: 37 | return _derive_path(master, [12381, 8444, 4, 0]) 38 | -------------------------------------------------------------------------------- /equality/wallet/did_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/did_wallet/__init__.py -------------------------------------------------------------------------------- /equality/wallet/did_wallet/did_info.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional, Tuple 3 | 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.ints import uint64 6 | from equality.util.streamable import streamable, Streamable 7 | from equality.wallet.cc_wallet.ccparent import CCParent 8 | from equality.types.blockchain_format.program import Program 9 | from equality.types.blockchain_format.coin import Coin 10 | 11 | 12 | @dataclass(frozen=True) 13 | @streamable 14 | class DIDInfo(Streamable): 15 | origin_coin: Optional[Coin] # puzzlehash of this coin is our DID 16 | backup_ids: List[bytes] 17 | num_of_backup_ids_needed: uint64 18 | parent_info: List[Tuple[bytes32, Optional[CCParent]]] # {coin.name(): CCParent} 19 | current_inner: Optional[Program] # represents a Program as bytes 20 | temp_coin: Optional[Coin] # partially recovered wallet uses these to hold info 21 | temp_puzhash: Optional[bytes32] 22 | temp_pubkey: Optional[bytes] 23 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/puzzles/__init__.py -------------------------------------------------------------------------------- /equality/wallet/puzzles/block_program_zero.clvm: -------------------------------------------------------------------------------- 1 | 2 | ; TODO convert generators arg to list of generators 3 | 4 | (mod (decompress_puzzle decompress_coin_solution_entry start end compressed_cses deserialize gen_list reserved_arg) 5 | 6 | (defun decompress_cses (decompress_puzzle decompress_coin_solution_entry cses deserialize puzzle_prefix) 7 | (if cses 8 | (c (a decompress_coin_solution_entry (list deserialize decompress_puzzle puzzle_prefix (f cses))) 9 | (decompress_cses decompress_puzzle decompress_coin_solution_entry (r cses) deserialize puzzle_prefix )) 10 | ()) ) 11 | 12 | (list (decompress_cses decompress_puzzle decompress_coin_solution_entry compressed_cses deserialize (substr (f gen_list) start end))) 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/block_program_zero.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff02ff02ffff04ff02ffff04ff05ffff04ff0bffff04ff5fffff04ff81bfffff04ffff0cff82027fff17ff2f80ff8080808080808080ff8080ffff04ffff01ff02ffff03ff17ffff01ff04ffff02ff0bffff04ff2fffff04ff05ffff04ff5fffff04ff27ff808080808080ffff02ff02ffff04ff02ffff04ff05ffff04ff0bffff04ff37ffff04ff2fffff04ff5fff808080808080808080ff8080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/block_program_zero.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | f0a38c8efe58895ae527c65c37f700a4238504691b83990e5dd91bd8b3c30eae 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/calculate_synthetic_public_key.clvm: -------------------------------------------------------------------------------- 1 | (mod 2 | (public_key hidden_puzzle_hash) 3 | 4 | (point_add public_key (pubkey_for_exp (sha256 public_key hidden_puzzle_hash))) 5 | ) 6 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/calculate_synthetic_public_key.clvm.hex: -------------------------------------------------------------------------------- 1 | ff1dff02ffff1effff0bff02ff05808080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/calculate_synthetic_public_key.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 624c5d5704d0decadfc0503e71bbffb6cdfe45025bce7cf3e6864d1eafe8f65e 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/cc.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | d4596fa7aa6eaa267ebce8d527546827de083d58fb4e14f4137c2448f7252e5c 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/cc_loader.py: -------------------------------------------------------------------------------- 1 | from equality.wallet.puzzles.load_clvm import load_clvm 2 | 3 | CC_MOD = load_clvm("cc.clvm", package_or_requirement=__name__) 4 | LOCK_INNER_PUZZLE = load_clvm("lock.inner.puzzle.clvm", package_or_requirement=__name__) 5 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/condition_codes.clvm: -------------------------------------------------------------------------------- 1 | ; See equality/types/condition_opcodes.py 2 | 3 | ( 4 | (defconstant AGG_SIG_UNSAFE 49) 5 | (defconstant AGG_SIG_ME 50) 6 | 7 | ; the conditions below reserve coin amounts and have to be accounted for in output totals 8 | 9 | (defconstant CREATE_COIN 51) 10 | (defconstant RESERVE_FEE 52) 11 | 12 | ; the conditions below deal with announcements, for inter-coin communication 13 | 14 | ; coin announcements 15 | (defconstant CREATE_COIN_ANNOUNCEMENT 60) 16 | (defconstant ASSERT_COIN_ANNOUNCEMENT 61) 17 | 18 | ; puzzle announcements 19 | (defconstant CREATE_PUZZLE_ANNOUNCEMENT 62) 20 | (defconstant ASSERT_PUZZLE_ANNOUNCEMENT 63) 21 | 22 | ; the conditions below let coins inquire about themselves 23 | 24 | (defconstant ASSERT_MY_COIN_ID 70) 25 | (defconstant ASSERT_MY_PARENT_ID 71) 26 | (defconstant ASSERT_MY_PUZZLEHASH 72) 27 | (defconstant ASSERT_MY_AMOUNT 73) 28 | 29 | ; the conditions below ensure that we're "far enough" in the future 30 | 31 | ; wall-clock time 32 | (defconstant ASSERT_SECONDS_RELATIVE 80) 33 | (defconstant ASSERT_SECONDS_ABSOLUTE 81) 34 | 35 | ; block index 36 | (defconstant ASSERT_HEIGHT_RELATIVE 82) 37 | (defconstant ASSERT_HEIGHT_ABSOLUTE 83) 38 | ) 39 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/create-lock-puzzlehash.clvm: -------------------------------------------------------------------------------- 1 | ; This puzzle evaluates to an empty list , which means no conditions need to be satisfied; 2 | ; The puzzle can never evaluate to my-id. 3 | ; my-id is there on a discarded branch of the tree as a signal to another piece of code 4 | ; that will be checking if a coin whose puzzle is of this form, was created or spent. 5 | ( 6 | (defun-inline create-lock-puzzlehash (my-id) 7 | (sha256tree (list r 8 | (list c 9 | (list q 10 | my-id) 11 | (q (q ())))))) 12 | ) -------------------------------------------------------------------------------- /equality/wallet/puzzles/create-lock-puzzlehash.clvm.hex.sha256tree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/puzzles/create-lock-puzzlehash.clvm.hex.sha256tree -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_coin_solution_entry.clvm: -------------------------------------------------------------------------------- 1 | (mod (deserialize decompress_puzzle puzzle_prefix suffix cse) 2 | 3 | ; decompress a single compressed standard transaction 4 | (c (f cse) (c (a decompress_puzzle (list deserialize puzzle_prefix (f (f (r cse))) suffix)) (c (f (r (f cse))) (r (f (r cse)))))) 5 | ) 6 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_coin_solution_entry.clvm.hex: -------------------------------------------------------------------------------- 1 | ff04ff4fffff04ffff02ff05ffff04ff02ffff04ff0bffff04ff82012fffff04ff17ff808080808080ffff04ff82014fff8201af808080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_coin_solution_entry.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 9d98ed08770d31be4bd1bde4705dab388db5e7e9c349f5a76fc3c347aa3a0b79 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_coin_solution_entry_with_prefix.clvm: -------------------------------------------------------------------------------- 1 | (mod (deserialize decompress_puzzle puzzle_prefix cse) 2 | 3 | ; decompress a single compressed standard transaction 4 | 5 | (c (f (f cse)) (c (a decompress_puzzle (list deserialize puzzle_prefix (f (f (r cse))) (q . 0xff018080))) (c (f (r (f cse))) (r (f (r cse)))))) 6 | 7 | ) 8 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_coin_solution_entry_with_prefix.clvm.hex: -------------------------------------------------------------------------------- 1 | ff04ff47ffff04ffff02ff05ffff04ff02ffff04ff0bffff04ff8197ffff01ff84ff0180808080808080ffff04ff81a7ff81d7808080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_coin_solution_entry_with_prefix.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 92aa4bc8060a8836355a1884075141b4791ce1b67ae6092bb166b2845954bc89 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_puzzle.clvm: -------------------------------------------------------------------------------- 1 | (mod (deserialize puzzle_prefix pubkey suffix) 2 | 3 | (a deserialize (list (concat puzzle_prefix pubkey suffix))) 4 | 5 | ) 6 | 7 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ff02ffff04ffff0eff05ff0bff1780ff808080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/decompress_puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | fe94c58f1117afe315e0450daca1c62460ec1a1c439cd4018d79967a5d7d1370 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/did_innerpuz.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 2f6e9a0237d200ac3b989d7c825ab68d732db6a2d1c8018e9f79d4be329eeed0 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/equalitylisp_deserialisation.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/equalitylisp_deserialisation.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 94ec19077f9a34e0b11ad2456af0170f4cc03f11230ca42e3f82e6e644ac4f5d 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/generator_for_single_coin.clvm: -------------------------------------------------------------------------------- 1 | (mod (block_program block_ref coinname) 2 | 3 | (defconstant local_deserialize_mod 4 | ;; this monstrosity is the assembly output of `equalitylisp_deserialisation.clvm` 5 | ;; it's pasted in here because the compiler doesn't yet support nested `mod` 6 | ;; my apologies -- RK 7 | 8 | (a (q 5 (a 62 (c 2 (c 5 ())))) 9 | (c (q ((-1 . 127) -33 . -65) ((a (i (= 11 (q . -128)) (q 4 () (c 5 ())) (q 2 (i (>s 11 24) (q 2 26 (c 2 (c (a (i (>s 11 28) (q 2 (i (>s 11 20) (q 8) (q 4 (concat (logand (q . 31) 11) (substr 5 () (q . 1))) (c (substr 5 (q . 1)) ()))) 1) (q 4 (logand (q . 63) 11) (c 5 ()))) 1) ()))) (q 4 11 (c 5 ()))) 1)) 1) 4 (substr 21 () 9) (c (substr 21 9) ())) (c (c 5 19) (c 43 ())) (a 22 (c 2 (c 9 (c (a 62 (c 2 (c 21 ()))) ())))) 2 (i (= (substr 5 () (q . 1)) 16) (q 2 46 (c 2 (c (a 62 (c 2 (c (substr 5 (q . 1)) ()))) ()))) (q 2 18 (c 2 (c (substr 5 (q . 1)) (c (substr 5 () (q . 1)) ()))))) 1) 10 | 1)) 11 | ) 12 | 13 | ; takes a lisp tree and returns the hash of it 14 | (defun sha256tree1 (TREE) 15 | (if (l TREE) 16 | (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) 17 | (sha256 1 TREE))) 18 | 19 | (defun check_coin_solution ((parent puzzle amount solution) coinname) 20 | (= (sha256 parent (sha256tree1 puzzle) amount) coinname) 21 | ) 22 | 23 | (defun check_for_coinname (coin_solutions coinname) 24 | (if coin_solutions 25 | (if (check_coin_solution (f coin_solutions) coinname) 26 | (list (f (r (f coin_solutions))) (f (r (r (r (f coin_solutions)))))) 27 | (check_for_coinname (r coin_solutions) coinname) 28 | ) 29 | (x) 30 | ) 31 | ) 32 | 33 | ; main 34 | (check_for_coinname (f (a block_program (list local_deserialize_mod block_ref))) coinname) 35 | ) 36 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/generator_for_single_coin.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ff0cffff04ff02ffff04ffff05ffff02ff05ffff04ff0affff04ff0bff8080808080ffff04ff17ff8080808080ffff04ffff01ffffff09ffff0bff09ffff02ff0effff04ff02ffff04ff15ff80808080ff2d80ff0b80ff02ffff03ff05ffff01ff02ffff03ffff02ff08ffff04ff02ffff04ff09ffff04ff0bff8080808080ffff01ff04ff29ffff04ff81b9ff808080ffff01ff02ff0cffff04ff02ffff04ff0dffff04ff0bff808080808080ff0180ffff01ff088080ff0180ffff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/generator_for_single_coin.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 303e2eeba68fd2d490a4af51dc9d175fcb33ae97a0ed242f9f08abc92c21898e 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/generator_loader.py: -------------------------------------------------------------------------------- 1 | from equality.wallet.puzzles.load_clvm import load_serialized_clvm 2 | 3 | GENERATOR_FOR_SINGLE_COIN_MOD = load_serialized_clvm("generator_for_single_coin.clvm", package_or_requirement=__name__) 4 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis-by-coin-id-with-0.clvm: -------------------------------------------------------------------------------- 1 | ; This is a "genesis checker" for use with cc.clvm. 2 | ; 3 | ; This checker allows new ccs to be created if they have a particular 4 | ; coin id as parent; or created by anyone if their value is 0. 5 | 6 | (mod ( 7 | genesis-id 8 | lineage-proof-parameters 9 | my-coin-info 10 | (parent-coin zero-parent-inner-puzzle-hash) 11 | ) 12 | 13 | ;; boolean or macro 14 | ;; This lets you write something like (if (or COND1 COND2 COND3) (do-something) (do-something-else)) 15 | (defmacro or ARGS 16 | (if ARGS 17 | (qq (if (unquote (f ARGS)) 18 | 1 19 | (unquote (c or (r ARGS))) 20 | )) 21 | 0) 22 | ) 23 | 24 | (defun-inline main ( 25 | genesis-id 26 | my-coin-info 27 | ) 28 | 29 | (or 30 | (= (f (r (r my-coin-info))) 0) 31 | (= (f my-coin-info) genesis-id) 32 | ) 33 | ) 34 | 35 | (main 36 | genesis-id 37 | my-coin-info 38 | ) 39 | ) -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis-by-coin-id-with-0.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff03ffff09ff5bff8080ffff01ff0101ffff01ff02ffff03ffff09ff13ff0280ffff01ff0101ff8080ff018080ff0180 -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis-by-coin-id-with-0.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 258008f81f21c270f4b58488b108a46a35e5df43ca5b0313ac83e900a5e44a5f 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis-by-puzzle-hash-with-0.clvm: -------------------------------------------------------------------------------- 1 | ; This is a "genesis checker" for use with cc.clvm. 2 | ; 3 | ; This checker allows new ccs to be created if their parent has a particular 4 | ; puzzle hash; or created by anyone if their value is 0. 5 | 6 | (mod ( 7 | genesis-puzzle-hash 8 | lineage-proof-parameters 9 | my-coin-info 10 | (parent-coin zero-parent-inner-puzzle-hash) 11 | ) 12 | 13 | ;; boolean and macro 14 | ;; This lets you write something like (if (and COND1 COND2 COND3) (do-something) (do-something-else)) 15 | (defmacro and ARGS 16 | (if ARGS 17 | (qq (if (unquote (f ARGS)) 18 | (unquote (c and (r ARGS))) 19 | () 20 | )) 21 | 1) 22 | ) 23 | 24 | ;; boolean or macro 25 | ;; This lets you write something like (if (or COND1 COND2 COND3) (do-something) (do-something-else)) 26 | (defmacro or ARGS 27 | (if ARGS 28 | (qq (if (unquote (f ARGS)) 29 | 1 30 | (unquote (c or (r ARGS))) 31 | )) 32 | 0) 33 | ) 34 | 35 | (defun-inline main ( 36 | genesis-puzzle-hash 37 | my-coin-info 38 | parent-coin 39 | ) 40 | 41 | (or 42 | (= (f (r (r my-coin-info))) 0) 43 | (and 44 | (= (sha256 (f parent-coin) (f (r parent-coin)) (f (r (r parent-coin)))) (f my-coin-info)) 45 | (= (f (r parent-coin)) genesis-puzzle-hash) 46 | ) 47 | ) 48 | ) 49 | 50 | (main 51 | genesis-puzzle-hash 52 | my-coin-info 53 | parent-coin 54 | ) 55 | ) -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis-by-puzzle-hash-with-0.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff03ffff09ff5bff8080ffff01ff0101ffff01ff02ffff03ffff02ffff03ffff09ffff0bff47ff81a7ff82016780ff1380ffff01ff02ffff03ffff09ff81a7ff0280ffff01ff0101ff8080ff0180ff8080ff0180ffff01ff0101ff8080ff018080ff0180 -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis-by-puzzle-hash-with-0.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 795964e0324fbc08e8383d67659194a70455956ad1ebd2329ccf20008da00936 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis_by_coin_id_with_0.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from equality.types.blockchain_format.coin import Coin 4 | from equality.types.blockchain_format.program import Program 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.wallet.puzzles.load_clvm import load_clvm 7 | 8 | MOD = load_clvm("genesis-by-coin-id-with-0.clvm", package_or_requirement=__name__) 9 | 10 | 11 | def create_genesis_or_zero_coin_checker(genesis_coin_id: bytes32) -> Program: 12 | """ 13 | Given a specific genesis coin id, create a `genesis_coin_mod` that allows 14 | both that coin id to issue a cc, or anyone to create a cc with amount 0. 15 | """ 16 | genesis_coin_mod = MOD 17 | return genesis_coin_mod.curry(genesis_coin_id) 18 | 19 | 20 | def genesis_coin_id_for_genesis_coin_checker( 21 | genesis_coin_checker: Program, 22 | ) -> Optional[bytes32]: 23 | """ 24 | Given a `genesis_coin_checker` program, pull out the genesis coin id. 25 | """ 26 | r = genesis_coin_checker.uncurry() 27 | if r is None: 28 | return r 29 | f, args = r 30 | if f != MOD: 31 | return None 32 | return args.first().as_atom() 33 | 34 | 35 | def lineage_proof_for_genesis(parent_coin: Coin) -> Program: 36 | return Program.to((0, [parent_coin.as_list(), 0])) 37 | 38 | 39 | def lineage_proof_for_zero(parent_coin: Coin) -> Program: 40 | return Program.to((0, [parent_coin.as_list(), 1])) 41 | 42 | 43 | def lineage_proof_for_coin(parent_coin: Coin) -> Program: 44 | if parent_coin.amount == 0: 45 | return lineage_proof_for_zero(parent_coin) 46 | return lineage_proof_for_genesis(parent_coin) 47 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/genesis_by_puzzle_hash_with_0.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from equality.types.blockchain_format.coin import Coin 4 | from equality.types.blockchain_format.program import Program 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.wallet.puzzles.load_clvm import load_clvm 7 | 8 | MOD = load_clvm("genesis-by-puzzle-hash-with-0.clvm", package_or_requirement=__name__) 9 | 10 | 11 | def create_genesis_puzzle_or_zero_coin_checker(genesis_puzzle_hash: bytes32) -> Program: 12 | """ 13 | Given a specific genesis coin id, create a `genesis_coin_mod` that allows 14 | both that coin id to issue a cc, or anyone to create a cc with amount 0. 15 | """ 16 | genesis_coin_mod = MOD 17 | return genesis_coin_mod.curry(genesis_puzzle_hash) 18 | 19 | 20 | def genesis_puzzle_hash_for_genesis_coin_checker( 21 | genesis_coin_checker: Program, 22 | ) -> Optional[bytes32]: 23 | """ 24 | Given a `genesis_coin_checker` program, pull out the genesis puzzle hash. 25 | """ 26 | r = genesis_coin_checker.uncurry() 27 | if r is None: 28 | return r 29 | f, args = r 30 | if f != MOD: 31 | return None 32 | return args.first().as_atom() 33 | 34 | 35 | def lineage_proof_for_genesis_puzzle(parent_coin: Coin) -> Program: 36 | return Program.to((0, [parent_coin.as_list(), 0])) 37 | 38 | 39 | def lineage_proof_for_zero(parent_coin: Coin) -> Program: 40 | return Program.to((0, [parent_coin.as_list(), 1])) 41 | 42 | 43 | def lineage_proof_for_coin(parent_coin: Coin) -> Program: 44 | if parent_coin.amount == 0: 45 | return lineage_proof_for_zero(parent_coin) 46 | return lineage_proof_for_genesis_puzzle(parent_coin) 47 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/load_clvm.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | import pkg_resources 4 | from clvm_tools.clvmc import compile_clvm 5 | 6 | from equality.types.blockchain_format.program import Program, SerializedProgram 7 | 8 | 9 | def load_serialized_clvm(clvm_filename, package_or_requirement=__name__) -> SerializedProgram: 10 | """ 11 | This function takes a .clvm file in the given package and compiles it to a 12 | .clvm.hex file if the .hex file is missing or older than the .clvm file, then 13 | returns the contents of the .hex file as a `Program`. 14 | 15 | clvm_filename: file name 16 | package_or_requirement: usually `__name__` if the clvm file is in the same package 17 | """ 18 | 19 | hex_filename = f"{clvm_filename}.hex" 20 | 21 | try: 22 | if pkg_resources.resource_exists(package_or_requirement, clvm_filename): 23 | full_path = pathlib.Path(pkg_resources.resource_filename(package_or_requirement, clvm_filename)) 24 | output = full_path.parent / hex_filename 25 | compile_clvm(full_path, output, search_paths=[full_path.parent]) 26 | except NotImplementedError: 27 | # pyinstaller doesn't support `pkg_resources.resource_exists` 28 | # so we just fall through to loading the hex clvm 29 | pass 30 | 31 | clvm_hex = pkg_resources.resource_string(package_or_requirement, hex_filename).decode("utf8") 32 | clvm_blob = bytes.fromhex(clvm_hex) 33 | return SerializedProgram.from_bytes(clvm_blob) 34 | 35 | 36 | def load_clvm(clvm_filename, package_or_requirement=__name__) -> Program: 37 | return Program.from_bytes(bytes(load_serialized_clvm(clvm_filename, package_or_requirement=__name__))) 38 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/lock.inner.puzzle.clvm: -------------------------------------------------------------------------------- 1 | ; a trivial puzzle used as the core of a lock puzzle 2 | (mod args (q ())) 3 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/lock.inner.puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff01ff8080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/lock.inner.puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | f3a579254623f8094e07af862df2e45c9db5592b4105d34a256dd6c498416288 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_conditions.clvm: -------------------------------------------------------------------------------- 1 | (mod (conditions) 2 | (qq (q . (unquote conditions))) 3 | ) 4 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_conditions.clvm.hex: -------------------------------------------------------------------------------- 1 | ff04ffff0101ff0280 -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_conditions.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 1c77d7d5efde60a7a1d2d27db6d746bc8e568aea1ef8586ca967a0d60b83cc36 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_conditions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pay to conditions 3 | 4 | In this puzzle program, the solution is ignored. The reveal of the puzzle 5 | returns a fixed list of conditions. This roughly corresponds to OP_SECURETHEBAG 6 | in bitcoin. 7 | 8 | This is a pretty useless most of the time. But some (most?) solutions 9 | require a delegated puzzle program, so in those cases, this is just what 10 | the doctor ordered. 11 | """ 12 | 13 | from equality.types.blockchain_format.program import Program 14 | 15 | from .load_clvm import load_clvm 16 | 17 | MOD = load_clvm("p2_conditions.clvm") 18 | 19 | 20 | def puzzle_for_conditions(conditions) -> Program: 21 | return MOD.run([conditions]) 22 | 23 | 24 | def solution_for_conditions(conditions) -> Program: 25 | return Program.to([puzzle_for_conditions(conditions), 0]) 26 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_conditions.clvm: -------------------------------------------------------------------------------- 1 | (mod 2 | (public_key conditions) 3 | 4 | (include condition_codes.clvm) 5 | 6 | ;; hash a tree 7 | ;; This is used to calculate a puzzle hash given a puzzle program. 8 | (defun sha256tree1 9 | (TREE) 10 | (if (l TREE) 11 | (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) 12 | (sha256 1 TREE) 13 | ) 14 | ) 15 | 16 | (c (list AGG_SIG_ME public_key (sha256tree1 conditions)) conditions) 17 | 18 | ) 19 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_conditions.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff0bff80808080ff80808080ff0b80ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_conditions.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 0ff94726f1a8dea5c3f70d3121945190778d3b2b3fcda3735a1f290977e98341 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_conditions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pay to delegated conditions 3 | 4 | In this puzzle program, the solution must be a signed list of conditions, which 5 | is returned literally. 6 | """ 7 | 8 | 9 | from equality.types.blockchain_format.program import Program 10 | 11 | from .load_clvm import load_clvm 12 | 13 | MOD = load_clvm("p2_delegated_conditions.clvm") 14 | 15 | 16 | def puzzle_for_pk(public_key: Program) -> Program: 17 | return MOD.curry(public_key) 18 | 19 | 20 | def solution_for_conditions(conditions: Program) -> Program: 21 | return conditions.to([conditions]) 22 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_puzzle.clvm: -------------------------------------------------------------------------------- 1 | (mod 2 | 3 | (public_key delegated_puzzle delegated_puzzle_solution) 4 | 5 | (include condition_codes.clvm) 6 | 7 | ;; hash a tree 8 | ;; This is used to calculate a puzzle hash given a puzzle program. 9 | (defun sha256tree1 10 | (TREE) 11 | (if (l TREE) 12 | (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) 13 | (sha256 1 TREE) 14 | ) 15 | ) 16 | 17 | (c (list AGG_SIG_ME public_key (sha256tree1 delegated_puzzle)) 18 | (a delegated_puzzle delegated_puzzle_solution)) 19 | ) 20 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff0bff80808080ff80808080ffff02ff0bff178080ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 542cde70d1102cd1b763220990873efc8ab15625ded7eae22cc11e21ef2e2f7c 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_puzzle.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pay to delegated puzzle 3 | 4 | In this puzzle program, the solution must be a signed delegated puzzle, along with 5 | its (unsigned) solution. The delegated puzzle is executed, passing in the solution. 6 | This obviously could be done recursively, arbitrarily deep (as long as the maximum 7 | cost is not exceeded). 8 | 9 | If you want to specify the conditions directly (thus terminating the potential recursion), 10 | you can use p2_conditions. 11 | 12 | This roughly corresponds to bitcoin's graftroot. 13 | """ 14 | 15 | from equality.types.blockchain_format.program import Program 16 | 17 | from . import p2_conditions 18 | from .load_clvm import load_clvm 19 | 20 | MOD = load_clvm("p2_delegated_puzzle.clvm") 21 | 22 | 23 | def puzzle_for_pk(public_key: bytes) -> Program: 24 | return MOD.curry(public_key) 25 | 26 | 27 | def solution_for_conditions(conditions) -> Program: 28 | delegated_puzzle = p2_conditions.puzzle_for_conditions(conditions) 29 | return solution_for_delegated_puzzle(delegated_puzzle, Program.to(0)) 30 | 31 | 32 | def solution_for_delegated_puzzle(delegated_puzzle: Program, delegated_solution: Program) -> Program: 33 | return delegated_puzzle.to([delegated_puzzle, delegated_solution]) 34 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | e9aaa49f45bad5c889b86ee3341550c155cfdd10c3a6757de618d20612fffd52 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_m_of_n_delegate_direct.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ffff09ff05ffff02ff16ffff04ff02ffff04ff17ff8080808080ffff01ff02ff0cffff04ff02ffff04ffff02ff0affff04ff02ffff04ff17ffff04ff0bff8080808080ffff04ffff02ff1effff04ff02ffff04ff2fff80808080ffff04ff2fffff04ff5fff80808080808080ffff01ff088080ff0180ffff04ffff01ffff31ff02ffff03ff05ffff01ff04ffff04ff08ffff04ff09ffff04ff0bff80808080ffff02ff0cffff04ff02ffff04ff0dffff04ff0bffff04ff17ffff04ff2fff8080808080808080ffff01ff02ff17ff2f8080ff0180ffff02ffff03ff05ffff01ff02ffff03ff09ffff01ff04ff13ffff02ff0affff04ff02ffff04ff0dffff04ff1bff808080808080ffff01ff02ff0affff04ff02ffff04ff0dffff04ff1bff808080808080ff0180ff8080ff0180ffff02ffff03ff05ffff01ff10ffff02ff16ffff04ff02ffff04ff0dff80808080ffff02ffff03ff09ffff01ff0101ff8080ff018080ff8080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff1effff04ff02ffff04ff09ff80808080ffff02ff1effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_m_of_n_delegate_direct.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 0f199d5263ac1a62b077c159404a71abd3f9691cc57520bf1d4c5cb501504457 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_m_of_n_delegate_direct.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pay to m of n direct 3 | 4 | This puzzle program is like p2_delegated_puzzle except instead of one public key, 5 | it includes N public keys, any M of which needs to sign the delegated puzzle. 6 | """ 7 | 8 | from equality.types.blockchain_format.program import Program 9 | 10 | from .load_clvm import load_clvm 11 | 12 | MOD = load_clvm("p2_m_of_n_delegate_direct.clvm") 13 | 14 | 15 | def puzzle_for_m_of_public_key_list(m, public_key_list) -> Program: 16 | return MOD.curry(m, public_key_list) 17 | 18 | 19 | def solution_for_delegated_puzzle(m, selectors, puzzle, solution) -> Program: 20 | return Program.to([selectors, puzzle, solution]) 21 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_puzzle_hash.clvm: -------------------------------------------------------------------------------- 1 | (mod 2 | (inner_puzzle_hash inner_puzzle inner_puzzle_solution) 3 | 4 | ;; hash a tree 5 | ;; This is used to calculate a puzzle hash given a puzzle program. 6 | (defun sha256tree1 7 | (TREE) 8 | (if (l TREE) 9 | (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) 10 | (sha256 1 TREE) 11 | ) 12 | ) 13 | 14 | (if (= inner_puzzle_hash (sha256tree1 inner_puzzle)) 15 | (a inner_puzzle inner_puzzle_solution) 16 | (x) 17 | ) 18 | ) -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_puzzle_hash.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ffff09ff05ffff02ff02ffff04ff02ffff04ff0bff8080808080ffff01ff02ff0bff1780ffff01ff088080ff0180ffff04ffff01ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff02ffff04ff02ffff04ff09ff80808080ffff02ff02ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_puzzle_hash.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 13e29a62b42cd2ef72a79e4bacdc59733ca6310d65af83d349360d36ec622363 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/p2_puzzle_hash.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pay to puzzle hash 3 | 4 | In this puzzle program, the solution must be a reveal of the puzzle with the given 5 | hash along with its solution. 6 | """ 7 | 8 | from equality.types.blockchain_format.program import Program 9 | from equality.types.blockchain_format.sized_bytes import bytes32 10 | 11 | from .load_clvm import load_clvm 12 | 13 | MOD = load_clvm("p2_puzzle_hash.clvm") 14 | 15 | 16 | def puzzle_for_inner_puzzle_hash(inner_puzzle_hash: bytes32) -> Program: 17 | program = MOD.curry(inner_puzzle_hash) 18 | return program 19 | 20 | 21 | def puzzle_for_inner_puzzle(inner_puzzle: Program) -> Program: 22 | return puzzle_for_inner_puzzle_hash(inner_puzzle.get_tree_hash()) 23 | 24 | 25 | def solution_for_inner_puzzle_and_inner_solution(inner_puzzle: Program, inner_puzzle_solution: Program) -> Program: 26 | return Program.to([inner_puzzle, inner_puzzle_solution]) 27 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/puzzle_utils.py: -------------------------------------------------------------------------------- 1 | from equality.util.condition_tools import ConditionOpcode 2 | 3 | 4 | def make_create_coin_condition(puzzle_hash, amount): 5 | return [ConditionOpcode.CREATE_COIN, puzzle_hash, amount] 6 | 7 | 8 | def make_assert_aggsig_condition(pubkey): 9 | return [ConditionOpcode.AGG_SIG_UNSAFE, pubkey] 10 | 11 | 12 | def make_assert_my_coin_id_condition(coin_name): 13 | return [ConditionOpcode.ASSERT_MY_COIN_ID, coin_name] 14 | 15 | 16 | def make_assert_absolute_height_exceeds_condition(block_index): 17 | return [ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, block_index] 18 | 19 | 20 | def make_assert_relative_height_exceeds_condition(block_index): 21 | return [ConditionOpcode.ASSERT_HEIGHT_RELATIVE, block_index] 22 | 23 | 24 | def make_assert_absolute_seconds_exceeds_condition(time): 25 | return [ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, time] 26 | 27 | 28 | def make_assert_relative_seconds_exceeds_condition(time): 29 | return [ConditionOpcode.ASSERT_SECONDS_RELATIVE, time] 30 | 31 | 32 | def make_reserve_fee_condition(fee): 33 | return [ConditionOpcode.RESERVE_FEE, fee] 34 | 35 | 36 | def make_assert_coin_announcement(announcement_hash): 37 | return [ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, announcement_hash] 38 | 39 | 40 | def make_assert_puzzle_announcement(announcement_hash): 41 | return [ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, announcement_hash] 42 | 43 | 44 | def make_create_coin_announcement(message): 45 | return [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, message] 46 | 47 | 48 | def make_create_puzzle_announcement(message): 49 | return [ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, message] 50 | 51 | 52 | def make_assert_my_parent_id(parent_id): 53 | return [ConditionOpcode.ASSERT_MY_PARENT_ID, parent_id] 54 | 55 | 56 | def make_assert_my_puzzlehash(puzzlehash): 57 | return [ConditionOpcode.ASSERT_MY_PUZZLEHASH, puzzlehash] 58 | 59 | 60 | def make_assert_my_amount(amount): 61 | return [ConditionOpcode.ASSERT_MY_AMOUNT, amount] 62 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/recompile-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This hack is a quick way to recompile everything in this directory 4 | 5 | #BASE_DIR=`pwd | dirname` 6 | 7 | FILES=$(ls ./*.clvm) 8 | echo "$FILES" 9 | 10 | INCLUDE_DIR=$(pwd) 11 | 12 | for FILE in $FILES 13 | do 14 | echo "run -d -i $INCLUDE_DIR $FILE > $FILE.hex" 15 | # run -d -i $INCLUDE_DIR $FILE > $FILE.hex 16 | done 17 | 18 | for FILE in $FILES 19 | do 20 | echo "opd -H $FILE.hex | head -1 > $FILE.hex.sha256tree" 21 | done 22 | 23 | echo 24 | echo "Copy & paste the above to the shell to recompile" 25 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rl.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ffff09ff81bfff2480ffff01ff04ffff04ff30ffff04ff5fffff04ffff02ff3effff04ff02ffff04ffff04ff81bfff81ff80ff80808080ff80808080ff81ff80ffff01ff04ffff04ff30ffff04ff05ffff04ffff02ff3effff04ff02ffff04ffff04ff81bfff81ff80ff80808080ff80808080ffff02ffff03ffff09ff81bfff3c80ffff01ff02ff2effff04ff02ffff04ff0bffff04ff17ffff04ff2fffff04ff81ffff80808080808080ffff01ff02ff22ffff04ff02ffff04ff2fffff04ff81ffff808080808080ff01808080ff0180ffff04ffff01ffffffffff02ffff03ffff15ff05ff0b80ffff01ff0101ffff01ff02ffff03ffff09ff05ff0b80ffff01ff0101ff8080ff018080ff018031ff5246ffff0333ff3c01ffffffff02ffff03ffff02ffff03ffff09ffff0bff820bfbff13ff8205fb80ff82017b80ffff01ff0101ffff01ff02ffff03ffff09ff05ff82017b80ffff01ff0101ff8080ff018080ff0180ffff01ff04ffff02ff26ffff04ff02ffff04ff82017bffff04ff13ffff04ff8202fbff808080808080ffff04ffff02ff2affff04ff02ffff04ff2bffff04ff5bffff04ff81bbff808080808080ffff04ffff02ff3affff04ff02ffff04ff13ffff04ffff10ff81bbff8202fb80ff8080808080ff80808080ffff01ffff08808080ff0180ff04ff34ffff04ff05ffff04ffff11ff0bffff10ff17ff2f8080ff80808080ffff04ff2cffff04ffff0bff05ff0bff1780ff808080ff04ff34ffff04ff05ffff04ff0bff80808080ffffff04ff38ffff04ffff0bff05ff0bff1780ff808080ff02ffff03ffff02ff20ffff04ff02ffff04ffff12ff05ff1780ffff04ffff12ff0bff2f80ff8080808080ffff01ff04ff28ffff04ff05ff808080ffff01ffff08808080ff0180ffff02ffff03ffff02ffff03ffff09ffff0bff8217efff81afff822fef80ff4f80ffff01ff0101ffff01ff02ffff03ffff09ff17ff4f80ffff01ff0101ff8080ff018080ff0180ffff01ff04ffff02ff36ffff04ff02ffff04ff820befffff04ff8205efffff04ff05ffff04ff0bff80808080808080ffff04ffff02ff32ffff04ff02ffff04ff81afffff04ff82016fffff04ff8205efffff04ff825fefff80808080808080ffff04ffff02ff26ffff04ff02ffff04ff4fffff04ff81afffff04ff82016fff808080808080ffff04ffff02ff3affff04ff02ffff04ff8202efffff04ff8205efff8080808080ff8080808080ffff01ffff08808080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff3effff04ff02ffff04ff09ff80808080ffff02ff3effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rl.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | f663796a8c522b85bd9472cbea2bf7f138e8351e8e4032706fc0539e87f94faf 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rl_aggregation.clvm: -------------------------------------------------------------------------------- 1 | (mod (wallet_puzzle 2 | my-id 3 | wallet-coin-primary-input 4 | wallet-coin-amount) 5 | 6 | (include condition_codes.clvm) 7 | 8 | (defun sha256tree (tree) 9 | (if (l tree) 10 | (sha256 2 (sha256tree (f tree)) (sha256tree (r tree))) 11 | (sha256 1 tree))) 12 | 13 | (defun-inline create-my-id-condition () 14 | (list ASSERT_MY_COIN_ID my-id)) 15 | 16 | (include create-lock-puzzlehash.clvm) 17 | 18 | (defun-inline parent-coin-id () 19 | (sha256 wallet-coin-primary-input wallet_puzzle wallet-coin-amount)) 20 | 21 | (defun-inline input-of-lock () 22 | (list ASSERT_COIN_ANNOUNCEMENT (sha256 (parent-coin-id) my-id))) 23 | 24 | (list (create-my-id-condition) 25 | (input-of-lock)) 26 | ) 27 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rl_aggregation.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff06ffff04ff0bff808080ffff04ffff04ff04ffff04ffff0bffff0bff17ff05ff2f80ff0b80ff808080ff808080ffff04ffff01ff3d46ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/rl_aggregation.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 007400187f63927ee023a7172bb88f14d49aaa4beb790ecaf7dde7c1a79b6481 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rom_bootstrap_generator.clvm: -------------------------------------------------------------------------------- 1 | (mod (block_decompresser_program (historical_blocks_tree)) 2 | 3 | (defconstant local_deserialize_mod 4 | ;; this monstrosity is the assembly output of `equalitylisp_deserialisation.clvm` 5 | ;; it's pasted in here because the compiler doesn't yet support nested `mod` 6 | ;; my apologies -- RK 7 | 8 | (a (q 5 (a 62 (c 2 (c 5 ())))) 9 | (c (q ((-1 . 127) -33 . -65) ((a (i (= 11 (q . -128)) (q 4 () (c 5 ())) (q 2 (i (>s 11 24) (q 2 26 (c 2 (c (a (i (>s 11 28) (q 2 (i (>s 11 20) (q 8) (q 4 (concat (logand (q . 31) 11) (substr 5 () (q . 1))) (c (substr 5 (q . 1)) ()))) 1) (q 4 (logand (q . 63) 11) (c 5 ()))) 1) ()))) (q 4 11 (c 5 ()))) 1)) 1) 4 (substr 21 () 9) (c (substr 21 9) ())) (c (c 5 19) (c 43 ())) (a 22 (c 2 (c 9 (c (a 62 (c 2 (c 21 ()))) ())))) 2 (i (= (substr 5 () (q . 1)) 16) (q 2 46 (c 2 (c (a 62 (c 2 (c (substr 5 (q . 1)) ()))) ()))) (q 2 18 (c 2 (c (substr 5 (q . 1)) (c (substr 5 () (q . 1)) ()))))) 1) 10 | 1)) 11 | ) 12 | 13 | (defun sha256tree 14 | (TREE) 15 | (if (l TREE) 16 | (sha256 2 (sha256tree (f TREE)) (sha256tree (r TREE))) 17 | (sha256 1 TREE) 18 | ) 19 | ) 20 | 21 | (defun process_coin_solution ((parent puzzle amount solution . spend_level_extras)) 22 | (c parent (c (sha256tree puzzle) (c amount (c (a puzzle solution) spend_level_extras)))) 23 | ) 24 | 25 | (defun recurse (coin_solutions) 26 | (if coin_solutions 27 | (c (process_coin_solution (f coin_solutions)) (recurse (r coin_solutions))) 28 | 0 29 | ) 30 | ) 31 | 32 | (defun process-decompressor ((coin_solutions . block-level-extras)) 33 | (c (recurse coin_solutions) block-level-extras) 34 | ) 35 | 36 | (process-decompressor (a block_decompresser_program (list local_deserialize_mod historical_blocks_tree)))) 37 | ) 38 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rom_bootstrap_generator.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ff0cffff04ff02ffff04ffff02ff05ffff04ff08ffff04ff13ff80808080ff80808080ffff04ffff01ffffff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080ff04ffff02ff16ffff04ff02ffff04ff09ff80808080ff0d80ffff04ff09ffff04ffff02ff1effff04ff02ffff04ff15ff80808080ffff04ff2dffff04ffff02ff15ff5d80ff7d80808080ffff02ffff03ff05ffff01ff04ffff02ff0affff04ff02ffff04ff09ff80808080ffff02ff16ffff04ff02ffff04ff0dff8080808080ff8080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff1effff04ff02ffff04ff09ff80808080ffff02ff1effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/rom_bootstrap_generator.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 161bade1f822dcd62ab712ebaf30f3922a301e48a639e4295c5685f8bece7bd9 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/rom_bootstrap_generator.py: -------------------------------------------------------------------------------- 1 | from equality.types.blockchain_format.program import SerializedProgram 2 | 3 | from .load_clvm import load_clvm 4 | 5 | MOD = SerializedProgram.from_bytes(load_clvm("rom_bootstrap_generator.clvm").as_bin()) 6 | 7 | 8 | def get_generator(): 9 | return MOD 10 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/sha256tree_module.clvm: -------------------------------------------------------------------------------- 1 | ( 2 | mod (program) 3 | 4 | ;; hash a tree 5 | ;; This is used to calculate a puzzle hash given a puzzle program. 6 | (defun sha256tree 7 | (TREE) 8 | (if (l TREE) 9 | (sha256 2 (sha256tree (f TREE)) (sha256tree (r TREE))) 10 | (sha256 1 TREE) 11 | ) 12 | ) 13 | 14 | (sha256tree program) 15 | ) -------------------------------------------------------------------------------- /equality/wallet/puzzles/sha256tree_module.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ff02ffff04ff02ffff04ff05ff80808080ffff04ffff01ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff02ffff04ff02ffff04ff09ff80808080ffff02ff02ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/sha256tree_module.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | eb4ead6576048c9d730b5ced00646c7fdd390649cfdf48a00de1590cdd8ee18f 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/singleton_top_layer.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | cb48893e85dfbcee15ceb21187cec39a31764381fd887d604f4727e8ff30e0b5 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/test_generator_deserialize.clvm: -------------------------------------------------------------------------------- 1 | (mod (deserializer generator_list reserved_arg) 2 | (a deserializer (list reserved_arg)) 3 | ) 4 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/test_generator_deserialize.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ff02ffff04ff0bff808080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/test_generator_deserialize.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 52add794fc76e89512e4a063c383418bda084c8a78c74055abe80179e4a7832c 2 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/test_multiple_generator_input_arguments.clvm: -------------------------------------------------------------------------------- 1 | 2 | (mod (decompress_puzzle decompress_coin_solution_entry start1 end1 start2 end2 compressed_cses deserialize gen_list reserved_arg) 3 | 4 | (defun decompress_cses (decompress_puzzle decompress_coin_solution_entry cses deserialize puzzle_prefix) 5 | (if cses 6 | (c (a decompress_coin_solution_entry (list deserialize decompress_puzzle puzzle_prefix (f cses))) 7 | (decompress_cses decompress_puzzle decompress_coin_solution_entry (r cses) deserialize puzzle_prefix )) 8 | ()) ) 9 | 10 | (defun join_gen_args (generators start1 end1 start2 end2) 11 | (concat 12 | (substr (f generators) start1 end1) 13 | (substr (f (r generators)) start2 end2) 14 | ) 15 | ) 16 | 17 | (list (decompress_cses decompress_puzzle decompress_coin_solution_entry compressed_cses deserialize (join_gen_args gen_list start1 end1 start2 end2))) 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /equality/wallet/puzzles/test_multiple_generator_input_arguments.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff02ff04ffff04ff02ffff04ff05ffff04ff0bffff04ff82017fffff04ff8202ffffff04ffff02ff06ffff04ff02ffff04ff8205ffffff04ff17ffff04ff2fffff04ff5fffff04ff81bfff8080808080808080ff8080808080808080ff8080ffff04ffff01ffff02ffff03ff17ffff01ff04ffff02ff0bffff04ff2fffff04ff05ffff04ff5fffff04ff27ff808080808080ffff02ff04ffff04ff02ffff04ff05ffff04ff0bffff04ff37ffff04ff2fffff04ff5fff808080808080808080ff8080ff0180ff0effff0cff09ff0bff1780ffff0cff15ff2fff5f8080ff018080 -------------------------------------------------------------------------------- /equality/wallet/puzzles/test_multiple_generator_input_arguments.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 156dafbddc3e1d3bfe1f2a84e48e5e46b287b8358bf65c3c091c93e855fbfc5b 2 | -------------------------------------------------------------------------------- /equality/wallet/rl_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/rl_wallet/__init__.py -------------------------------------------------------------------------------- /equality/wallet/secret_key_store.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional 2 | 3 | from blspy import G1Element, PrivateKey 4 | 5 | GROUP_ORDER = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 6 | 7 | 8 | class SecretKeyStore: 9 | _pk2sk: Dict[G1Element, PrivateKey] 10 | 11 | def __init__(self): 12 | self._pk2sk = {} 13 | 14 | def save_secret_key(self, secret_key: PrivateKey): 15 | public_key = secret_key.get_g1() 16 | self._pk2sk[bytes(public_key)] = secret_key 17 | 18 | def secret_key_for_public_key(self, public_key: G1Element) -> Optional[PrivateKey]: 19 | return self._pk2sk.get(bytes(public_key)) 20 | -------------------------------------------------------------------------------- /equality/wallet/settings/default_settings.py: -------------------------------------------------------------------------------- 1 | from equality.wallet.settings.settings_objects import BackupInitialized 2 | 3 | default_backup_initialized = BackupInitialized(False, False, False, True) 4 | 5 | default_settings = {BackupInitialized.__name__: default_backup_initialized} 6 | -------------------------------------------------------------------------------- /equality/wallet/settings/settings_objects.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.util.streamable import Streamable, streamable 4 | 5 | 6 | @dataclass(frozen=True) 7 | @streamable 8 | class BackupInitialized(Streamable): 9 | """ 10 | Stores user decision regarding import of backup info 11 | """ 12 | 13 | user_initialized: bool # Stores if user made a selection in UI. (Skip vs Import backup) 14 | user_skipped: bool # Stores if user decided to skip import of backup info 15 | backup_info_imported: bool # Stores if backup info has been imported 16 | new_wallet: bool # Stores if this wallet is newly created / not restored from backup 17 | -------------------------------------------------------------------------------- /equality/wallet/sign_coin_solutions.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, List, Optional 2 | 3 | import blspy 4 | from blspy import AugSchemeMPL, PrivateKey 5 | 6 | from equality.types.coin_solution import CoinSolution 7 | from equality.types.spend_bundle import SpendBundle 8 | from equality.util.condition_tools import conditions_dict_for_solution, pkm_pairs_for_conditions_dict 9 | 10 | 11 | async def sign_coin_solutions( 12 | coin_solutions: List[CoinSolution], 13 | secret_key_for_public_key_f: Callable[[blspy.G1Element], Optional[PrivateKey]], 14 | additional_data: bytes, 15 | max_cost: int, 16 | ) -> SpendBundle: 17 | signatures: List[blspy.G2Element] = [] 18 | pk_list: List[blspy.G1Element] = [] 19 | msg_list: List[bytes] = [] 20 | for coin_solution in coin_solutions: 21 | # Get AGG_SIG conditions 22 | err, conditions_dict, cost = conditions_dict_for_solution( 23 | coin_solution.puzzle_reveal, coin_solution.solution, max_cost 24 | ) 25 | if err or conditions_dict is None: 26 | error_msg = f"Sign transaction failed, con:{conditions_dict}, error: {err}" 27 | raise ValueError(error_msg) 28 | 29 | # Create signature 30 | for pk, msg in pkm_pairs_for_conditions_dict( 31 | conditions_dict, bytes(coin_solution.coin.name()), additional_data 32 | ): 33 | pk_list.append(pk) 34 | msg_list.append(msg) 35 | secret_key = secret_key_for_public_key_f(pk) 36 | if secret_key is None: 37 | e_msg = f"no secret key for {pk}" 38 | raise ValueError(e_msg) 39 | assert bytes(secret_key.get_g1()) == bytes(pk) 40 | signature = AugSchemeMPL.sign(secret_key, msg) 41 | assert AugSchemeMPL.verify(pk, msg, signature) 42 | signatures.append(signature) 43 | 44 | # Aggregate signatures 45 | aggsig = AugSchemeMPL.aggregate(signatures) 46 | assert AugSchemeMPL.aggregate_verify(pk_list, msg_list, aggsig) 47 | return SpendBundle(coin_solutions, aggsig) 48 | -------------------------------------------------------------------------------- /equality/wallet/trade_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional, Tuple 3 | 4 | from equality.types.blockchain_format.coin import Coin 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.types.spend_bundle import SpendBundle 7 | from equality.util.ints import uint8, uint32, uint64 8 | from equality.util.streamable import Streamable, streamable 9 | 10 | 11 | @dataclass(frozen=True) 12 | @streamable 13 | class TradeRecord(Streamable): 14 | """ 15 | Used for storing transaction data and status in wallets. 16 | """ 17 | 18 | confirmed_at_index: uint32 19 | accepted_at_time: Optional[uint64] 20 | created_at_time: uint64 21 | my_offer: bool 22 | sent: uint32 23 | spend_bundle: SpendBundle # This in not complete spendbundle 24 | tx_spend_bundle: Optional[SpendBundle] # this is full trade 25 | additions: List[Coin] 26 | removals: List[Coin] 27 | trade_id: bytes32 28 | status: uint32 # TradeStatus, enum not streamable 29 | sent_to: List[Tuple[str, uint8, Optional[str]]] 30 | -------------------------------------------------------------------------------- /equality/wallet/trading/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/trading/__init__.py -------------------------------------------------------------------------------- /equality/wallet/trading/trade_status.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class TradeStatus(Enum): 5 | PENDING_ACCEPT = 0 6 | PENDING_CONFIRM = 1 7 | PENDING_CANCEL = 2 8 | CANCELED = 3 9 | CONFIRMED = 4 10 | FAILED = 5 11 | -------------------------------------------------------------------------------- /equality/wallet/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/equality/wallet/util/__init__.py -------------------------------------------------------------------------------- /equality/wallet/util/transaction_type.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | 4 | class TransactionType(IntEnum): 5 | INCOMING_TX = 0 6 | OUTGOING_TX = 1 7 | COINBASE_REWARD = 2 8 | FEE_REWARD = 3 9 | INCOMING_TRADE = 4 10 | OUTGOING_TRADE = 5 11 | -------------------------------------------------------------------------------- /equality/wallet/util/wallet_types.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | 4 | class WalletType(IntEnum): 5 | # Wallet Types 6 | STANDARD_WALLET = 0 7 | RATE_LIMITED = 1 8 | ATOMIC_SWAP = 2 9 | AUTHORIZED_PAYEE = 3 10 | MULTI_SIG = 4 11 | CUSTODY = 5 12 | COLOURED_COIN = 6 13 | RECOVERABLE = 7 14 | DISTRIBUTED_ID = 8 15 | -------------------------------------------------------------------------------- /equality/wallet/wallet_action.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from equality.util.ints import uint32 5 | from equality.wallet.util.wallet_types import WalletType 6 | 7 | 8 | @dataclass(frozen=True) 9 | class WalletAction: 10 | """ 11 | This object represents the wallet action as it is stored in the database. 12 | 13 | Purpose: 14 | Some wallets require wallet node to perform a certain action when event happens. 15 | For Example, coloured coin wallet needs to fetch solutions once it receives a coin. 16 | In order to be safe from losing connection, closing the app, etc, those actions need to be persisted. 17 | 18 | id: auto-incremented for every added action 19 | name: Specified by the wallet 20 | Wallet_id: ID of the wallet that created this action 21 | type: Type of the wallet that created this action 22 | wallet_callback: Name of the callback function in the wallet that created this action, if specified it will 23 | get called when action has been performed. 24 | done: Indicates if the action has been performed 25 | data: JSON encoded string containing any data wallet or a wallet_node needs for this specific action. 26 | """ 27 | 28 | id: uint32 29 | name: str 30 | wallet_id: int 31 | type: WalletType 32 | wallet_callback: Optional[str] 33 | done: bool 34 | data: str 35 | -------------------------------------------------------------------------------- /equality/wallet/wallet_coin_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from equality.types.blockchain_format.coin import Coin 4 | from equality.types.blockchain_format.sized_bytes import bytes32 5 | from equality.util.ints import uint32 6 | from equality.wallet.util.wallet_types import WalletType 7 | 8 | 9 | @dataclass(frozen=True) 10 | class WalletCoinRecord: 11 | """ 12 | These are values that correspond to a CoinName that are used 13 | in keeping track of the unspent database. 14 | """ 15 | 16 | coin: Coin 17 | confirmed_block_height: uint32 18 | spent_block_height: uint32 19 | spent: bool 20 | coinbase: bool 21 | wallet_type: WalletType 22 | wallet_id: int 23 | 24 | def name(self) -> bytes32: 25 | return self.coin.name() 26 | -------------------------------------------------------------------------------- /equality/wallet/wallet_info.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from equality.util.ints import uint8, uint32 5 | from equality.util.streamable import Streamable, streamable 6 | 7 | 8 | @dataclass(frozen=True) 9 | @streamable 10 | class WalletInfo(Streamable): 11 | """ 12 | This object represents the wallet data as it is stored in DB. 13 | ID: Main wallet (Standard) is stored at index 1, every wallet created after done has auto incremented id. 14 | Name: can be a user provided or default generated name. (can be modified) 15 | Type: is specified during wallet creation and should never be changed. 16 | Data: this filed is intended to be used for storing any wallet specific information required for it. 17 | (RL wallet stores origin_id, admin/user pubkey, rate limit, etc.) 18 | This data should be json encoded string. 19 | """ 20 | 21 | id: uint32 22 | name: str 23 | type: uint8 # WalletType(type) 24 | data: str 25 | 26 | 27 | @dataclass(frozen=True) 28 | @streamable 29 | class WalletInfoBackup(Streamable): 30 | """ 31 | Used for transforming list of WalletInfo objects into bytes. 32 | """ 33 | 34 | wallet_list: List[WalletInfo] 35 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | ignore_missing_imports = True 3 | 4 | [mypy - lib] 5 | ignore_errors = True 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=4.1.2"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.setuptools_scm] 6 | local_scheme = "no-local-version" 7 | 8 | [tool.black] 9 | line-length = 120 10 | -------------------------------------------------------------------------------- /run-py-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | python3 -m venv venv 4 | # shellcheck disable=SC1091 5 | . ./activate 6 | pip3 install ".[dev]" 7 | 8 | py.test ./tests/blockchain -s -v 9 | py.test ./tests/core -s -v 10 | py.test ./tests/wallet -s -v 11 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/__init__.py -------------------------------------------------------------------------------- /tests/blockchain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/blockchain/__init__.py -------------------------------------------------------------------------------- /tests/blockchain/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/clvm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/clvm/__init__.py -------------------------------------------------------------------------------- /tests/clvm/config.py: -------------------------------------------------------------------------------- 1 | parallel = True 2 | checkout_blocks_and_plots = False 3 | install_timelord = False 4 | -------------------------------------------------------------------------------- /tests/clvm/test_serialized_program.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from equality.types.blockchain_format.program import Program, SerializedProgram, INFINITE_COST 4 | from equality.wallet.puzzles.load_clvm import load_clvm 5 | 6 | SHA256TREE_MOD = load_clvm("sha256tree_module.clvm") 7 | 8 | 9 | # TODO: test multiple args 10 | class TestSerializedProgram(TestCase): 11 | def test_tree_hash(self): 12 | p = SHA256TREE_MOD 13 | s = SerializedProgram.from_bytes(bytes(SHA256TREE_MOD)) 14 | self.assertEqual(s.get_tree_hash(), p.get_tree_hash()) 15 | 16 | def test_program_execution(self): 17 | p_result = SHA256TREE_MOD.run(SHA256TREE_MOD) 18 | sp = SerializedProgram.from_bytes(bytes(SHA256TREE_MOD)) 19 | cost, sp_result = sp.run_with_cost(INFINITE_COST, sp) 20 | self.assertEqual(p_result, sp_result) 21 | 22 | def test_serialization(self): 23 | s0 = SerializedProgram.from_bytes(b"\x00") 24 | p0 = Program.from_bytes(b"\x00") 25 | print(s0, p0) 26 | # TODO: enable when clvm updated for minimal encoding of zero 27 | # self.assertEqual(bytes(p0), bytes(s0)) 28 | -------------------------------------------------------------------------------- /tests/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/__init__.py -------------------------------------------------------------------------------- /tests/core/consensus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/consensus/__init__.py -------------------------------------------------------------------------------- /tests/core/full_node/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/full_node/__init__.py -------------------------------------------------------------------------------- /tests/core/full_node/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/core/full_node/dos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/full_node/dos/__init__.py -------------------------------------------------------------------------------- /tests/core/full_node/dos/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/core/full_node/full_sync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/full_node/full_sync/__init__.py -------------------------------------------------------------------------------- /tests/core/full_node/full_sync/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/core/full_node/ram_db.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | import aiosqlite 4 | 5 | from equality.consensus.blockchain import Blockchain 6 | from equality.consensus.constants import ConsensusConstants 7 | from equality.full_node.block_store import BlockStore 8 | from equality.full_node.coin_store import CoinStore 9 | from equality.util.db_wrapper import DBWrapper 10 | 11 | 12 | async def create_ram_blockchain(consensus_constants: ConsensusConstants) -> Tuple[aiosqlite.Connection, Blockchain]: 13 | connection = await aiosqlite.connect(":memory:") 14 | db_wrapper = DBWrapper(connection) 15 | block_store = await BlockStore.create(db_wrapper) 16 | coin_store = await CoinStore.create(db_wrapper) 17 | blockchain = await Blockchain.create(coin_store, block_store, consensus_constants) 18 | return connection, blockchain 19 | -------------------------------------------------------------------------------- /tests/core/full_node/test_node_load.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | 4 | import pytest 5 | 6 | from equality.protocols import full_node_protocol 7 | from equality.types.peer_info import PeerInfo 8 | from equality.util.ints import uint16 9 | from tests.connection_utils import connect_and_get_peer 10 | from tests.setup_nodes import bt, self_hostname, setup_two_nodes, test_constants 11 | from tests.time_out_assert import time_out_assert 12 | 13 | 14 | @pytest.fixture(scope="session") 15 | def event_loop(): 16 | loop = asyncio.get_event_loop() 17 | yield loop 18 | 19 | 20 | class TestNodeLoad: 21 | @pytest.fixture(scope="function") 22 | async def two_nodes(self): 23 | async for _ in setup_two_nodes(test_constants): 24 | yield _ 25 | 26 | @pytest.mark.asyncio 27 | async def test_blocks_load(self, two_nodes): 28 | num_blocks = 50 29 | full_node_1, full_node_2, server_1, server_2 = two_nodes 30 | blocks = bt.get_consecutive_blocks(num_blocks) 31 | peer = await connect_and_get_peer(server_1, server_2) 32 | await full_node_1.full_node.respond_block(full_node_protocol.RespondBlock(blocks[0]), peer) 33 | 34 | await server_2.start_client(PeerInfo(self_hostname, uint16(server_1._port)), None) 35 | 36 | async def num_connections(): 37 | return len(server_2.get_connections()) 38 | 39 | await time_out_assert(10, num_connections, 1) 40 | 41 | start_unf = time.time() 42 | for i in range(1, num_blocks): 43 | await full_node_1.full_node.respond_block(full_node_protocol.RespondBlock(blocks[i])) 44 | await full_node_2.full_node.respond_block(full_node_protocol.RespondBlock(blocks[i])) 45 | print(f"Time taken to process {num_blocks} is {time.time() - start_unf}") 46 | assert time.time() - start_unf < 100 47 | -------------------------------------------------------------------------------- /tests/core/node_height.py: -------------------------------------------------------------------------------- 1 | def node_height_at_least(node, h): 2 | if node.full_node.blockchain.get_peak() is not None: 3 | return node.full_node.blockchain.get_peak().height >= h 4 | return False 5 | 6 | 7 | def node_height_exactly(node, h): 8 | if node.full_node.blockchain.get_peak() is not None: 9 | return node.full_node.blockchain.get_peak().height == h 10 | return False 11 | -------------------------------------------------------------------------------- /tests/core/test_filter.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import List 3 | 4 | import pytest 5 | from chiabip158 import PyBIP158 6 | 7 | from tests.setup_nodes import setup_simulators_and_wallets, bt 8 | 9 | 10 | @pytest.fixture(scope="module") 11 | def event_loop(): 12 | loop = asyncio.get_event_loop() 13 | yield loop 14 | 15 | 16 | class TestFilter: 17 | @pytest.fixture(scope="function") 18 | async def wallet_and_node(self): 19 | async for _ in setup_simulators_and_wallets(1, 1, {}): 20 | yield _ 21 | 22 | @pytest.mark.asyncio 23 | async def test_basic_filter_test(self, wallet_and_node): 24 | full_nodes, wallets = wallet_and_node 25 | wallet_node, server_2 = wallets[0] 26 | wallet = wallet_node.wallet_state_manager.main_wallet 27 | 28 | num_blocks = 2 29 | ph = await wallet.get_new_puzzlehash() 30 | blocks = bt.get_consecutive_blocks( 31 | 10, 32 | guarantee_transaction_block=True, 33 | farmer_reward_puzzle_hash=ph, 34 | pool_reward_puzzle_hash=ph, 35 | ) 36 | for i in range(1, num_blocks): 37 | byte_array_tx: List[bytes] = [] 38 | block = blocks[i] 39 | coins = list(block.get_included_reward_coins()) 40 | coin_0 = bytearray(coins[0].puzzle_hash) 41 | coin_1 = bytearray(coins[1].puzzle_hash) 42 | byte_array_tx.append(coin_0) 43 | byte_array_tx.append(coin_1) 44 | 45 | pl = PyBIP158(byte_array_tx) 46 | present = pl.Match(coin_0) 47 | fee_present = pl.Match(coin_1) 48 | 49 | assert present 50 | assert fee_present 51 | -------------------------------------------------------------------------------- /tests/core/test_merkle_set.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import itertools 3 | 4 | import pytest 5 | 6 | from equality.util.merkle_set import MerkleSet, confirm_included_already_hashed 7 | from tests.setup_nodes import bt 8 | 9 | 10 | @pytest.fixture(scope="module") 11 | def event_loop(): 12 | loop = asyncio.get_event_loop() 13 | yield loop 14 | 15 | 16 | class TestMerkleSet: 17 | @pytest.mark.asyncio 18 | async def test_basics(self): 19 | num_blocks = 20 20 | blocks = bt.get_consecutive_blocks(num_blocks) 21 | 22 | merkle_set = MerkleSet() 23 | merkle_set_reverse = MerkleSet() 24 | coins = list(itertools.chain.from_iterable(map(lambda block: block.get_included_reward_coins(), blocks))) 25 | 26 | # excluded coin (not present in 'coins' and Merkle sets) 27 | excl_coin = coins.pop() 28 | 29 | for coin in reversed(coins): 30 | merkle_set_reverse.add_already_hashed(coin.name()) 31 | 32 | for coin in coins: 33 | merkle_set.add_already_hashed(coin.name()) 34 | 35 | for coin in coins: 36 | result, proof = merkle_set.is_included_already_hashed(coin.name()) 37 | assert result is True 38 | result_excl, proof_excl = merkle_set.is_included_already_hashed(excl_coin.name()) 39 | assert result_excl is False 40 | validate_proof = confirm_included_already_hashed(merkle_set.get_root(), coin.name(), proof) 41 | validate_proof_excl = confirm_included_already_hashed(merkle_set.get_root(), excl_coin.name(), proof_excl) 42 | assert validate_proof is True 43 | assert validate_proof_excl is False 44 | 45 | # Test if the order of adding items changes the outcome 46 | assert merkle_set.get_root() == merkle_set_reverse.get_root() 47 | -------------------------------------------------------------------------------- /tests/core/test_setproctitle.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from equality.util.setproctitle import setproctitle 4 | 5 | 6 | class TestSetProcTitle(unittest.TestCase): 7 | def test_does_not_crash(self): 8 | setproctitle("equality test title") 9 | -------------------------------------------------------------------------------- /tests/core/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/types/__init__.py -------------------------------------------------------------------------------- /tests/core/types/test_coin.py: -------------------------------------------------------------------------------- 1 | from equality.types.blockchain_format.coin import Coin 2 | from equality.types.blockchain_format.sized_bytes import bytes32 3 | from equality.util.ints import uint64 4 | from equality.util.hash import std_hash 5 | import io 6 | 7 | 8 | def coin_serialize(amount: uint64, clvm_serialize: bytes, full_serialize: bytes): 9 | 10 | c = Coin(bytes32(b"a" * 32), bytes32(b"b" * 32), amount) 11 | expected_hash = (b"a" * 32) + (b"b" * 32) + clvm_serialize 12 | 13 | expected_serialization = (b"a" * 32) + (b"b" * 32) + full_serialize 14 | 15 | assert c.get_hash() == std_hash(expected_hash) 16 | assert c.name() == std_hash(expected_hash) 17 | f = io.BytesIO() 18 | c.stream(f) 19 | assert bytes(f.getvalue()) == expected_serialization 20 | 21 | # make sure the serialization round-trips 22 | f = io.BytesIO(expected_serialization) 23 | c2 = Coin.parse(f) 24 | assert c2 == c 25 | 26 | 27 | class TestCoin: 28 | def test_coin_serialization(self): 29 | 30 | coin_serialize(uint64(0xFFFF), bytes([0, 0xFF, 0xFF]), bytes([0, 0, 0, 0, 0, 0, 0xFF, 0xFF])) 31 | coin_serialize(uint64(1337000000), bytes([0x4F, 0xB1, 0x00, 0x40]), bytes([0, 0, 0, 0, 0x4F, 0xB1, 0x00, 0x40])) 32 | 33 | # if the amount is 0, the amount is omitted in the "short" format, 34 | # that's hashed 35 | coin_serialize(uint64(0), b"", bytes([0, 0, 0, 0, 0, 0, 0, 0])) 36 | 37 | # when amount is > INT64_MAX, the "short" serialization format is 1 byte 38 | # longer, since it needs a leading zero to make it positive 39 | coin_serialize( 40 | uint64(0xFFFFFFFFFFFFFFFF), 41 | bytes([0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), 42 | bytes([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), 43 | ) 44 | -------------------------------------------------------------------------------- /tests/core/types/test_proof_of_space.py: -------------------------------------------------------------------------------- 1 | from secrets import token_bytes 2 | 3 | from equality.consensus.default_constants import DEFAULT_CONSTANTS 4 | from equality.types.blockchain_format.proof_of_space import ProofOfSpace # pylint: disable=E0401 5 | 6 | 7 | class TestProofOfSpace: 8 | def test_can_create_proof(self): 9 | """ 10 | Tests that the change of getting a correct proof is exactly 1/target_filter. 11 | """ 12 | num_trials = 100000 13 | success_count = 0 14 | target_filter = 2 ** DEFAULT_CONSTANTS.NUMBER_ZERO_BITS_PLOT_FILTER 15 | for _ in range(num_trials): 16 | challenge_hash = token_bytes(32) 17 | plot_id = token_bytes(32) 18 | sp_output = token_bytes(32) 19 | 20 | if ProofOfSpace.passes_plot_filter(DEFAULT_CONSTANTS, plot_id, challenge_hash, sp_output): 21 | success_count += 1 22 | 23 | assert abs((success_count * target_filter / num_trials) - 1) < 0.35 24 | -------------------------------------------------------------------------------- /tests/core/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/core/util/__init__.py -------------------------------------------------------------------------------- /tests/core/util/test_lru_cache.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from equality.util.lru_cache import LRUCache 4 | 5 | 6 | class TestLRUCache(unittest.TestCase): 7 | def test_lru_cache(self): 8 | cache = LRUCache(5) 9 | 10 | assert cache.get(b"0") is None 11 | 12 | assert len(cache.cache) == 0 13 | cache.put(b"0", 1) 14 | assert len(cache.cache) == 1 15 | assert cache.get(b"0") == 1 16 | cache.put(b"0", 2) 17 | cache.put(b"0", 3) 18 | cache.put(b"0", 4) 19 | cache.put(b"0", 6) 20 | assert cache.get(b"0") == 6 21 | assert len(cache.cache) == 1 22 | 23 | cache.put(b"1", 1) 24 | assert len(cache.cache) == 2 25 | assert cache.get(b"0") == 6 26 | assert cache.get(b"1") == 1 27 | cache.put(b"2", 2) 28 | assert len(cache.cache) == 3 29 | assert cache.get(b"0") == 6 30 | assert cache.get(b"1") == 1 31 | assert cache.get(b"2") == 2 32 | cache.put(b"3", 3) 33 | assert len(cache.cache) == 4 34 | assert cache.get(b"0") == 6 35 | assert cache.get(b"1") == 1 36 | assert cache.get(b"2") == 2 37 | assert cache.get(b"3") == 3 38 | cache.put(b"4", 4) 39 | assert len(cache.cache) == 5 40 | assert cache.get(b"0") == 6 41 | assert cache.get(b"1") == 1 42 | assert cache.get(b"2") == 2 43 | assert cache.get(b"4") == 4 44 | cache.put(b"5", 5) 45 | assert cache.get(b"5") == 5 46 | assert len(cache.cache) == 5 47 | print(cache.cache) 48 | assert cache.get(b"3") is None # 3 is least recently used 49 | assert cache.get(b"1") == 1 50 | assert cache.get(b"2") == 2 51 | cache.put(b"7", 7) 52 | assert len(cache.cache) == 5 53 | assert cache.get(b"0") is None 54 | assert cache.get(b"1") == 1 55 | -------------------------------------------------------------------------------- /tests/core/util/test_significant_bits.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from equality.util.significant_bits import count_significant_bits, truncate_to_significant_bits 4 | 5 | 6 | class TestSignificantBits(unittest.TestCase): 7 | def test_truncate_to_significant_bits(self): 8 | a = -0b001101 9 | assert truncate_to_significant_bits(a, 2) == -0b1100 10 | a = -0b001111 11 | assert truncate_to_significant_bits(a, 2) == -0b1100 12 | a = 0b1111 13 | assert truncate_to_significant_bits(a, 2) == 0b1100 14 | a = 0b1000000111 15 | assert truncate_to_significant_bits(a, 8) == 0b1000000100 16 | a = 0b1000000111 17 | assert truncate_to_significant_bits(a, 0) == 0b0 18 | a = 0b1000000111 19 | assert truncate_to_significant_bits(a, 500) == a 20 | a = 0b10101 21 | assert truncate_to_significant_bits(a, 5) == a 22 | a = 0b10101 23 | assert truncate_to_significant_bits(a, 4) == 0b10100 24 | 25 | def test_count_significant_bits(self): 26 | assert count_significant_bits(0b0001) == 1 27 | assert count_significant_bits(0b00010) == 1 28 | assert count_significant_bits(0b01010) == 3 29 | assert count_significant_bits(-0b01010) == 3 30 | assert count_significant_bits(0b0) == 0 31 | assert count_significant_bits(0b1) == 1 32 | assert count_significant_bits(0b1000010101010000) == 12 33 | 34 | 35 | if __name__ == "__main__": 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /tests/equality-start-sim: -------------------------------------------------------------------------------- 1 | _kill_servers() { 2 | PROCS=`ps -e | grep -E 'equality|vdf_client' -v "equality-start-sim" | awk '!/grep/' | awk '{print $1}'` 3 | if [ -n "$PROCS" ]; then 4 | echo "$PROCS" | xargs -L1 kill -KILL 5 | fi 6 | } 7 | 8 | _kill_servers 9 | 10 | BG_PIDS="" 11 | _run_bg_cmd() { 12 | "$@" & 13 | BG_PIDS="$BG_PIDS $!" 14 | } 15 | 16 | 17 | _term() { 18 | echo "Caught TERM or INT signal, killing all servers." 19 | for PID in $BG_PIDS; do 20 | kill -TERM "$PID" 21 | done 22 | _kill_servers 23 | } 24 | 25 | echo "Starting local blockchain simulation. Runs a local introducer and equality system." 26 | echo "Note that this simulation will not work if connected to external nodes." 27 | 28 | # Starts a harvester, farmer, timelord, introducer, and 3 full nodes, locally. 29 | # Please note that the simulation is meant to be run locally and not connected to external nodes. 30 | # NOTE: you must run install.sh when changing this file 31 | 32 | _run_bg_cmd equality_farmer --logging.log_stdout=True --logging.log_level=INFO 33 | _run_bg_cmd equality_harvester --logging.log_stdout=True --logging.log_level=INFO 34 | _run_bg_cmd equality_timelord --logging.log_stdout=True --logging.log_level=INFO 35 | _run_bg_cmd equality_timelord_launcher --logging.log_stdout=True --logging.log_level=INFO 36 | _run_bg_cmd equality_introducer --logging.log_stdout=True --logging.log_level=INFO 37 | _run_bg_cmd equality_full_node --port=8444 --database_path="simulation_1.db" --rpc_port=8555 --introducer_peer.host="127.0.0.1" --introducer_peer.port=8445 --logging.log_stdout=True --logging.log_level=INFO --logging.log_level=INFO 38 | sleep 1 39 | _run_bg_cmd equality_full_node --port=8002 --database_path="simulation_2.db" --rpc_port=8556 --introducer_peer.host="127.0.0.1" --introducer_peer.port=8445 --logging.log_stdout=True --logging.log_level=INFO 40 | _run_bg_cmd python -m equality.daemon.server --logging.log_stdout=True --logging.log_level=INFO 41 | 42 | wait 43 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | ; logging options 3 | log_cli = 1 4 | log_level = WARNING 5 | log_format = %(asctime)s %(name)s: %(levelname)s %(message)s 6 | -------------------------------------------------------------------------------- /tests/runner-templates/checkout-test-plots.include.yml: -------------------------------------------------------------------------------- 1 | - name: Checkout test blocks and plots 2 | uses: actions/checkout@v2 3 | with: 4 | repository: 'Equality-Network/test-cache' 5 | path: '.equality' 6 | ref: '0.26.0' 7 | fetch-depth: 1 8 | 9 | -------------------------------------------------------------------------------- /tests/runner-templates/install-timelord.include.yml: -------------------------------------------------------------------------------- 1 | - name: Install timelord 2 | run: | 3 | . ./activate 4 | sh install-timelord.sh 5 | ./vdf_bench square_asm 400000 6 | -------------------------------------------------------------------------------- /tests/simulation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/simulation/__init__.py -------------------------------------------------------------------------------- /tests/simulation/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/testconfig.py: -------------------------------------------------------------------------------- 1 | # Github actions template config. 2 | oses = ["ubuntu", "macos"] 3 | root_test_dirs = ["blockchain", "clvm", "core", "generator", "simulation", "wallet"] 4 | 5 | # Defaults are conservative. 6 | parallel = False 7 | checkout_blocks_and_plots = True 8 | install_timelord = True 9 | job_timeout = 30 10 | -------------------------------------------------------------------------------- /tests/time_out_assert.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | import time 4 | from typing import Callable 5 | 6 | from equality.protocols.protocol_message_types import ProtocolMessageTypes 7 | 8 | log = logging.getLogger(__name__) 9 | 10 | 11 | async def time_out_assert_custom_interval(timeout: int, interval, function, value=True, *args, **kwargs): 12 | start = time.time() 13 | while time.time() - start < timeout: 14 | if asyncio.iscoroutinefunction(function): 15 | f_res = await function(*args, **kwargs) 16 | else: 17 | f_res = function(*args, **kwargs) 18 | if value == f_res: 19 | return None 20 | await asyncio.sleep(interval) 21 | assert False, "Timed assertion timed out" 22 | 23 | 24 | async def time_out_assert(timeout: int, function, value=True, *args, **kwargs): 25 | await time_out_assert_custom_interval(timeout, 0.05, function, value, *args, *kwargs) 26 | 27 | 28 | async def time_out_assert_not_none(timeout: int, function, *args, **kwargs): 29 | start = time.time() 30 | while time.time() - start < timeout: 31 | if asyncio.iscoroutinefunction(function): 32 | f_res = await function(*args, **kwargs) 33 | else: 34 | f_res = function(*args, **kwargs) 35 | if f_res is not None: 36 | return None 37 | await asyncio.sleep(0.05) 38 | assert False, "Timed assertion timed out" 39 | 40 | 41 | def time_out_messages(incoming_queue: asyncio.Queue, msg_name: str, count: int = 1) -> Callable: 42 | async def bool_f(): 43 | if incoming_queue.qsize() < count: 44 | return False 45 | for _ in range(count): 46 | response = (await incoming_queue.get())[0].type 47 | if ProtocolMessageTypes(response).name != msg_name: 48 | # log.warning(f"time_out_message: found {response} instead of {msg_name}") 49 | return False 50 | return True 51 | 52 | return bool_f 53 | -------------------------------------------------------------------------------- /tests/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/util/__init__.py -------------------------------------------------------------------------------- /tests/util/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/util/generator_tools_testing.py: -------------------------------------------------------------------------------- 1 | from typing import List, Tuple 2 | 3 | from equality.full_node.mempool_check_conditions import get_name_puzzle_conditions 4 | from equality.types.blockchain_format.coin import Coin 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.types.full_block import FullBlock 7 | from equality.types.generator_types import BlockGenerator 8 | from equality.util.generator_tools import additions_for_npc 9 | 10 | 11 | def run_and_get_removals_and_additions( 12 | block: FullBlock, max_cost: int, safe_mode=False 13 | ) -> Tuple[List[bytes32], List[Coin]]: 14 | removals: List[bytes32] = [] 15 | additions: List[Coin] = [] 16 | 17 | assert len(block.transactions_generator_ref_list) == 0 18 | if not block.is_transaction_block(): 19 | return [], [] 20 | 21 | if block.transactions_generator is not None: 22 | npc_result = get_name_puzzle_conditions(BlockGenerator(block.transactions_generator, []), max_cost, safe_mode) 23 | # build removals list 24 | for npc in npc_result.npc_list: 25 | removals.append(npc.coin_name) 26 | additions.extend(additions_for_npc(npc_result.npc_list)) 27 | 28 | rewards = block.get_included_reward_coins() 29 | additions.extend(rewards) 30 | return removals, additions 31 | -------------------------------------------------------------------------------- /tests/util/key_tool.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from blspy import AugSchemeMPL, G2Element, PrivateKey 4 | 5 | from equality.types.blockchain_format.sized_bytes import bytes32 6 | from equality.types.coin_solution import CoinSolution 7 | from equality.util.condition_tools import conditions_by_opcode, conditions_for_solution, pkm_pairs_for_conditions_dict 8 | from tests.core.make_block_generator import GROUP_ORDER, int_to_public_key 9 | from equality.util.block_tools import test_constants 10 | 11 | 12 | class KeyTool(dict): 13 | @classmethod 14 | def __new__(cls, *args): 15 | return dict.__new__(*args) 16 | 17 | def add_secret_exponents(self, secret_exponents: List[int]) -> None: 18 | for _ in secret_exponents: 19 | self[bytes(int_to_public_key(_))] = _ % GROUP_ORDER 20 | 21 | def sign(self, public_key: bytes, message_hash: bytes32) -> G2Element: 22 | secret_exponent = self.get(public_key) 23 | if not secret_exponent: 24 | raise ValueError("unknown pubkey %s" % public_key.hex()) 25 | bls_private_key = PrivateKey.from_bytes(secret_exponent.to_bytes(32, "big")) 26 | return AugSchemeMPL.sign(bls_private_key, message_hash) 27 | 28 | def signature_for_solution(self, coin_solution: CoinSolution, additional_data: bytes) -> AugSchemeMPL: 29 | signatures = [] 30 | err, conditions, cost = conditions_for_solution( 31 | coin_solution.puzzle_reveal, coin_solution.solution, test_constants.MAX_BLOCK_COST_CLVM 32 | ) 33 | assert conditions is not None 34 | conditions_dict = conditions_by_opcode(conditions) 35 | for public_key, message_hash in pkm_pairs_for_conditions_dict( 36 | conditions_dict, coin_solution.coin.name(), additional_data 37 | ): 38 | signature = self.sign(bytes(public_key), message_hash) 39 | signatures.append(signature) 40 | return AugSchemeMPL.aggregate(signatures) 41 | -------------------------------------------------------------------------------- /tests/util/misc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from equality.util.misc import format_minutes 3 | 4 | 5 | class TestMisc: 6 | @pytest.mark.asyncio 7 | async def test_format_minutes(self): 8 | assert format_minutes(None) == "Invalid" 9 | assert format_minutes(dict()) == "Invalid" 10 | assert format_minutes("some minutes") == "Invalid" 11 | assert format_minutes(-1) == "Unknown" 12 | assert format_minutes(0) == "Now" 13 | assert format_minutes(1) == "1 minute" 14 | assert format_minutes(59) == "59 minutes" 15 | assert format_minutes(60) == "1 hour" 16 | assert format_minutes(61) == "1 hour and 1 minute" 17 | assert format_minutes(119) == "1 hour and 59 minutes" 18 | assert format_minutes(1380) == "23 hours" 19 | assert format_minutes(1440) == "1 day" 20 | assert format_minutes(2160) == "1 day and 12 hours" 21 | assert format_minutes(8640) == "6 days" 22 | assert format_minutes(10080) == "1 week" 23 | assert format_minutes(20160) == "2 weeks" 24 | assert format_minutes(40240) == "3 weeks and 6 days" 25 | assert format_minutes(40340) == "4 weeks" 26 | assert format_minutes(43800) == "1 month" 27 | assert format_minutes(102000) == "2 months and 1 week" 28 | assert format_minutes(481800) == "11 months" 29 | assert format_minutes(525600) == "1 year" 30 | assert format_minutes(1007400) == "1 year and 11 months" 31 | assert format_minutes(5256000) == "10 years" 32 | -------------------------------------------------------------------------------- /tests/wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/wallet/__init__.py -------------------------------------------------------------------------------- /tests/wallet/cc_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/wallet/cc_wallet/__init__.py -------------------------------------------------------------------------------- /tests/wallet/rl_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/wallet/rl_wallet/__init__.py -------------------------------------------------------------------------------- /tests/wallet/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/wallet/rpc/__init__.py -------------------------------------------------------------------------------- /tests/wallet/sync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Equality-Network/equality-blockchain/acff7bd3e842574679a94b8cb1e628f2eb629002/tests/wallet/sync/__init__.py -------------------------------------------------------------------------------- /tests/wallet/sync/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/wallet/test_bech32m.py: -------------------------------------------------------------------------------- 1 | # Based on this specification from Pieter Wuille: 2 | # https://github.com/sipa/bips/blob/bip-bech32m/bip-bech32m.mediawiki 3 | 4 | from equality.util.bech32m import bech32_decode 5 | 6 | 7 | def test_valid_imports(): 8 | test_strings = [ 9 | "A1LQFN3A", 10 | "a1lqfn3a", 11 | "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", 12 | "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", 13 | "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", 14 | "split1checkupstagehandshakeupstreamerranterredcaperredlc445v", 15 | "?1v759aa", 16 | ] 17 | for test_str in test_strings: 18 | hrp, data = bech32_decode(test_str) 19 | assert data is not None 20 | 21 | 22 | def test_invalid_imports(): 23 | test_strings = [ 24 | f"{0x20}1xj0phk", 25 | f"{0x7F}1g6xzxy", 26 | f"{0x80}1vctc34", 27 | "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", 28 | "qyrz8wqd2c9m", 29 | "1qyrz8wqd2c9m", 30 | "y1b0jsk6g", 31 | "lt1igcx5c0", 32 | "in1muywd", 33 | "mm1crxm3i", 34 | "au1s5cgom", 35 | "M1VUXWEZ", 36 | "16plkw9", 37 | "1p2gdwpf", 38 | ] 39 | for test_str in test_strings: 40 | hrp, data = bech32_decode(test_str) 41 | assert data is None 42 | -------------------------------------------------------------------------------- /tests/wallet/test_taproot.py: -------------------------------------------------------------------------------- 1 | from equality.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import ( 2 | DEFAULT_HIDDEN_PUZZLE, 3 | calculate_synthetic_offset, 4 | calculate_synthetic_public_key, 5 | ) 6 | from tests.core.make_block_generator import int_to_public_key 7 | 8 | 9 | class TestTaproot: 10 | def test_1(self): 11 | for main_secret_exponent in range(500, 600): 12 | hidden_puzzle_hash = DEFAULT_HIDDEN_PUZZLE.get_tree_hash() 13 | main_pubkey = int_to_public_key(main_secret_exponent) 14 | offset = calculate_synthetic_offset(main_pubkey, hidden_puzzle_hash) 15 | offset_pubkey = int_to_public_key(offset) 16 | spk1 = main_pubkey + offset_pubkey 17 | spk2 = calculate_synthetic_public_key(main_pubkey, hidden_puzzle_hash) 18 | assert spk1 == spk2 19 | 20 | return 0 21 | --------------------------------------------------------------------------------