├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── dependabot.yml ├── linters │ ├── .flake8 │ ├── .isort.cfg │ ├── .markdown-lint.yml │ ├── .python-black │ └── .python-lint └── workflows │ ├── build-linux-arm64-installer.yml.disabled │ ├── build-linux-installer-deb.yml │ ├── build-macos-installer.yml │ ├── build-macos-m1-installer.yml.disabled │ ├── build-test-macos-blockchain.yml.disabled │ ├── build-test-macos-clvm.yml.disabled │ ├── build-test-macos-core-consensus.yml.disabled │ ├── build-test-macos-core-custom_types.yml.disabled │ ├── build-test-macos-core-daemon.yml.disabled │ ├── build-test-macos-core-full_node-full_sync.yml.disabled │ ├── build-test-macos-core-full_node.yml.disabled │ ├── build-test-macos-core-server.yml.disabled │ ├── build-test-macos-core-ssl.yml.disabled │ ├── build-test-macos-core-util.yml.disabled │ ├── build-test-macos-core.yml.disabled │ ├── build-test-macos-generator.yml.disabled │ ├── build-test-macos-pools.yml.disabled │ ├── build-test-macos-simulation.yml.disabled │ ├── build-test-macos-wallet-cc_wallet.yml.disabled │ ├── build-test-macos-wallet-rl_wallet.yml.disabled │ ├── build-test-macos-wallet-rpc.yml.disabled │ ├── build-test-macos-wallet-sync.yml.disabled │ ├── build-test-macos-wallet.yml.disabled │ ├── build-test-ubuntu-blockchain.yml.disabled │ ├── build-test-ubuntu-clvm.yml.disabled │ ├── build-test-ubuntu-core-consensus.yml.disabled │ ├── build-test-ubuntu-core-custom_types.yml.disabled │ ├── build-test-ubuntu-core-daemon.yml.disabled │ ├── build-test-ubuntu-core-full_node-full_sync.yml.disabled │ ├── build-test-ubuntu-core-full_node.yml.disabled │ ├── build-test-ubuntu-core-server.yml.disabled │ ├── build-test-ubuntu-core-ssl.yml.disabled │ ├── build-test-ubuntu-core-util.yml.disabled │ ├── build-test-ubuntu-core.yml.disabled │ ├── build-test-ubuntu-generator.yml.disabled │ ├── build-test-ubuntu-pools.yml.disabled │ ├── build-test-ubuntu-simulation.yml.disabled │ ├── build-test-ubuntu-wallet-cc_wallet.yml.disabled │ ├── build-test-ubuntu-wallet-rl_wallet.yml.disabled │ ├── build-test-ubuntu-wallet-rpc.yml.disabled │ ├── build-test-ubuntu-wallet-sync.yml.disabled │ ├── build-test-ubuntu-wallet.yml.disabled │ ├── build-windows-installer.yml │ ├── codeql-analysis.yml.disabled │ ├── mozilla-ca-cert.yml │ ├── super-linter.yml.disabled │ ├── test-install-scripts.yml │ └── upload-pypi-source.yml.disabled ├── .gitignore ├── .gitmodules ├── .isort.cfg ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── benchmarks └── coin_store.py ├── build_scripts ├── build_linux_deb.sh ├── build_linux_rpm.sh ├── build_macos.sh ├── build_macos_m1.sh ├── build_windows.ps1 ├── clean-runner.sh └── installer-version.py ├── install-gui.sh ├── install-timelord.sh ├── install.sh ├── installhelper.py ├── mypy.ini ├── pyproject.toml ├── run-py-tests.sh ├── setup.py ├── tad ├── __init__.py ├── clvm │ ├── singleton.py │ └── spend_sim.py ├── cmds │ ├── __init__.py │ ├── configure.py │ ├── farm.py │ ├── farm_funcs.py │ ├── init.py │ ├── init_funcs.py │ ├── keys.py │ ├── keys_funcs.py │ ├── netspace.py │ ├── netspace_funcs.py │ ├── passphrase.py │ ├── passphrase_funcs.py │ ├── plotnft.py │ ├── plotnft_funcs.py │ ├── plots.py │ ├── show.py │ ├── start.py │ ├── start_funcs.py │ ├── stop.py │ ├── tad.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 │ ├── keychain_proxy.py │ ├── keychain_server.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 │ ├── hint_store.py │ ├── mempool.py │ ├── mempool_check_conditions.py │ ├── mempool_manager.py │ ├── pending_tx_cache.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 │ ├── manager.py │ ├── plot_tools.py │ └── util.py ├── pools │ ├── __init__.py │ ├── pool_config.py │ ├── pool_puzzles.py │ ├── pool_wallet.py │ └── pool_wallet_info.py ├── protocols │ ├── __init__.py │ ├── farmer_protocol.py │ ├── full_node_protocol.py │ ├── harvester_protocol.py │ ├── introducer_protocol.py │ ├── pool_protocol.py │ ├── protocol_message_types.py │ ├── protocol_state_machine.py │ ├── protocol_timing.py │ ├── shared_protocol.py │ ├── timelord_protocol.py │ └── wallet_protocol.py ├── py.typed ├── 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 │ ├── tad_ca.crt │ └── tad_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 │ ├── coin_spend.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 │ ├── byte_types.py │ ├── cached_bls.py │ ├── chain_utils.py │ ├── check_fork_next_block.py │ ├── clvm.py │ ├── condition_tools.py │ ├── config.py │ ├── create_alert_file.py │ ├── db_wrapper.py │ ├── default_root.py │ ├── dump_keyring.py │ ├── english.txt │ ├── errors.py │ ├── file_keyring.py │ ├── generator_tools.py │ ├── hash.py │ ├── initial-config.yaml │ ├── ints.py │ ├── json_util.py │ ├── keychain.py │ ├── keyring_wrapper.py │ ├── lru_cache.py │ ├── make_test_constants.py │ ├── merkle_set.py │ ├── misc.py │ ├── network.py │ ├── partial_func.py │ ├── path.py │ ├── permissions.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 │ ├── ssl.py │ ├── streamable.py │ ├── struct_stream.py │ ├── tad_logging.py │ ├── type_checking.py │ ├── validate_alert.py │ ├── vdf_prover.py │ └── ws_message.py └── wallet │ ├── __init__.py │ ├── block_record.py │ ├── cc_wallet │ ├── __init__.py │ ├── cc_info.py │ ├── cc_utils.py │ └── cc_wallet.py │ ├── chialisp.py │ ├── derivation_record.py │ ├── derive_keys.py │ ├── did_wallet │ ├── __init__.py │ ├── did_info.py │ ├── did_wallet.py │ └── did_wallet_puzzles.py │ ├── key_val_store.py │ ├── lineage_proof.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 │ ├── chialisp_deserialisation.clvm │ ├── chialisp_deserialisation.clvm.hex │ ├── chialisp_deserialisation.clvm.hex.sha256tree │ ├── condition_codes.clvm │ ├── create-lock-puzzlehash.clvm │ ├── create-lock-puzzlehash.clvm.hex.sha256tree │ ├── curry-and-treehash.clinc │ ├── decompress_coin_spend_entry.clvm │ ├── decompress_coin_spend_entry.clvm.hex │ ├── decompress_coin_spend_entry.clvm.hex.sha256tree │ ├── decompress_coin_spend_entry_with_prefix.clvm │ ├── decompress_coin_spend_entry_with_prefix.clvm.hex │ ├── decompress_coin_spend_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 │ ├── 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 │ ├── p2_singleton.clvm │ ├── p2_singleton.clvm.hex │ ├── p2_singleton.clvm.hex.sha256tree │ ├── p2_singleton_or_delayed_puzhash.clvm │ ├── p2_singleton_or_delayed_puzhash.clvm.hex │ ├── p2_singleton_or_delayed_puzhash.clvm.hex.sha256tree │ ├── pool_member_innerpuz.clvm │ ├── pool_member_innerpuz.clvm.hex │ ├── pool_member_innerpuz.clvm.hex.sha256tree │ ├── pool_waitingroom_innerpuz.clvm │ ├── pool_waitingroom_innerpuz.clvm.hex │ ├── pool_waitingroom_innerpuz.clvm.hex.sha256tree │ ├── 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_launcher.clvm │ ├── singleton_launcher.clvm.hex │ ├── singleton_launcher.clvm.hex.sha256tree │ ├── singleton_top_layer.clvm │ ├── singleton_top_layer.clvm.hex │ ├── singleton_top_layer.clvm.hex.sha256tree │ ├── singleton_top_layer.py │ ├── singleton_truths.clib │ ├── 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_spends.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 │ ├── debug_spend_bundle.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_interested_store.py │ ├── wallet_node.py │ ├── wallet_node_api.py │ ├── wallet_pool_store.py │ ├── wallet_puzzle_store.py │ ├── wallet_state_manager.py │ ├── wallet_sync_store.py │ ├── wallet_transaction_store.py │ └── wallet_user_store.py └── tests ├── README.md ├── __init__.py ├── block_tools.py ├── blockchain ├── __init__.py ├── config.py ├── test_blockchain.py └── test_blockchain_transactions.py ├── build-workflows.py ├── check_pytest_monitor_output.py ├── clvm ├── __init__.py ├── coin_store.py ├── config.py ├── test_chialisp_deserialization.py ├── test_clvm_compilation.py ├── test_puzzles.py ├── test_serialized_program.py ├── test_singletons.py └── test_spend_sim.py ├── connection_utils.py ├── core ├── __init__.py ├── consensus │ ├── __init__.py │ └── test_pot_iterations.py ├── custom_types │ ├── __init__.py │ ├── test_coin.py │ ├── test_proof_of_space.py │ └── test_spend_bundle.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_hint_store.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 └── util │ ├── __init__.py │ ├── test_cached_bls.py │ ├── test_config.py │ ├── test_file_keyring_synchronization.py │ ├── test_keychain.py │ ├── test_keyring_wrapper.py │ ├── test_lru_cache.py │ ├── test_significant_bits.py │ ├── test_streamable.py │ └── test_type_checking.py ├── generator ├── test_compression.py ├── test_generator_types.py ├── test_rom.py └── test_scan.py ├── pools ├── __init__.py ├── test_pool_config.py ├── test_pool_puzzles_lifecycle.py ├── test_pool_rpc.py ├── test_pool_wallet.py └── test_wallet_pool_store.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 ├── tad-start-sim ├── 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 ├── keyring.py ├── misc.py └── test_struct_stream.py ├── wallet ├── __init__.py ├── cc_wallet │ ├── __init__.py │ ├── test_cc_wallet.py │ └── test_trades.py ├── did_wallet │ ├── test_did.py │ └── test_did_rpc.py ├── rl_wallet │ ├── __init__.py │ ├── test_rl_rpc.py │ └── test_rl_wallet.py ├── rpc │ ├── __init__.py │ └── test_wallet_rpc.py ├── simple_sync │ └── test_simple_sync_protocol.py ├── sync │ ├── __init__.py │ ├── config.py │ └── test_wallet_sync.py ├── test_backup.py ├── test_bech32m.py ├── test_chialisp.py ├── test_puzzle_store.py ├── test_singleton.py ├── test_singleton_lifecycle.py ├── test_singleton_lifecycle_fast.py ├── test_taproot.py ├── test_wallet.py ├── test_wallet_interested_store.py └── test_wallet_store.py ├── wallet_tools.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 | 12 | 13 | **To Reproduce** 14 | 15 | Steps to reproduce the behavior: 16 | 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | 24 | 25 | **Screenshots** 26 | 27 | 28 | **Desktop** 29 | 30 | 31 | - OS: 32 | - OS Version/Flavor: 33 | - CPU: 34 | 35 | **Additional context** 36 | 37 | -------------------------------------------------------------------------------- /.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/Tad-Network/tad-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/Tad-Network/tad-blockchain/discussions/new?category=ideas 10 | 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic dependabot.yml file with 2 | # minimum configuration for two package managers 3 | 4 | version: 2 5 | updates: 6 | # Enable version updates for Python 7 | - package-ecosystem: "pip" 8 | # Look for a `Dockerfile` in the `root` directory 9 | directory: "/" 10 | # Check for updates once a week 11 | schedule: 12 | interval: "weekly" 13 | day: "tuesday" 14 | target-branch: "main" 15 | pull-request-branch-name: 16 | # Separate sections of the branch name with a hyphen 17 | # for example, `dependabot-npm_and_yarn-next_js-acorn-6.4.1` 18 | separator: "-" 19 | 20 | # Maintain dependencies for GitHub Actions 21 | - package-ecosystem: "github-actions" 22 | directory: "/" 23 | # Check for updates once a week 24 | schedule: 25 | interval: "weekly" 26 | day: "tuesday" 27 | target-branch: "main" 28 | pull-request-branch-name: 29 | # Separate sections of the branch name with a hyphen 30 | # for example, `dependabot-npm_and_yarn-next_js-acorn-6.4.1` 31 | separator: "-" 32 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/mozilla-ca-cert.yml: -------------------------------------------------------------------------------- 1 | name: "Update Mozilla CA sub module" 2 | on: 3 | workflow_dispatch: 4 | branches: 5 | - $default-branch 6 | 7 | jobs: 8 | update_ca_module: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | ref: "${{ github.event.inputs.chia_ref }}" 15 | repository: chia-network/chia-blockchain 16 | submodules: recursive 17 | token: "${{ secrets.GITHUB_TOKEN }}" 18 | - name: "Add changes to new branch" 19 | run: | 20 | cd ./mozilla-ca 21 | git pull origin main 22 | - name: "Create Pull Request" 23 | uses: peter-evans/create-pull-request@v3 24 | with: 25 | base: main 26 | body: "Newest Mozilla CA cert" 27 | branch: mozilla-ca-updates 28 | commit-message: "adding ca updates" 29 | delete-branch: true 30 | reviewers: "justinengland,hoffmang9,cmmarslender,nirajpathak13" 31 | title: "CA Cert updates" 32 | token: "${{ secrets.GITHUB_TOKEN }}" 33 | -------------------------------------------------------------------------------- /.github/workflows/test-install-scripts.yml: -------------------------------------------------------------------------------- 1 | name: Test Install Scripts 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '**' 9 | pull_request: 10 | branches: 11 | - '**' 12 | 13 | jobs: 14 | test_scripts: 15 | name: Test Install Scripts 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | max-parallel: 4 20 | matrix: 21 | python-version: [3.9] 22 | os: [macOS-latest, ubuntu-latest] 23 | 24 | steps: 25 | - name: Cancel previous runs on the same branch 26 | if: ${{ github.ref != 'refs/heads/main' }} 27 | uses: styfle/cancel-workflow-action@0.9.1 28 | with: 29 | access_token: ${{ github.token }} 30 | 31 | - name: Checkout Code 32 | uses: actions/checkout@v2 33 | with: 34 | fetch-depth: 0 35 | 36 | - name: Setup Python environment 37 | uses: actions/setup-python@v2 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | 41 | - name: Run install script 42 | env: 43 | INSTALL_PYTHON_VERSION: ${{ matrix.python-version }} 44 | BUILD_VDF_CLIENT: "N" 45 | run: sh install.sh 46 | 47 | - name: Run install-gui script 48 | run: | 49 | . ./activate 50 | sh install-gui.sh 51 | -------------------------------------------------------------------------------- /.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 | tad-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 | tad-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 | # tad-blockchain-gui/src/dev_config.js 92 | # React built app 93 | tad-blockchain-gui/.eslintcache 94 | tad-blockchain-gui/build 95 | build_scripts/dist 96 | build_scripts/*.Dmg 97 | tad-blockchain-gui/src/locales/_build 98 | build_scripts\win_build 99 | build_scripts/win_build 100 | win_code_sign_cert.p12 101 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | 2 | [submodule "mozilla-ca"] 3 | path = mozilla-ca 4 | url = https://github.com/Chia-Network/mozilla-ca.git 5 | branch = main 6 | [submodule "tad-blockchain-gui"] 7 | path = tad-blockchain-gui 8 | url = https://github.com/Tad-Network/tad-blockchain-gui.git 9 | branch = main 10 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | .github/linters/.isort.cfg -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.0.1 4 | hooks: 5 | - id: check-yaml 6 | - id: end-of-file-fixer 7 | exclude: ".*?(.hex|.clvm|.clib)" 8 | - id: trailing-whitespace 9 | - id: check-merge-conflict 10 | - id: check-ast 11 | - id: debug-statements 12 | - repo: https://github.com/psf/black 13 | rev: 21.8b0 14 | hooks: 15 | - id: black 16 | - repo: https://gitlab.com/pycqa/flake8 17 | rev: 3.9.2 18 | hooks: 19 | - id: flake8 20 | - repo: https://github.com/pre-commit/mirrors-mypy 21 | rev: v0.910 22 | hooks: 23 | - id: mypy 24 | additional_dependencies: [types-setuptools, types-PyYAML] 25 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Welcome to the Tad Blockchain project! 4 | We're really tad team trying to make the best fork of the tad. Due to very limited resources we'd really appreciate help :) 5 | 6 | Feel free to open issues or even better - create a Pull Request :) 7 | You'll be rewarded, of course. 8 | 9 | ## Copyright 10 | 11 | By contributing to this repository, you agree to license your work under the Apache License Version 2.0, or the MIT License, or release your work to the public domain. Any work contributed where you are not the original author must contain its license header with the original author(s) and be in the public domain, or licensed under the Apache License Version 2.0 or the MIT License. 12 | -------------------------------------------------------------------------------- /build_scripts/clean-runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Cleans up files/directories that may be left over from previous runs for a clean slate before starting a new build 3 | 4 | PWD=$(pwd) 5 | 6 | rm -rf ../venv || true 7 | rm -rf venv || true 8 | rm -rf tad_blockchain.egg-info || true 9 | rm -rf build_scripts/final_installer || true 10 | rm -rf build_scripts/dist || true 11 | rm -rf build_scripts/pyinstaller || true 12 | rm -rf tad-blockchain-gui/build || true 13 | rm -rf tad-blockchain-gui/daemon || true 14 | rm -rf tad-blockchain-gui/node_modules || true 15 | rm tad-blockchain-gui/temp.json || true 16 | ( cd "$PWD/tad-blockchain-gui" && git checkout HEAD -- package-lock.json ) || true 17 | cd "$PWD" || true 18 | 19 | # Do our best to get rid of any globally installed notarize-cli versions so the version in the current build script is 20 | # installed without conflicting with the other version that might be installed 21 | PATH=$(brew --prefix node@14)/bin:$PATH || true 22 | export PATH 23 | npm uninstall -g notarize-cli || true 24 | npm uninstall -g @tad-network/notarize-cli || true 25 | npm uninstall -g electron-installer-dmg || true 26 | npm uninstall -g electron-packager || true 27 | npm uninstall -g electron/electron-osx-sign || true 28 | -------------------------------------------------------------------------------- /installhelper.py: -------------------------------------------------------------------------------- 1 | # 2 | # Install helper code to manage inserting the correct version for the GUI 3 | # Gets the version from the result of "chia version" 4 | # Converts to proper symver format so NPM doesn't complain 5 | # Adds the version info to the package.json file 6 | # 7 | import json 8 | import os 9 | import subprocess 10 | from pkg_resources import parse_version 11 | 12 | 13 | # 14 | # The following function is borrowed from 15 | # https://github.com/inveniosoftware/invenio-assets/blob/maint-1.0/invenio_assets/npm.py 16 | # Copyright (C) 2015-2018 CERN. 17 | # 18 | def make_semver(version_str): 19 | v = parse_version(version_str) 20 | major = v._version.release[0] 21 | try: 22 | minor = v._version.release[1] 23 | except IndexError: 24 | minor = 0 25 | try: 26 | patch = v._version.release[2] 27 | except IndexError: 28 | patch = 0 29 | 30 | prerelease = [] 31 | if v._version.pre: 32 | prerelease.append("".join(str(x) for x in v._version.pre)) 33 | if v._version.dev: 34 | prerelease.append("".join(str(x) for x in v._version.dev)) 35 | prerelease = ".".join(prerelease) 36 | 37 | local = v.local 38 | 39 | version = "{0}.{1}.{2}".format(major, minor, patch) 40 | 41 | if prerelease: 42 | version += "-{0}".format(prerelease) 43 | if local: 44 | version += "+{0}".format(local) 45 | 46 | return version 47 | 48 | 49 | def update_version(): 50 | with open(f"{os.path.dirname(__file__)}/chia-blockchain-gui/package.json") as f: 51 | data = json.load(f) 52 | 53 | version: str = "0.0" 54 | output = subprocess.run(["chia", "version"], capture_output=True) 55 | if output.returncode == 0: 56 | version = str(output.stdout.strip(), "utf-8").splitlines()[-1] 57 | 58 | data["version"] = make_semver(version) 59 | 60 | with open(f"{os.path.dirname(__file__)}/chia-blockchain-gui/package.json", "w") as w: 61 | json.dump(data, indent=4, fp=w) 62 | 63 | 64 | if __name__ == "__main__": 65 | update_version() 66 | -------------------------------------------------------------------------------- /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 | mypy --install-types 8 | 9 | py.test ./tests -s -v 10 | -------------------------------------------------------------------------------- /tad/__init__.py: -------------------------------------------------------------------------------- 1 | from pkg_resources import DistributionNotFound, get_distribution, resource_filename 2 | 3 | try: 4 | __version__ = get_distribution("tad-blockchain").version 5 | except DistributionNotFound: 6 | # package is not installed 7 | __version__ = "unknown" 8 | 9 | PYINSTALLER_SPEC_PATH = resource_filename("tad", "pyinstaller.spec") 10 | -------------------------------------------------------------------------------- /tad/clvm/singleton.py: -------------------------------------------------------------------------------- 1 | from tad.wallet.puzzles.load_clvm import load_clvm 2 | 3 | P2_SINGLETON_MOD = load_clvm("p2_singleton.clvm") 4 | SINGLETON_TOP_LAYER_MOD = load_clvm("singleton_top_layer.clvm") 5 | SINGLETON_LAUNCHER = load_clvm("singleton_launcher.clvm") 6 | -------------------------------------------------------------------------------- /tad/cmds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/cmds/__init__.py -------------------------------------------------------------------------------- /tad/cmds/netspace.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import click 4 | 5 | 6 | @click.command("netspace", short_help="Estimate total farmed space on the network") 7 | @click.option( 8 | "-p", 9 | "--rpc-port", 10 | help=( 11 | "Set the port where the Full Node is hosting the RPC interface. " 12 | "See the rpc_port under full_node in config.yaml. " 13 | "[default: 4555]" 14 | ), 15 | type=int, 16 | default=None, 17 | ) 18 | @click.option( 19 | "-d", 20 | "--delta-block-height", 21 | help=( 22 | "Compare a block X blocks older to estimate total network space. " 23 | "Defaults to 4608 blocks (~1 day) and Peak block as the starting block. " 24 | "Use --start BLOCK_HEIGHT to specify starting block. " 25 | "Use 192 blocks to estimate over the last hour." 26 | ), 27 | type=str, 28 | default="4608", 29 | ) 30 | @click.option( 31 | "-s", 32 | "--start", 33 | help="Newest block used to calculate estimated total network space. Defaults to Peak block.", 34 | type=str, 35 | default="", 36 | ) 37 | def netspace_cmd(rpc_port: Optional[int], delta_block_height: str, start: str) -> None: 38 | """ 39 | Calculates the estimated space on the network given two block header hashes. 40 | """ 41 | import asyncio 42 | from .netspace_funcs import netstorge_async 43 | 44 | asyncio.run(netstorge_async(rpc_port, delta_block_height, start)) 45 | -------------------------------------------------------------------------------- /tad/cmds/start.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from tad.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(list(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 | -------------------------------------------------------------------------------- /tad/cmds/stop.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import click 5 | 6 | from tad.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 tad.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 tad 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(list(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 | -------------------------------------------------------------------------------- /tad/cmds/units.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | # The rest of the codebase uses mtads everywhere. 4 | # Only use these units for user facing interfaces. 5 | units: Dict[str, int] = { 6 | "tad": 10 ** 12, # 1 tadcoin (TAD) is 1,000,000,000,000 mtad (1 trillion) 7 | "mtad:": 1, 8 | "colouredcoin": 10 ** 3, # 1 coloured coin is 1000 colouredcoin mtads 9 | } 10 | -------------------------------------------------------------------------------- /tad/consensus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/consensus/__init__.py -------------------------------------------------------------------------------- /tad/consensus/block_root_validation.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Optional 2 | 3 | from tad.types.blockchain_format.coin import Coin, hash_coin_list 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.util.errors import Err 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/consensus/coinbase.py: -------------------------------------------------------------------------------- 1 | from blspy import G1Element 2 | 3 | from tad.types.blockchain_format.coin import Coin 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.util.ints import uint32, uint64 6 | from tad.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) -> bytes32: 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 | -------------------------------------------------------------------------------- /tad/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_MY_PARENT_ID = 0 10 | ASSERT_MY_PUZZLEHASH = 0 11 | ASSERT_MY_AMOUNT = 0 12 | ASSERT_SECONDS_RELATIVE = 0 13 | ASSERT_SECONDS_ABSOLUTE = 0 14 | ASSERT_HEIGHT_RELATIVE = 0 15 | ASSERT_HEIGHT_ABSOLUTE = 0 16 | RESERVE_FEE = 0 17 | CREATE_COIN_ANNOUNCEMENT = 0 18 | ASSERT_COIN_ANNOUNCEMENT = 0 19 | CREATE_PUZZLE_ANNOUNCEMENT = 0 20 | ASSERT_PUZZLE_ANNOUNCEMENT = 0 21 | -------------------------------------------------------------------------------- /tad/consensus/find_fork_point.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from tad.consensus.block_record import BlockRecord 4 | from tad.consensus.blockchain_interface import BlockchainInterface 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/consensus/network_type.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | 4 | class NetworkType(IntEnum): 5 | MAINNET = 0 6 | TESTNET = 1 7 | -------------------------------------------------------------------------------- /tad/consensus/pos_quality.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/daemon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/daemon/__init__.py -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/farmer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/farmer/__init__.py -------------------------------------------------------------------------------- /tad/full_node/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/full_node/__init__.py -------------------------------------------------------------------------------- /tad/full_node/hint_store.py: -------------------------------------------------------------------------------- 1 | from typing import List, Tuple 2 | import aiosqlite 3 | from tad.types.blockchain_format.sized_bytes import bytes32 4 | from tad.util.db_wrapper import DBWrapper 5 | import logging 6 | 7 | log = logging.getLogger(__name__) 8 | 9 | 10 | class HintStore: 11 | coin_record_db: aiosqlite.Connection 12 | db_wrapper: DBWrapper 13 | 14 | @classmethod 15 | async def create(cls, db_wrapper: DBWrapper): 16 | self = cls() 17 | self.db_wrapper = db_wrapper 18 | self.coin_record_db = db_wrapper.db 19 | await self.coin_record_db.execute( 20 | "CREATE TABLE IF NOT EXISTS hints(id INTEGER PRIMARY KEY AUTOINCREMENT, coin_id blob, hint blob)" 21 | ) 22 | await self.coin_record_db.execute("CREATE INDEX IF NOT EXISTS hint_index on hints(hint)") 23 | await self.coin_record_db.commit() 24 | return self 25 | 26 | async def get_coin_ids(self, hint: bytes) -> List[bytes32]: 27 | cursor = await self.coin_record_db.execute("SELECT * from hints WHERE hint=?", (hint,)) 28 | rows = await cursor.fetchall() 29 | await cursor.close() 30 | coin_ids = [] 31 | for row in rows: 32 | coin_ids.append(row[1]) 33 | return coin_ids 34 | 35 | async def add_hints(self, coin_hint_list: List[Tuple[bytes32, bytes]]) -> None: 36 | cursor = await self.coin_record_db.executemany( 37 | "INSERT INTO hints VALUES(?, ?, ?)", 38 | [(None,) + record for record in coin_hint_list], 39 | ) 40 | await cursor.close() 41 | -------------------------------------------------------------------------------- /tad/full_node/pending_tx_cache.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from tad.types.blockchain_format.sized_bytes import bytes32 4 | from tad.types.mempool_item import MempoolItem 5 | 6 | 7 | class PendingTxCache: 8 | _cache_max_total_cost: int 9 | _cache_cost: int 10 | _txs: Dict[bytes32, MempoolItem] 11 | 12 | def __init__(self, cost_limit: int): 13 | self._cache_max_total_cost = cost_limit 14 | self._cache_cost = 0 15 | self._txs = {} 16 | 17 | def add(self, item: MempoolItem): 18 | """ 19 | Adds SpendBundles that have failed to be added to the pool in potential tx set. 20 | This is later used to retry to add them. 21 | """ 22 | if item.spend_bundle_name in self._txs: 23 | return None 24 | 25 | self._txs[item.spend_bundle_name] = item 26 | self._cache_cost += item.cost 27 | 28 | while self._cache_cost > self._cache_max_total_cost: 29 | first_in = list(self._txs.keys())[0] 30 | self._cache_cost -= self._txs[first_in].cost 31 | self._txs.pop(first_in) 32 | 33 | def drain(self) -> Dict[bytes32, MempoolItem]: 34 | ret = self._txs 35 | self._txs = {} 36 | self._cache_cost = 0 37 | return ret 38 | 39 | def cost(self) -> int: 40 | return self._cache_cost 41 | -------------------------------------------------------------------------------- /tad/full_node/signage_point.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from tad.types.blockchain_format.vdf import VDFInfo, VDFProof 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/harvester/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/harvester/__init__.py -------------------------------------------------------------------------------- /tad/introducer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/introducer/__init__.py -------------------------------------------------------------------------------- /tad/plotting/plot_tools.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/plotting/plot_tools.py -------------------------------------------------------------------------------- /tad/pools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/pools/__init__.py -------------------------------------------------------------------------------- /tad/protocols/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/protocols/__init__.py -------------------------------------------------------------------------------- /tad/protocols/introducer_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from tad.types.peer_info import TimestampedPeerInfo 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/protocols/protocol_timing.py: -------------------------------------------------------------------------------- 1 | # These settings should not be end-user configurable 2 | INVALID_PROTOCOL_BAN_SECONDS = 10 3 | API_EXCEPTION_BAN_SECONDS = 10 4 | INTERNAL_PROTOCOL_ERROR_BAN_SECONDS = 10 # Don't flap if our client is at fault 5 | -------------------------------------------------------------------------------- /tad/protocols/shared_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import IntEnum 3 | from typing import List, Tuple 4 | 5 | from tad.util.ints import uint8, uint16 6 | from tad.util.streamable import Streamable, streamable 7 | 8 | protocol_version = "0.0.33" 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 tad 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 | -------------------------------------------------------------------------------- /tad/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/py.typed -------------------------------------------------------------------------------- /tad/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/rpc/__init__.py -------------------------------------------------------------------------------- /tad/rpc/harvester_rpc_client.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List 2 | 3 | from tad.rpc.rpc_client import RpcClient 4 | 5 | 6 | class HarvesterRpcClient(RpcClient): 7 | """ 8 | Client to Tad 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 Tad'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 | -------------------------------------------------------------------------------- /tad/server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/server/__init__.py -------------------------------------------------------------------------------- /tad/server/outbound_message.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import IntEnum 3 | from typing import Any, Optional 4 | 5 | from tad.protocols.protocol_message_types import ProtocolMessageTypes 6 | from tad.util.ints import uint8, uint16 7 | from tad.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 | -------------------------------------------------------------------------------- /tad/server/reconnect_task.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import socket 3 | 4 | from tad.server.server import TadServer 5 | from tad.types.peer_info import PeerInfo 6 | 7 | 8 | def start_reconnect_task(server: TadServer, 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 | -------------------------------------------------------------------------------- /tad/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 tad_ssl_ca_paths(path: Path, config: Dict): 27 | return ( 28 | path / config["tad_ssl_ca"]["crt"], 29 | path / config["tad_ssl_ca"]["key"], 30 | ) 31 | -------------------------------------------------------------------------------- /tad/server/start_introducer.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from typing import Dict 3 | 4 | from tad.introducer.introducer import Introducer 5 | from tad.introducer.introducer_api import IntroducerAPI 6 | from tad.server.outbound_message import NodeType 7 | from tad.server.start_service import run_service 8 | from tad.util.config import load_config_cli 9 | from tad.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 | -------------------------------------------------------------------------------- /tad/server/start_timelord.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pathlib 3 | from typing import Dict 4 | 5 | from tad.consensus.constants import ConsensusConstants 6 | from tad.consensus.default_constants import DEFAULT_CONSTANTS 7 | from tad.server.outbound_message import NodeType 8 | from tad.server.start_service import run_service 9 | from tad.timelord.timelord import Timelord 10 | from tad.timelord.timelord_api import TimelordAPI 11 | from tad.types.peer_info import PeerInfo 12 | from tad.util.config import load_config_cli 13 | from tad.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 | -------------------------------------------------------------------------------- /tad/simulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/simulator/__init__.py -------------------------------------------------------------------------------- /tad/simulator/simulator_constants.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from tests.block_tools import create_block_tools, test_constants 3 | from tests.util.keyring import TempKeyring 4 | from tad.util.default_root import DEFAULT_ROOT_PATH 5 | 6 | with TempKeyring() as keychain: 7 | # TODO: mariano: fix this with new consensus 8 | bt = create_block_tools(root_path=DEFAULT_ROOT_PATH, keychain=keychain) 9 | new_genesis_block = bt.create_genesis_block(test_constants, b"0") 10 | 11 | print(bytes(new_genesis_block)) 12 | -------------------------------------------------------------------------------- /tad/simulator/simulator_protocol.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from tad.types.blockchain_format.sized_bytes import bytes32 4 | from tad.util.ints import uint32 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/ssl/tad_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDJTCCAg2gAwIBAgIUPbIVL8hYFkLoTX6967prbP8UQSEwDQYJKoZIhvcNAQEL 3 | BQAwQjEMMAoGA1UECgwDVGFkMQ8wDQYDVQQDDAZUYWQgQ0ExITAfBgNVBAsMGE9y 4 | Z2FuaWMgRmFybWluZyBEaXZpc2lvbjAeFw0yMTA3MTgxNjQzNTFaFw0zMTA3MTYx 5 | NjQzNTFaMEIxDDAKBgNVBAoMA1RhZDEPMA0GA1UEAwwGVGFkIENBMSEwHwYDVQQL 6 | DBhPcmdhbmljIEZhcm1pbmcgRGl2aXNpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB 7 | DwAwggEKAoIBAQDSaAI01PYhMSqb/l0BYBo4znQSCuMUe02e0vMbplhZBDdVzqg/ 8 | GengZv3moiOEcRr2MJc2ZQIqDuBjTLJCwF5X4NKNY58siKmuvtaHsmZt2YC0BcYa 9 | 1pjYWo5dg2fJPaRDWdvnjg8c4fsm1244KesLpsQqqln9Pv2pW/HqM1SCnpQSp4aF 10 | OfJs6jIaQnlj/lkEF92vQ7CjAwqFa9s82TtoEBQRPeu3yLYq4fMxBumnF5e8M3+u 11 | 42FysgZ5VFKgs/4I+LD+wa8U9sNwxLhzmgRgflU0T+cbraONHSXZ9tDHSasflpkQ 12 | LgodF2pd7za6aZ/jWnY8pX9W7xWVspO5zFbvAgMBAAGjEzARMA8GA1UdEwEB/wQF 13 | MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJdOm9QTvXz2feHQ3kBoTDWXJo8pQcTT 14 | r9V+FzYm5ZWVVOOFX2ccIWMxU8go03QtNXLjJGixm9DMEnSBIkiuiZVDeAUIFoCN 15 | q//f2R7iNpgocaop2r7zXNh+yp+dCPuqiaxqzbf8fOSJRANXC/RzBV6ymwi03aU8 16 | zprGFWyBN+ZpB6LU1E/0t3yyj14b6lX9wx7JYsyabw9vq0wPZmJbLbpRE9qFhM2D 17 | OM7zXNUr+2x0zhUoHTsYt6goCtwvck6Mp4p/MLKHXAM2qB1eWwEq33uwhl2mpE5x 18 | ldB7h+NpOX/G59auShtI2cxLhxc4as8pstCXVcmj724oQGZWilzmY1o= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /tad/ssl/tad_ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA0mgCNNT2ITEqm/5dAWAaOM50EgrjFHtNntLzG6ZYWQQ3Vc6o 3 | Pxnp4Gb95qIjhHEa9jCXNmUCKg7gY0yyQsBeV+DSjWOfLIiprr7Wh7JmbdmAtAXG 4 | GtaY2FqOXYNnyT2kQ1nb544PHOH7JtduOCnrC6bEKqpZ/T79qVvx6jNUgp6UEqeG 5 | hTnybOoyGkJ5Y/5ZBBfdr0OwowMKhWvbPNk7aBAUET3rt8i2KuHzMQbppxeXvDN/ 6 | ruNhcrIGeVRSoLP+CPiw/sGvFPbDcMS4c5oEYH5VNE/nG62jjR0l2fbQx0mrH5aZ 7 | EC4KHRdqXe82ummf41p2PKV/Vu8VlbKTucxW7wIDAQABAoIBAQCYR76RDjC1BSiZ 8 | jhcyD6PqyUEobci74iHjeTmKIXJ/upVx/Qe348FpMCFC8sZR7Kpe0/wc2i4e0nWE 9 | aNX7ic6+O3En85oYWTC10FjkDTIk/u4vXJ5sfjRRIfk3pQtQktR4CmklxaMafFOR 10 | YQncjDoZTLFdT72hlB1ynaEQp+XaQisYG+J+L1yvU35YX08/6/KpAsK49x0511hx 11 | nt1P5mO1LeZ95OuTFnM/3xcrJpHZY71wEOPeRjMRYWPwf7pYFrBBhwRtd0ZwzOAf 12 | uIwF9Z7pRzXjJrlXABPQYsFpSv20wWlGC6Tiu4GmKutA2IPrU9jIDZgjyrTX9niP 13 | 0fSBKfEBAoGBAPI1x9J4Jbxq5Kg1Y0liKioTAtEjVgZVBnZKWTJhDB+G2DjJu8Rs 14 | 8BF3KcZ3pu5VfQTy6Aq2oDv1xYekR3cRjPseKFCazPNVdYry0OKjFsrEyBpba3o3 15 | Q3TV25+hAgD8cqzUVkvXPGVb1VLg8AaM2isl3n8owew1VFLNmQAynKRxAoGBAN5i 16 | r84pkO+Bhi1jAZokT3wSxlDreHKkv7q9bgd3pwG88LkmBwDHJMZClNxQuYFMkKYb 17 | /SeZmxeLPBYSuzGG21G3F1QPhhde2vte8oUJipUFa7W3yxlbGjPO7MzW1UEXfB14 18 | mrMhXejLNSF8MwTOkiduZEJ1U1CflDHuFSYZZOFfAoGASx3cw+xZH68zDOvJyAd2 19 | tifBNuUzl7Ek2OjD8vjgDqL+SdZeslNqvK/tN80B62Z96FwBFNmApjfZs/CB7W52 20 | /6ZhsGXh7BFz68zUdYVI20feGzaBBZ1AAWsVutLvUSj505nizZCJ/ZFWfmaeahmi 21 | y8Dp2Rvl0IDVjvyOV95acDECgYEAvjh33MbdPKD0HrqDUlAg1SZSEEF6Q8X8YkVK 22 | aoTEi84LsrN+ZgHxyMyG/HToWFsfl5Z1Unwnl1n3yYG4MUMDMsCD6dllf3QZp86t 23 | b3Kb4aBDY6qUxVmR7iLfls9klkr6VVMmMHcoFspK5huQoFn51lSs2dP1sg7t+k+G 24 | M5KhIc0CgYB7WIscuZjMDFc/9gL26tsRsSWyrCg6LxOv2CGOpr+WM5dRobIsAa5I 25 | 3g+l2YK5xyD6WobAePhNmULFLfc07lh+kes3BGaF2AwOZmsUbe4WSDKxiMfkbpQO 26 | csAQDQf8EfC9KlWJpBwtKnos2RmIe96BNlfyh34/gwzm3UKBKHY6jA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tad/timelord/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/timelord/__init__.py -------------------------------------------------------------------------------- /tad/timelord/iters_from_block.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Tuple, Union 2 | 3 | from tad.consensus.pot_iterations import calculate_ip_iters, calculate_iterations_quality, calculate_sp_iters 4 | from tad.types.blockchain_format.reward_chain_block import RewardChainBlock, RewardChainBlockUnfinished 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/types/__init__.py -------------------------------------------------------------------------------- /tad/types/announcement.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from tad.types.blockchain_format.sized_bytes import bytes32 4 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/blockchain_format/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/types/blockchain_format/__init__.py -------------------------------------------------------------------------------- /tad/types/blockchain_format/classgroup.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from tad.consensus.constants import ConsensusConstants 4 | from tad.types.blockchain_format.sized_bytes import bytes100 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/blockchain_format/pool_target.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from tad.types.blockchain_format.sized_bytes import bytes32 4 | from tad.util.ints import uint32 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/blockchain_format/sized_bytes.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/blockchain_format/slots.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from blspy import G2Element 5 | 6 | from tad.types.blockchain_format.proof_of_space import ProofOfSpace 7 | from tad.types.blockchain_format.sized_bytes import bytes32 8 | from tad.types.blockchain_format.vdf import VDFInfo, VDFProof 9 | from tad.util.ints import uint8, uint64 10 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/blockchain_format/sub_epoch_summary.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.util.ints import uint8, uint64 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 tad.types.blockchain_format.sized_bytes import bytes32 14 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/coin_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from tad.protocols.wallet_protocol import CoinState 5 | from tad.types.blockchain_format.coin import Coin 6 | from tad.types.blockchain_format.sized_bytes import bytes32 7 | from tad.util.ints import uint32, uint64 8 | from tad.util.streamable import Streamable, streamable 9 | 10 | 11 | @dataclass(frozen=True) 12 | @streamable 13 | class CoinRecord(Streamable): 14 | """ 15 | These are values that correspond to a CoinName that are used 16 | in keeping track of the unspent database. 17 | """ 18 | 19 | coin: Coin 20 | confirmed_block_index: uint32 21 | spent_block_index: uint32 22 | spent: bool 23 | coinbase: bool 24 | timestamp: uint64 # Timestamp of the block at height confirmed_block_index 25 | 26 | @property 27 | def name(self) -> bytes32: 28 | return self.coin.name() 29 | 30 | @property 31 | def coin_state(self) -> CoinState: 32 | spent_h = None 33 | if self.spent: 34 | spent_h = self.spent_block_index 35 | confirmed_height: Optional[uint32] = self.confirmed_block_index 36 | if self.confirmed_block_index == 0 and self.timestamp == 0: 37 | confirmed_height = None 38 | return CoinState(self.coin, spent_h, confirmed_height) 39 | -------------------------------------------------------------------------------- /tad/types/coin_solution.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from .coin_spend import CoinSpend as CoinSolution # noqa 4 | 5 | 6 | warnings.warn("`CoinSolution` is now `CoinSpend`") 7 | -------------------------------------------------------------------------------- /tad/types/coin_spend.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from tad.types.blockchain_format.coin import Coin 5 | from tad.types.blockchain_format.program import SerializedProgram, INFINITE_COST 6 | from tad.util.chain_utils import additions_for_solution, fee_for_solution 7 | from tad.util.streamable import Streamable, streamable 8 | 9 | 10 | @dataclass(frozen=True) 11 | @streamable 12 | class CoinSpend(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 | 26 | def reserved_fee(self) -> int: 27 | return fee_for_solution(self.puzzle_reveal, self.solution, INFINITE_COST) 28 | -------------------------------------------------------------------------------- /tad/types/condition_opcodes.py: -------------------------------------------------------------------------------- 1 | import enum 2 | from typing import Any 3 | 4 | 5 | # See tad/wallet/puzzles/condition_codes.clvm 6 | class ConditionOpcode(bytes, enum.Enum): 7 | # AGG_SIG is ascii "1" 8 | 9 | # the conditions below require bls12-381 signatures 10 | 11 | AGG_SIG_UNSAFE = bytes([49]) 12 | AGG_SIG_ME = bytes([50]) 13 | 14 | # the conditions below reserve coin amounts and have to be accounted for in output totals 15 | 16 | CREATE_COIN = bytes([51]) 17 | RESERVE_FEE = bytes([52]) 18 | 19 | # the conditions below deal with announcements, for inter-coin communication 20 | 21 | CREATE_COIN_ANNOUNCEMENT = bytes([60]) 22 | ASSERT_COIN_ANNOUNCEMENT = bytes([61]) 23 | CREATE_PUZZLE_ANNOUNCEMENT = bytes([62]) 24 | ASSERT_PUZZLE_ANNOUNCEMENT = bytes([63]) 25 | 26 | # the conditions below let coins inquire about themselves 27 | 28 | ASSERT_MY_COIN_ID = bytes([70]) 29 | ASSERT_MY_PARENT_ID = bytes([71]) 30 | ASSERT_MY_PUZZLEHASH = bytes([72]) 31 | ASSERT_MY_AMOUNT = bytes([73]) 32 | 33 | # the conditions below ensure that we're "far enough" in the future 34 | 35 | # wall-clock time 36 | ASSERT_SECONDS_RELATIVE = bytes([80]) 37 | ASSERT_SECONDS_ABSOLUTE = bytes([81]) 38 | 39 | # block index 40 | ASSERT_HEIGHT_RELATIVE = bytes([82]) 41 | ASSERT_HEIGHT_ABSOLUTE = bytes([83]) 42 | 43 | def __bytes__(self) -> bytes: 44 | return bytes(self.value) 45 | 46 | @classmethod 47 | def from_bytes(cls: Any, blob: bytes) -> Any: 48 | assert len(blob) == 1 49 | return cls(blob) 50 | -------------------------------------------------------------------------------- /tad/types/condition_with_args.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from tad.types.condition_opcodes import ConditionOpcode 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/end_of_slot_bundle.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from tad.types.blockchain_format.slots import ( 5 | ChallengeChainSubSlot, 6 | InfusedChallengeChainSubSlot, 7 | RewardChainSubSlot, 8 | SubSlotProofs, 9 | ) 10 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/generator_types.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | from tad.types.blockchain_format.program import SerializedProgram 4 | from tad.util.ints import uint32 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/types/mempool_item.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from tad.consensus.cost_calculator import NPCResult 5 | from tad.types.blockchain_format.coin import Coin 6 | from tad.types.blockchain_format.program import SerializedProgram 7 | from tad.types.blockchain_format.sized_bytes import bytes32 8 | from tad.types.spend_bundle import SpendBundle 9 | from tad.util.ints import uint64 10 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/name_puzzle_condition.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Dict, List, Tuple 3 | 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.types.condition_with_args import ConditionWithArgs 6 | from tad.util.condition_tools import ConditionOpcode 7 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/unfinished_block.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional 3 | 4 | from tad.types.blockchain_format.foliage import Foliage, FoliageTransactionBlock, TransactionsInfo 5 | from tad.types.blockchain_format.program import SerializedProgram 6 | from tad.types.blockchain_format.reward_chain_block import RewardChainBlockUnfinished 7 | from tad.types.blockchain_format.vdf import VDFProof 8 | from tad.types.end_of_slot_bundle import EndOfSubSlotBundle 9 | from tad.util.ints import uint32 10 | from tad.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 | -------------------------------------------------------------------------------- /tad/types/unfinished_header_block.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional 3 | 4 | from tad.types.blockchain_format.foliage import Foliage, FoliageTransactionBlock 5 | from tad.types.blockchain_format.reward_chain_block import RewardChainBlockUnfinished 6 | from tad.types.blockchain_format.vdf import VDFProof 7 | from tad.types.end_of_slot_bundle import EndOfSubSlotBundle 8 | from tad.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 | -------------------------------------------------------------------------------- /tad/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/util/__init__.py -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/util/cached_bls.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from typing import List, Optional 3 | 4 | from blspy import AugSchemeMPL, G1Element, G2Element, GTElement 5 | from tad.util.hash import std_hash 6 | from tad.util.lru_cache import LRUCache 7 | 8 | 9 | def get_pairings(cache: LRUCache, pks: List[G1Element], msgs: List[bytes], force_cache: bool) -> List[GTElement]: 10 | pairings: List[Optional[GTElement]] = [] 11 | missing_count: int = 0 12 | for pk, msg in zip(pks, msgs): 13 | aug_msg: bytes = bytes(pk) + msg 14 | h: bytes = bytes(std_hash(aug_msg)) 15 | pairing: Optional[GTElement] = cache.get(h) 16 | if not force_cache and pairing is None: 17 | missing_count += 1 18 | # Heuristic to avoid more expensive sig validation with pairing 19 | # cache when it's empty and cached pairings won't be useful later 20 | # (e.g. while syncing) 21 | if missing_count > len(pks) // 2: 22 | return [] 23 | pairings.append(pairing) 24 | 25 | for i, pairing in enumerate(pairings): 26 | if pairing is None: 27 | aug_msg = bytes(pks[i]) + msgs[i] 28 | aug_hash: G2Element = AugSchemeMPL.g2_from_message(aug_msg) 29 | pairing = pks[i].pair(aug_hash) 30 | 31 | h = bytes(std_hash(aug_msg)) 32 | cache.put(h, pairing) 33 | pairings[i] = pairing 34 | 35 | return pairings 36 | 37 | 38 | LOCAL_CACHE: LRUCache = LRUCache(10000) 39 | 40 | 41 | def aggregate_verify( 42 | pks: List[G1Element], msgs: List[bytes], sig: G2Element, force_cache: bool = False, cache: LRUCache = LOCAL_CACHE 43 | ): 44 | pairings: List[GTElement] = get_pairings(cache, pks, msgs, force_cache) 45 | if len(pairings) == 0: 46 | return AugSchemeMPL.aggregate_verify(pks, msgs, sig) 47 | 48 | pairings_prod: GTElement = functools.reduce(GTElement.__mul__, pairings) 49 | return pairings_prod == sig.pair(G1Element.generator()) 50 | -------------------------------------------------------------------------------- /tad/util/chain_utils.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from clvm.casts import int_from_bytes 4 | 5 | from tad.types.blockchain_format.coin import Coin 6 | from tad.types.blockchain_format.program import SerializedProgram 7 | from tad.types.blockchain_format.sized_bytes import bytes32 8 | from tad.types.condition_opcodes import ConditionOpcode 9 | from tad.util.condition_tools import ( 10 | conditions_dict_for_solution, 11 | created_outputs_for_conditions_dict, 12 | ) 13 | 14 | 15 | def additions_for_solution( 16 | coin_name: bytes32, puzzle_reveal: SerializedProgram, solution: SerializedProgram, max_cost: int 17 | ) -> List[Coin]: 18 | """ 19 | Checks the conditions created by CoinSpend and returns the list of all coins created 20 | """ 21 | err, dic, cost = conditions_dict_for_solution(puzzle_reveal, solution, max_cost) 22 | if err or dic is None: 23 | return [] 24 | return created_outputs_for_conditions_dict(dic, coin_name) 25 | 26 | 27 | def fee_for_solution(puzzle_reveal: SerializedProgram, solution: SerializedProgram, max_cost: int) -> int: 28 | err, dic, cost = conditions_dict_for_solution(puzzle_reveal, solution, max_cost) 29 | if err or dic is None: 30 | return 0 31 | 32 | total = 0 33 | for cvp in dic.get(ConditionOpcode.RESERVE_FEE, []): 34 | amount_bin = cvp.vars[0] 35 | amount = int_from_bytes(amount_bin) 36 | total += amount 37 | return total 38 | -------------------------------------------------------------------------------- /tad/util/check_fork_next_block.py: -------------------------------------------------------------------------------- 1 | from typing import List, Callable 2 | 3 | from tad.consensus.blockchain_interface import BlockchainInterface 4 | from tad.util.ints import uint32 5 | 6 | 7 | async def check_fork_next_block( 8 | blockchain: BlockchainInterface, fork_point_height: uint32, peers_with_peak: List, check_block_future: Callable 9 | ): 10 | our_peak_height = blockchain.get_peak_height() 11 | ses_heigths = blockchain.get_ses_heights() 12 | if len(ses_heigths) > 2 and our_peak_height is not None: 13 | ses_heigths.sort() 14 | max_fork_ses_height = ses_heigths[-3] 15 | potential_peek = uint32(our_peak_height + 1) 16 | # This is the fork point in SES in the case where no fork was detected 17 | if blockchain.get_peak_height() is not None and fork_point_height == max_fork_ses_height: 18 | for peer in peers_with_peak: 19 | if peer.closed: 20 | peers_with_peak.remove(peer) 21 | continue 22 | # Grab a block at peak + 1 and check if fork point is actually our current height 23 | if await check_block_future(peer, potential_peek, blockchain): 24 | fork_point_height = our_peak_height 25 | break 26 | return fork_point_height 27 | -------------------------------------------------------------------------------- /tad/util/clvm.py: -------------------------------------------------------------------------------- 1 | from clvm.casts import int_from_bytes, int_to_bytes # noqa 2 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/util/default_root.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | DEFAULT_ROOT_PATH = Path(os.path.expanduser(os.getenv("TAD_ROOT", "~/.tad/mainnet"))).resolve() 5 | 6 | DEFAULT_KEYS_ROOT_PATH = Path(os.path.expanduser(os.getenv("TAD_KEYS_ROOT", "~/.tad_keys"))).resolve() 7 | -------------------------------------------------------------------------------- /tad/util/hash.py: -------------------------------------------------------------------------------- 1 | import blspy 2 | 3 | from tad.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 | -------------------------------------------------------------------------------- /tad/util/json_util.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | import json 3 | from typing import Any 4 | 5 | from aiohttp import web 6 | 7 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/util/make_test_constants.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from tad.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 | -------------------------------------------------------------------------------- /tad/util/network.py: -------------------------------------------------------------------------------- 1 | from ipaddress import ip_address, IPv4Network, IPv6Network 2 | from typing import Iterable, Union, Any 3 | from tad.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 tad.full_node.full_node_api import FullNodeAPI 21 | 22 | return FullNodeAPI 23 | elif type is NodeType.WALLET: 24 | from tad.wallet.wallet_node_api import WalletNodeAPI 25 | 26 | return WalletNodeAPI 27 | elif type is NodeType.INTRODUCER: 28 | from tad.introducer.introducer_api import IntroducerAPI 29 | 30 | return IntroducerAPI 31 | elif type is NodeType.TIMELORD: 32 | from tad.timelord.timelord_api import TimelordAPI 33 | 34 | return TimelordAPI 35 | elif type is NodeType.FARMER: 36 | from tad.farmer.farmer_api import FarmerAPI 37 | 38 | return FarmerAPI 39 | elif type is NodeType.HARVESTER: 40 | from tad.harvester.harvester_api import HarvesterAPI 41 | 42 | return HarvesterAPI 43 | raise ValueError("No class for type") 44 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/util/permissions.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from typing import Tuple 4 | 5 | 6 | def verify_file_permissions(path: Path, mask: int) -> Tuple[bool, int]: 7 | """ 8 | Check that the file's permissions are properly restricted, as compared to the 9 | permission mask 10 | """ 11 | if not path.exists(): 12 | raise Exception(f"file {path} does not exist") 13 | 14 | mode = os.stat(path).st_mode & 0o777 15 | return (mode & mask == 0, mode) 16 | 17 | 18 | def octal_mode_string(mode: int) -> str: 19 | """Yields a permission mode string: e.g. 0644""" 20 | return f"0{oct(mode)[-3:]}" 21 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/util/prev_transaction_block.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from tad.consensus.block_record import BlockRecord 4 | from tad.consensus.blockchain_interface import BlockchainInterface 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/util/service_groups.py: -------------------------------------------------------------------------------- 1 | from typing import KeysView, Generator 2 | 3 | SERVICES_FOR_GROUP = { 4 | "all": "tad_harvester tad_timelord_launcher tad_timelord tad_farmer tad_full_node tad_wallet".split(), 5 | "node": "tad_full_node".split(), 6 | "harvester": "tad_harvester".split(), 7 | "farmer": "tad_harvester tad_farmer tad_full_node tad_wallet".split(), 8 | "farmer-no-wallet": "tad_harvester tad_farmer tad_full_node".split(), 9 | "farmer-only": "tad_farmer".split(), 10 | "timelord": "tad_timelord_launcher tad_timelord tad_full_node".split(), 11 | "timelord-only": "tad_timelord".split(), 12 | "timelord-launcher-only": "tad_timelord_launcher".split(), 13 | "wallet": "tad_wallet tad_full_node".split(), 14 | "wallet-only": "tad_wallet".split(), 15 | "introducer": "tad_introducer".split(), 16 | "simulator": "tad_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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/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 input_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 | -------------------------------------------------------------------------------- /tad/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 | value = int(value) 15 | try: 16 | v1 = struct.unpack(cls.PACK, struct.pack(cls.PACK, value))[0] 17 | if value != v1: 18 | raise ValueError(f"Value {value} does not fit into {cls.__name__}") 19 | except Exception: 20 | bits = struct.calcsize(cls.PACK) * 8 21 | raise ValueError( 22 | f"Value {value} of size {value.bit_length()} does not fit into " f"{cls.__name__} of size {bits}" 23 | ) 24 | return int.__new__(cls, value) # type: ignore 25 | 26 | @classmethod 27 | def parse(cls: Any, f: BinaryIO) -> Any: 28 | bytes_to_read = struct.calcsize(cls.PACK) 29 | read_bytes = f.read(bytes_to_read) 30 | assert read_bytes is not None and len(read_bytes) == bytes_to_read 31 | return cls(*struct.unpack(cls.PACK, read_bytes)) 32 | 33 | def stream(self, f): 34 | f.write(struct.pack(self.PACK, self)) 35 | 36 | @classmethod 37 | def from_bytes(cls: Any, blob: bytes) -> Any: # type: ignore 38 | f = io.BytesIO(blob) 39 | result = cls.parse(f) 40 | assert f.read() == b"" 41 | return result 42 | 43 | def __bytes__(self: Any) -> bytes: 44 | f = io.BytesIO() 45 | self.stream(f) 46 | return bytes(f.getvalue()) 47 | -------------------------------------------------------------------------------- /tad/util/validate_alert.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | from blspy import AugSchemeMPL, PublicKeyMPL, SignatureMPL 5 | 6 | from tad.util.byte_types import hexstr_to_bytes 7 | from tad.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 | -------------------------------------------------------------------------------- /tad/util/vdf_prover.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from chiavdf import prove 4 | 5 | from tad.consensus.constants import ConsensusConstants 6 | from tad.types.blockchain_format.classgroup import ClassgroupElement 7 | from tad.types.blockchain_format.sized_bytes import bytes32 8 | from tad.types.blockchain_format.vdf import VDFInfo, VDFProof 9 | from tad.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 | -------------------------------------------------------------------------------- /tad/util/ws_message.py: -------------------------------------------------------------------------------- 1 | from secrets import token_bytes 2 | from typing import Any, Dict 3 | 4 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/__init__.py -------------------------------------------------------------------------------- /tad/wallet/block_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from tad.types.blockchain_format.coin import Coin 5 | from tad.types.header_block import HeaderBlock 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/cc_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/cc_wallet/__init__.py -------------------------------------------------------------------------------- /tad/wallet/cc_wallet/cc_info.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional, Tuple 3 | 4 | from tad.types.blockchain_format.program import Program 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/derivation_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from blspy import G1Element 4 | 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.util.ints import uint32 7 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/did_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/did_wallet/__init__.py -------------------------------------------------------------------------------- /tad/wallet/did_wallet/did_info.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional, Tuple 3 | 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.util.ints import uint64 6 | from tad.util.streamable import streamable, Streamable 7 | from tad.wallet.lineage_proof import LineageProof 8 | from tad.types.blockchain_format.program import Program 9 | from tad.types.blockchain_format.coin import Coin 10 | 11 | 12 | @dataclass(frozen=True) 13 | @streamable 14 | class DIDInfo(Streamable): 15 | origin_coin: Optional[Coin] # Coin ID 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[LineageProof]]] # {coin.name(): LineageProof} 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 | sent_recovery_transaction: bool 24 | -------------------------------------------------------------------------------- /tad/wallet/key_val_store.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | import aiosqlite 4 | 5 | from tad.util.byte_types import hexstr_to_bytes 6 | from tad.util.db_wrapper import DBWrapper 7 | from tad.util.streamable import Streamable 8 | 9 | 10 | class KeyValStore: 11 | """ 12 | Multipurpose persistent key-value store 13 | """ 14 | 15 | db_connection: aiosqlite.Connection 16 | db_wrapper: DBWrapper 17 | 18 | @classmethod 19 | async def create(cls, db_wrapper: DBWrapper): 20 | self = cls() 21 | self.db_wrapper = db_wrapper 22 | self.db_connection = db_wrapper.db 23 | await self.db_connection.execute( 24 | ("CREATE TABLE IF NOT EXISTS key_val_store(" " key text PRIMARY KEY," " value text)") 25 | ) 26 | 27 | await self.db_connection.execute("CREATE INDEX IF NOT EXISTS name on key_val_store(key)") 28 | 29 | await self.db_connection.commit() 30 | return self 31 | 32 | async def _clear_database(self): 33 | cursor = await self.db_connection.execute("DELETE FROM key_val_store") 34 | await cursor.close() 35 | await self.db_connection.commit() 36 | 37 | async def get_object(self, key: str, type: Any) -> Any: 38 | """ 39 | Return bytes representation of stored object 40 | """ 41 | 42 | cursor = await self.db_connection.execute("SELECT * from key_val_store WHERE key=?", (key,)) 43 | row = await cursor.fetchone() 44 | await cursor.close() 45 | 46 | if row is None: 47 | return None 48 | 49 | return type.from_bytes(hexstr_to_bytes(row[1])) 50 | 51 | async def set_object(self, key: str, obj: Streamable): 52 | """ 53 | Adds object to key val store 54 | """ 55 | async with self.db_wrapper.lock: 56 | cursor = await self.db_connection.execute( 57 | "INSERT OR REPLACE INTO key_val_store VALUES(?, ?)", 58 | (key, bytes(obj).hex()), 59 | ) 60 | await cursor.close() 61 | await self.db_connection.commit() 62 | -------------------------------------------------------------------------------- /tad/wallet/lineage_proof.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.util.ints import uint64 6 | from tad.util.streamable import Streamable, streamable 7 | 8 | 9 | @dataclass(frozen=True) 10 | @streamable 11 | class LineageProof(Streamable): 12 | parent_name: bytes32 13 | inner_puzzle_hash: Optional[bytes32] 14 | amount: uint64 15 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/puzzles/__init__.py -------------------------------------------------------------------------------- /tad/wallet/puzzles/block_program_zero.clvm: -------------------------------------------------------------------------------- 1 | 2 | ; TODO convert generators arg to list of generators 3 | 4 | (mod (decompress_puzzle decompress_coin_spend_entry start end compressed_cses deserialize gen_list reserved_arg) 5 | 6 | (defun decompress_cses (decompress_puzzle decompress_coin_spend_entry cses deserialize puzzle_prefix) 7 | (if cses 8 | (c (a decompress_coin_spend_entry (list deserialize decompress_puzzle puzzle_prefix (f cses))) 9 | (decompress_cses decompress_puzzle decompress_coin_spend_entry (r cses) deserialize puzzle_prefix )) 10 | ()) ) 11 | 12 | (list (decompress_cses decompress_puzzle decompress_coin_spend_entry compressed_cses deserialize (substr (f gen_list) start end))) 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/block_program_zero.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff02ff02ffff04ff02ffff04ff05ffff04ff0bffff04ff5fffff04ff81bfffff04ffff0cff82027fff17ff2f80ff8080808080808080ff8080ffff04ffff01ff02ffff03ff17ffff01ff04ffff02ff0bffff04ff2fffff04ff05ffff04ff5fffff04ff27ff808080808080ffff02ff02ffff04ff02ffff04ff05ffff04ff0bffff04ff37ffff04ff2fffff04ff5fff808080808080808080ff8080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/block_program_zero.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | f0a38c8efe58895ae527c65c37f700a4238504691b83990e5dd91bd8b3c30eae 2 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/calculate_synthetic_public_key.clvm.hex: -------------------------------------------------------------------------------- 1 | ff1dff02ffff1effff0bff02ff05808080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/calculate_synthetic_public_key.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 624c5d5704d0decadfc0503e71bbffb6cdfe45025bce7cf3e6864d1eafe8f65e 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/cc.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | d4596fa7aa6eaa267ebce8d527546827de083d58fb4e14f4137c2448f7252e5c 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/cc_loader.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/chialisp_deserialisation.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/chialisp_deserialisation.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 94ec19077f9a34e0b11ad2456af0170f4cc03f11230ca42e3f82e6e644ac4f5d 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/condition_codes.clvm: -------------------------------------------------------------------------------- 1 | ; See tad/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 | -------------------------------------------------------------------------------- /tad/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 | ) -------------------------------------------------------------------------------- /tad/wallet/puzzles/create-lock-puzzlehash.clvm.hex.sha256tree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/puzzles/create-lock-puzzlehash.clvm.hex.sha256tree -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_coin_spend_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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_coin_spend_entry.clvm.hex: -------------------------------------------------------------------------------- 1 | ff04ff4fffff04ffff02ff05ffff04ff02ffff04ff0bffff04ff82012fffff04ff17ff808080808080ffff04ff82014fff8201af808080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_coin_spend_entry.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 9d98ed08770d31be4bd1bde4705dab388db5e7e9c349f5a76fc3c347aa3a0b79 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_coin_spend_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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_coin_spend_entry_with_prefix.clvm.hex: -------------------------------------------------------------------------------- 1 | ff04ff47ffff04ffff02ff05ffff04ff02ffff04ff0bffff04ff8197ffff01ff84ff0180808080808080ffff04ff81a7ff81d7808080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_coin_spend_entry_with_prefix.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 92aa4bc8060a8836355a1884075141b4791ce1b67ae6092bb166b2845954bc89 2 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ff02ffff04ffff0eff05ff0bff1780ff808080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/decompress_puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | fe94c58f1117afe315e0450daca1c62460ec1a1c439cd4018d79967a5d7d1370 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/did_innerpuz.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | ef41902d9964f6050f87de98b5c4e34512b7d2abded3fe700f7850ff20323bf2 2 | -------------------------------------------------------------------------------- /tad/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 `chialisp_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_spend ((parent puzzle amount solution) coinname) 20 | (= (sha256 parent (sha256tree1 puzzle) amount) coinname) 21 | ) 22 | 23 | (defun check_for_coinname (coin_spends coinname) 24 | (if coin_spends 25 | (if (check_coin_spend (f coin_spends) coinname) 26 | (list (f (r (f coin_spends))) (f (r (r (r (f coin_spends)))))) 27 | (check_for_coinname (r coin_spends) 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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/generator_for_single_coin.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ff0cffff04ff02ffff04ffff05ffff02ff05ffff04ff0affff04ff13ff8080808080ffff04ff17ff8080808080ffff04ffff01ffffff09ffff0bff09ffff02ff0effff04ff02ffff04ff15ff80808080ff2d80ff0b80ff02ffff03ff05ffff01ff02ffff03ffff02ff08ffff04ff02ffff04ff09ffff04ff0bff8080808080ffff01ff04ff29ffff04ff81b9ff808080ffff01ff02ff0cffff04ff02ffff04ff0dffff04ff0bff808080808080ff0180ffff01ff088080ff0180ffff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/generator_for_single_coin.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 9eb0d58814fff6aec8e9eb9522c08a68d8f004bf1506e6c98388758beee2f80e 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/generator_loader.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | ) -------------------------------------------------------------------------------- /tad/wallet/puzzles/genesis-by-coin-id-with-0.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff03ffff09ff5bff8080ffff01ff0101ffff01ff02ffff03ffff09ff13ff0280ffff01ff0101ff8080ff018080ff0180 -------------------------------------------------------------------------------- /tad/wallet/puzzles/genesis-by-coin-id-with-0.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 258008f81f21c270f4b58488b108a46a35e5df43ca5b0313ac83e900a5e44a5f 2 | -------------------------------------------------------------------------------- /tad/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 | ) -------------------------------------------------------------------------------- /tad/wallet/puzzles/genesis-by-puzzle-hash-with-0.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff03ffff09ff5bff8080ffff01ff0101ffff01ff02ffff03ffff02ffff03ffff09ffff0bff47ff81a7ff82016780ff1380ffff01ff02ffff03ffff09ff81a7ff0280ffff01ff0101ff8080ff0180ff8080ff0180ffff01ff0101ff8080ff018080ff0180 -------------------------------------------------------------------------------- /tad/wallet/puzzles/genesis-by-puzzle-hash-with-0.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 795964e0324fbc08e8383d67659194a70455956ad1ebd2329ccf20008da00936 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/genesis_by_coin_id_with_0.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from tad.types.blockchain_format.coin import Coin 4 | from tad.types.blockchain_format.program import Program 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/genesis_by_puzzle_hash_with_0.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from tad.types.blockchain_format.coin import Coin 4 | from tad.types.blockchain_format.program import Program 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/load_clvm.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | import pkg_resources 4 | from clvm_tools.clvmc import compile_clvm 5 | 6 | from tad.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=package_or_requirement))) 38 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/lock.inner.puzzle.clvm: -------------------------------------------------------------------------------- 1 | ; a trivial puzzle used as the core of a lock puzzle 2 | (mod args (q ())) 3 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/lock.inner.puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff01ff8080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/lock.inner.puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | f3a579254623f8094e07af862df2e45c9db5592b4105d34a256dd6c498416288 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_conditions.clvm: -------------------------------------------------------------------------------- 1 | (mod (conditions) 2 | (qq (q . (unquote conditions))) 3 | ) 4 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_conditions.clvm.hex: -------------------------------------------------------------------------------- 1 | ff04ffff0101ff0280 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_conditions.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 1c77d7d5efde60a7a1d2d27db6d746bc8e568aea1ef8586ca967a0d60b83cc36 2 | -------------------------------------------------------------------------------- /tad/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 tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_delegated_conditions.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff0bff80808080ff80808080ff0b80ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_delegated_conditions.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 0ff94726f1a8dea5c3f70d3121945190778d3b2b3fcda3735a1f290977e98341 2 | -------------------------------------------------------------------------------- /tad/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 tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_delegated_puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff0bff80808080ff80808080ffff02ff0bff178080ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_delegated_puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 542cde70d1102cd1b763220990873efc8ab15625ded7eae22cc11e21ef2e2f7c 2 | -------------------------------------------------------------------------------- /tad/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 tad.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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | e9aaa49f45bad5c889b86ee3341550c155cfdd10c3a6757de618d20612fffd52 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_m_of_n_delegate_direct.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ffff09ff05ffff02ff16ffff04ff02ffff04ff17ff8080808080ffff01ff02ff0cffff04ff02ffff04ffff02ff0affff04ff02ffff04ff17ffff04ff0bff8080808080ffff04ffff02ff1effff04ff02ffff04ff2fff80808080ffff04ff2fffff04ff5fff80808080808080ffff01ff088080ff0180ffff04ffff01ffff31ff02ffff03ff05ffff01ff04ffff04ff08ffff04ff09ffff04ff0bff80808080ffff02ff0cffff04ff02ffff04ff0dffff04ff0bffff04ff17ffff04ff2fff8080808080808080ffff01ff02ff17ff2f8080ff0180ffff02ffff03ff05ffff01ff02ffff03ff09ffff01ff04ff13ffff02ff0affff04ff02ffff04ff0dffff04ff1bff808080808080ffff01ff02ff0affff04ff02ffff04ff0dffff04ff1bff808080808080ff0180ff8080ff0180ffff02ffff03ff05ffff01ff10ffff02ff16ffff04ff02ffff04ff0dff80808080ffff02ffff03ff09ffff01ff0101ff8080ff018080ff8080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff1effff04ff02ffff04ff09ff80808080ffff02ff1effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_m_of_n_delegate_direct.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 0f199d5263ac1a62b077c159404a71abd3f9691cc57520bf1d4c5cb501504457 2 | -------------------------------------------------------------------------------- /tad/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 tad.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 | -------------------------------------------------------------------------------- /tad/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 | ) -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_puzzle_hash.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ffff09ff05ffff02ff02ffff04ff02ffff04ff0bff8080808080ffff01ff02ff0bff1780ffff01ff088080ff0180ffff04ffff01ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff02ffff04ff02ffff04ff09ff80808080ffff02ff02ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_puzzle_hash.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 13e29a62b42cd2ef72a79e4bacdc59733ca6310d65af83d349360d36ec622363 2 | -------------------------------------------------------------------------------- /tad/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 tad.types.blockchain_format.program import Program 9 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_singleton.clvm: -------------------------------------------------------------------------------- 1 | (mod (SINGLETON_MOD_HASH LAUNCHER_ID LAUNCHER_PUZZLE_HASH singleton_inner_puzzle_hash my_id) 2 | 3 | ; SINGLETON_MOD_HASH is the mod-hash for the singleton_top_layer puzzle 4 | ; LAUNCHER_ID is the ID of the singleton we are commited to paying to 5 | ; LAUNCHER_PUZZLE_HASH is the puzzle hash of the launcher 6 | ; singleton_inner_puzzle_hash is the innerpuzzlehash for our singleton at the current time 7 | ; my_id is the coin_id of the coin that this puzzle is locked into 8 | 9 | (include condition_codes.clvm) 10 | (include curry-and-treehash.clinc) 11 | 12 | ; takes a lisp tree and returns the hash of it 13 | (defun sha256tree (TREE) 14 | (if (l TREE) 15 | (sha256 2 (sha256tree (f TREE)) (sha256tree (r TREE))) 16 | (sha256 1 TREE) 17 | ) 18 | ) 19 | 20 | ;; return the full puzzlehash for a singleton with the innerpuzzle curried in 21 | ; puzzle-hash-of-curried-function is imported from curry-and-treehash.clinc 22 | (defun-inline calculate_full_puzzle_hash (SINGLETON_MOD_HASH LAUNCHER_ID LAUNCHER_PUZZLE_HASH inner_puzzle_hash) 23 | (puzzle-hash-of-curried-function SINGLETON_MOD_HASH 24 | inner_puzzle_hash 25 | (sha256tree (c SINGLETON_MOD_HASH (c LAUNCHER_ID LAUNCHER_PUZZLE_HASH))) 26 | ) 27 | ) 28 | 29 | (defun-inline claim_rewards (SINGLETON_MOD_HASH LAUNCHER_ID LAUNCHER_PUZZLE_HASH singleton_inner_puzzle_hash my_id) 30 | (list 31 | (list ASSERT_PUZZLE_ANNOUNCEMENT (sha256 (calculate_full_puzzle_hash SINGLETON_MOD_HASH LAUNCHER_ID LAUNCHER_PUZZLE_HASH singleton_inner_puzzle_hash) my_id)) 32 | (list CREATE_COIN_ANNOUNCEMENT '$') 33 | (list ASSERT_MY_COIN_ID my_id)) 34 | ) 35 | 36 | ; main 37 | (claim_rewards SINGLETON_MOD_HASH LAUNCHER_ID LAUNCHER_PUZZLE_HASH singleton_inner_puzzle_hash my_id) 38 | ) 39 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_singleton.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff18ffff04ffff0bffff02ff2effff04ff02ffff04ff05ffff04ff2fffff04ffff02ff3effff04ff02ffff04ffff04ff05ffff04ff0bff178080ff80808080ff808080808080ff5f80ff808080ffff04ffff04ff2cffff01ff248080ffff04ffff04ff10ffff04ff5fff808080ff80808080ffff04ffff01ffffff463fff02ff3c04ffff01ff0102ffff02ffff03ff05ffff01ff02ff16ffff04ff02ffff04ff0dffff04ffff0bff3affff0bff12ff3c80ffff0bff3affff0bff3affff0bff12ff2a80ff0980ffff0bff3aff0bffff0bff12ff8080808080ff8080808080ffff010b80ff0180ffff0bff3affff0bff12ff1480ffff0bff3affff0bff3affff0bff12ff2a80ff0580ffff0bff3affff02ff16ffff04ff02ffff04ff07ffff04ffff0bff12ff1280ff8080808080ffff0bff12ff8080808080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff3effff04ff02ffff04ff09ff80808080ffff02ff3effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_singleton.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 40f828d8dd55603f4ff9fbf6b73271e904e69406982f4fbefae2c8dcceaf9834 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_singleton_or_delayed_puzhash.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ff82017fffff01ff04ffff04ff38ffff04ffff0bffff02ff2effff04ff02ffff04ff05ffff04ff81bfffff04ffff02ff3effff04ff02ffff04ffff04ff05ffff04ff0bff178080ff80808080ff808080808080ff82017f80ff808080ffff04ffff04ff3cffff01ff248080ffff04ffff04ff28ffff04ff82017fff808080ff80808080ffff01ff04ffff04ff24ffff04ff2fff808080ffff04ffff04ff2cffff04ff5fffff04ff81bfff80808080ffff04ffff04ff10ffff04ff81bfff808080ff8080808080ff0180ffff04ffff01ffffff49ff463fffff5002ff333cffff04ff0101ffff02ff02ffff03ff05ffff01ff02ff36ffff04ff02ffff04ff0dffff04ffff0bff26ffff0bff2aff1280ffff0bff26ffff0bff26ffff0bff2aff3a80ff0980ffff0bff26ff0bffff0bff2aff8080808080ff8080808080ffff010b80ff0180ffff0bff26ffff0bff2aff3480ffff0bff26ffff0bff26ffff0bff2aff3a80ff0580ffff0bff26ffff02ff36ffff04ff02ffff04ff07ffff04ffff0bff2aff2a80ff8080808080ffff0bff2aff8080808080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff3effff04ff02ffff04ff09ff80808080ffff02ff3effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/p2_singleton_or_delayed_puzhash.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | adb656e0211e2ab4f42069a4c5efc80dc907e7062be08bf1628c8e5b6d94d25b 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/pool_member_innerpuz.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ff8202ffffff01ff02ff16ffff04ff02ffff04ff05ffff04ff8204bfffff04ff8206bfffff04ff82017fffff04ffff0bffff19ff2fffff18ffff019100ffffffffffffffffffffffffffffffffff8202ff8080ff0bff82017f80ff8080808080808080ffff01ff04ffff04ff08ffff04ff17ffff04ffff02ff1effff04ff02ffff04ff82017fff80808080ff80808080ffff04ffff04ff1cffff04ff5fffff04ff8206bfff80808080ff80808080ff0180ffff04ffff01ffff32ff3d33ff3effff04ffff04ff1cffff04ff0bffff04ff17ff80808080ffff04ffff04ff1cffff04ff05ffff04ff2fff80808080ffff04ffff04ff0affff04ff5fff808080ffff04ffff04ff14ffff04ffff0bff5fffff012480ff808080ff8080808080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff1effff04ff02ffff04ff09ff80808080ffff02ff1effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/pool_member_innerpuz.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | a8490702e333ddd831a3ac9c22d0fa26d2bfeaf2d33608deb22f0e0123eb0494 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/pool_waitingroom_innerpuz.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ff82017fffff01ff04ffff04ff1cffff04ff5fff808080ffff04ffff04ff12ffff04ff8205ffffff04ff8206bfff80808080ffff04ffff04ff08ffff04ff17ffff04ffff02ff1effff04ff02ffff04ffff04ff8205ffffff04ff8202ffff808080ff80808080ff80808080ff80808080ffff01ff02ff16ffff04ff02ffff04ff05ffff04ff8204bfffff04ff8206bfffff04ff8202ffffff04ffff0bffff19ff2fffff18ffff019100ffffffffffffffffffffffffffffffffff8205ff8080ff0bff8202ff80ff808080808080808080ff0180ffff04ffff01ffff32ff3d52ffff333effff04ffff04ff12ffff04ff0bffff04ff17ff80808080ffff04ffff04ff12ffff04ff05ffff04ff2fff80808080ffff04ffff04ff1affff04ff5fff808080ffff04ffff04ff14ffff04ffff0bff5fffff012480ff808080ff8080808080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff1effff04ff02ffff04ff09ff80808080ffff02ff1effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/pool_waitingroom_innerpuz.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | a317541a765bf8375e1c6e7c13503d0d2cbf56cacad5182befe947e78e2c0307 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/puzzle_utils.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/rl.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ffff03ffff09ff81bfff2480ffff01ff04ffff04ff30ffff04ff5fffff04ffff02ff3effff04ff02ffff04ffff04ff81bfff81ff80ff80808080ff80808080ff81ff80ffff01ff04ffff04ff30ffff04ff05ffff04ffff02ff3effff04ff02ffff04ffff04ff81bfff81ff80ff80808080ff80808080ffff02ffff03ffff09ff81bfff3c80ffff01ff02ff2effff04ff02ffff04ff0bffff04ff17ffff04ff2fffff04ff81ffff80808080808080ffff01ff02ff22ffff04ff02ffff04ff2fffff04ff81ffff808080808080ff01808080ff0180ffff04ffff01ffffffffff02ffff03ffff15ff05ff0b80ffff01ff0101ffff01ff02ffff03ffff09ff05ff0b80ffff01ff0101ff8080ff018080ff018031ff5246ffff0333ff3c01ffffffff02ffff03ffff02ffff03ffff09ffff0bff820bfbff13ff8205fb80ff82017b80ffff01ff0101ffff01ff02ffff03ffff09ff05ff82017b80ffff01ff0101ff8080ff018080ff0180ffff01ff04ffff02ff26ffff04ff02ffff04ff82017bffff04ff13ffff04ff8202fbff808080808080ffff04ffff02ff2affff04ff02ffff04ff2bffff04ff5bffff04ff81bbff808080808080ffff04ffff02ff3affff04ff02ffff04ff13ffff04ffff10ff81bbff8202fb80ff8080808080ff80808080ffff01ffff08808080ff0180ff04ff34ffff04ff05ffff04ffff11ff0bffff10ff17ff2f8080ff80808080ffff04ff2cffff04ffff0bff05ff0bff1780ff808080ff04ff34ffff04ff05ffff04ff0bff80808080ffffff04ff38ffff04ffff0bff05ff0bff1780ff808080ff02ffff03ffff02ff20ffff04ff02ffff04ffff12ff05ff1780ffff04ffff12ff0bff2f80ff8080808080ffff01ff04ff28ffff04ff05ff808080ffff01ffff08808080ff0180ffff02ffff03ffff02ffff03ffff09ffff0bff8217efff81afff822fef80ff4f80ffff01ff0101ffff01ff02ffff03ffff09ff17ff4f80ffff01ff0101ff8080ff018080ff0180ffff01ff04ffff02ff36ffff04ff02ffff04ff820befffff04ff8205efffff04ff05ffff04ff0bff80808080808080ffff04ffff02ff32ffff04ff02ffff04ff81afffff04ff82016fffff04ff8205efffff04ff825fefff80808080808080ffff04ffff02ff26ffff04ff02ffff04ff4fffff04ff81afffff04ff82016fff808080808080ffff04ffff02ff3affff04ff02ffff04ff8202efffff04ff8205efff8080808080ff8080808080ffff01ffff08808080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff3effff04ff02ffff04ff09ff80808080ffff02ff3effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/rl.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | f663796a8c522b85bd9472cbea2bf7f138e8351e8e4032706fc0539e87f94faf 2 | -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/rl_aggregation.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff06ffff04ff0bff808080ffff04ffff04ff04ffff04ffff0bffff0bff17ff05ff2f80ff0b80ff808080ff808080ffff04ffff01ff3d46ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/rl_aggregation.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 007400187f63927ee023a7172bb88f14d49aaa4beb790ecaf7dde7c1a79b6481 2 | -------------------------------------------------------------------------------- /tad/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 `chialisp_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_spend ((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_spends) 26 | (if coin_spends 27 | (c (process_coin_spend (f coin_spends)) (recurse (r coin_spends))) 28 | 0 29 | ) 30 | ) 31 | 32 | (defun process-decompressor ((coin_spends . block-level-extras)) 33 | (c (recurse coin_spends) block-level-extras) 34 | ) 35 | 36 | (process-decompressor (a block_decompresser_program (list local_deserialize_mod historical_blocks_tree)))) 37 | ) 38 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/rom_bootstrap_generator.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ff0cffff04ff02ffff04ffff02ff05ffff04ff08ffff04ff13ff80808080ff80808080ffff04ffff01ffffff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080ff04ffff02ff16ffff04ff02ffff04ff09ff80808080ff0d80ffff04ff09ffff04ffff02ff1effff04ff02ffff04ff15ff80808080ffff04ff2dffff04ffff02ff15ff5d80ff7d80808080ffff02ffff03ff05ffff01ff04ffff02ff0affff04ff02ffff04ff09ff80808080ffff02ff16ffff04ff02ffff04ff0dff8080808080ff8080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff1effff04ff02ffff04ff09ff80808080ffff02ff1effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/rom_bootstrap_generator.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 161bade1f822dcd62ab712ebaf30f3922a301e48a639e4295c5685f8bece7bd9 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/rom_bootstrap_generator.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/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 | ) -------------------------------------------------------------------------------- /tad/wallet/puzzles/sha256tree_module.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff02ff02ffff04ff02ffff04ff05ff80808080ffff04ffff01ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff02ffff04ff02ffff04ff09ff80808080ffff02ff02ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/sha256tree_module.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | eb4ead6576048c9d730b5ced00646c7fdd390649cfdf48a00de1590cdd8ee18f 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/singleton_launcher.clvm: -------------------------------------------------------------------------------- 1 | (mod (singleton_full_puzzle_hash amount key_value_list) 2 | 3 | (include condition_codes.clvm) 4 | 5 | ; takes a lisp tree and returns the hash of it 6 | (defun sha256tree1 (TREE) 7 | (if (l TREE) 8 | (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) 9 | (sha256 1 TREE) 10 | ) 11 | ) 12 | 13 | ; main 14 | (list (list CREATE_COIN singleton_full_puzzle_hash amount) 15 | (list CREATE_COIN_ANNOUNCEMENT (sha256tree1 (list singleton_full_puzzle_hash amount key_value_list)))) 16 | ) 17 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/singleton_launcher.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff04ff04ffff04ff05ffff04ff0bff80808080ffff04ffff04ff0affff04ffff02ff0effff04ff02ffff04ffff04ff05ffff04ff0bffff04ff17ff80808080ff80808080ff808080ff808080ffff04ffff01ff33ff3cff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/singleton_launcher.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | eff07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/singleton_top_layer.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 24e044101e57b3d8c908b8a38ad57848afd29d3eecc439dba45f4412df4954fd 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/singleton_truths.clib: -------------------------------------------------------------------------------- 1 | ( 2 | (defun-inline truth_data_to_truth_struct (my_id full_puzhash innerpuzhash my_amount lineage_proof singleton_struct) (c (c my_id full_puzhash) (c (c innerpuzhash my_amount) (c lineage_proof singleton_struct)))) 3 | 4 | (defun-inline my_id_truth (Truths) (f (f Truths))) 5 | (defun-inline my_full_puzzle_hash_truth (Truths) (r (f Truths))) 6 | (defun-inline my_inner_puzzle_hash_truth (Truths) (f (f (r Truths)))) 7 | (defun-inline my_amount_truth (Truths) (r (f (r Truths)))) 8 | (defun-inline my_lineage_proof_truth (Truths) (f (r (r Truths)))) 9 | (defun-inline singleton_struct_truth (Truths) (r (r (r Truths)))) 10 | 11 | (defun-inline singleton_mod_hash_truth (Truths) (f (singleton_struct_truth Truths))) 12 | (defun-inline singleton_launcher_id_truth (Truths) (f (r (singleton_struct_truth Truths)))) 13 | (defun-inline singleton_launcher_puzzle_hash_truth (Truths) (f (r (r (singleton_struct_truth Truths))))) 14 | 15 | (defun-inline parent_info_for_lineage_proof (lineage_proof) (f lineage_proof)) 16 | (defun-inline puzzle_hash_for_lineage_proof (lineage_proof) (f (r lineage_proof))) 17 | (defun-inline amount_for_lineage_proof (lineage_proof) (f (r (r lineage_proof)))) 18 | (defun-inline is_not_eve_proof (lineage_proof) (r (r lineage_proof))) 19 | (defun-inline parent_info_for_eve_proof (lineage_proof) (f lineage_proof)) 20 | (defun-inline amount_for_eve_proof (lineage_proof) (f (r lineage_proof))) 21 | ) -------------------------------------------------------------------------------- /tad/wallet/puzzles/test_generator_deserialize.clvm: -------------------------------------------------------------------------------- 1 | (mod (deserializer generator_list reserved_arg) 2 | (a deserializer (list reserved_arg)) 3 | ) 4 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/test_generator_deserialize.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ff02ffff04ff0bff808080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/test_generator_deserialize.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 52add794fc76e89512e4a063c383418bda084c8a78c74055abe80179e4a7832c 2 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/test_multiple_generator_input_arguments.clvm: -------------------------------------------------------------------------------- 1 | 2 | (mod (decompress_puzzle decompress_coin_spend_entry start1 end1 start2 end2 compressed_cses deserialize gen_list reserved_arg) 3 | 4 | (defun decompress_cses (decompress_puzzle decompress_coin_spend_entry cses deserialize puzzle_prefix) 5 | (if cses 6 | (c (a decompress_coin_spend_entry (list deserialize decompress_puzzle puzzle_prefix (f cses))) 7 | (decompress_cses decompress_puzzle decompress_coin_spend_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_spend_entry compressed_cses deserialize (join_gen_args gen_list start1 end1 start2 end2))) 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /tad/wallet/puzzles/test_multiple_generator_input_arguments.clvm.hex: -------------------------------------------------------------------------------- 1 | ff02ffff01ff04ffff02ff04ffff04ff02ffff04ff05ffff04ff0bffff04ff82017fffff04ff8202ffffff04ffff02ff06ffff04ff02ffff04ff8205ffffff04ff17ffff04ff2fffff04ff5fffff04ff81bfff8080808080808080ff8080808080808080ff8080ffff04ffff01ffff02ffff03ff17ffff01ff04ffff02ff0bffff04ff2fffff04ff05ffff04ff5fffff04ff27ff808080808080ffff02ff04ffff04ff02ffff04ff05ffff04ff0bffff04ff37ffff04ff2fffff04ff5fff808080808080808080ff8080ff0180ff0effff0cff09ff0bff1780ffff0cff15ff2fff5f8080ff018080 -------------------------------------------------------------------------------- /tad/wallet/puzzles/test_multiple_generator_input_arguments.clvm.hex.sha256tree: -------------------------------------------------------------------------------- 1 | 156dafbddc3e1d3bfe1f2a84e48e5e46b287b8358bf65c3c091c93e855fbfc5b 2 | -------------------------------------------------------------------------------- /tad/wallet/rl_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/rl_wallet/__init__.py -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/settings/default_settings.py: -------------------------------------------------------------------------------- 1 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/settings/settings_objects.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/trade_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Optional, Tuple 3 | 4 | from tad.types.blockchain_format.coin import Coin 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.types.spend_bundle import SpendBundle 7 | from tad.util.ints import uint8, uint32, uint64 8 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/trading/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/trading/__init__.py -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/wallet/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tad/wallet/util/__init__.py -------------------------------------------------------------------------------- /tad/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 | -------------------------------------------------------------------------------- /tad/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 | POOLING_WALLET = 9 16 | -------------------------------------------------------------------------------- /tad/wallet/wallet_action.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | from tad.util.ints import uint32 5 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/wallet_coin_record.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from tad.types.blockchain_format.coin import Coin 4 | from tad.types.blockchain_format.sized_bytes import bytes32 5 | from tad.util.ints import uint32 6 | from tad.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 | -------------------------------------------------------------------------------- /tad/wallet/wallet_info.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List 3 | 4 | from tad.util.ints import uint8, uint32 5 | from tad.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 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/__init__.py -------------------------------------------------------------------------------- /tests/blockchain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/blockchain/__init__.py -------------------------------------------------------------------------------- /tests/blockchain/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/check_pytest_monitor_output.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | 4 | ret = 0 5 | 6 | # example input line 7 | # test_non_tx_aggregate_limits 0.997759588095738 1.45325589179993 554.45703125 8 | for ln in sys.stdin: 9 | line = ln.strip().split() 10 | 11 | print(f"{float(line[1]) * 100.0: 8.1f}% CPU {float(line[2]):7.1f}s {float(line[3]): 8.2f} MB RAM {line[0]}") 12 | limit = 800 13 | 14 | # until this can be optimized, use higher limits 15 | if "test_duplicate_coin_announces" in line[0]: 16 | limit = 2200 17 | elif ( 18 | "test_duplicate_large_integer_substr" in line[0] 19 | or "test_duplicate_reserve_fee" in line[0] 20 | or "test_duplicate_large_integer_negative" in line[0] 21 | or "test_duplicate_large_integer" in line[0] 22 | ): 23 | limit = 1100 24 | 25 | if float(line[3]) > limit: 26 | print(" ERROR: ^^ exceeded RAM limit ^^ \n") 27 | ret += 1 28 | 29 | if ret > 0: 30 | print("some tests used too much RAM") 31 | 32 | sys.exit(ret) 33 | -------------------------------------------------------------------------------- /tests/clvm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/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 tad.types.blockchain_format.program import Program, SerializedProgram, INFINITE_COST 4 | from tad.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/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/core/__init__.py -------------------------------------------------------------------------------- /tests/core/consensus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/core/consensus/__init__.py -------------------------------------------------------------------------------- /tests/core/custom_types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/core/custom_types/__init__.py -------------------------------------------------------------------------------- /tests/core/custom_types/test_coin.py: -------------------------------------------------------------------------------- 1 | from tad.types.blockchain_format.coin import Coin 2 | from tad.types.blockchain_format.sized_bytes import bytes32 3 | from tad.util.ints import uint64 4 | from tad.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/custom_types/test_proof_of_space.py: -------------------------------------------------------------------------------- 1 | from secrets import token_bytes 2 | 3 | from tad.consensus.default_constants import DEFAULT_CONSTANTS 4 | from tad.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/full_node/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/core/full_node/__init__.py -------------------------------------------------------------------------------- /tests/core/full_node/config.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: E501 2 | job_timeout = 60 3 | CHECK_RESOURCE_USAGE = """ 4 | - name: Check resource usage 5 | run: | 6 | sqlite3 -readonly -separator " " .pymon "select item,cpu_usage,total_time,mem_usage from TEST_METRICS order by mem_usage desc;" >metrics.out 7 | ./tests/check_pytest_monitor_output.py Tuple[aiosqlite.Connection, Blockchain]: 14 | connection = await aiosqlite.connect(":memory:") 15 | db_wrapper = DBWrapper(connection) 16 | block_store = await BlockStore.create(db_wrapper) 17 | coin_store = await CoinStore.create(db_wrapper) 18 | hint_store = await HintStore.create(db_wrapper) 19 | blockchain = await Blockchain.create(coin_store, block_store, consensus_constants, hint_store) 20 | return connection, blockchain 21 | -------------------------------------------------------------------------------- /tests/core/full_node/test_node_load.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | 4 | import pytest 5 | 6 | from tad.protocols import full_node_protocol 7 | from tad.types.peer_info import PeerInfo 8 | from tad.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 | 12 | 13 | def node_height_between(node, h1, h2): 14 | if node.full_node.blockchain.get_peak() is not None: 15 | height = node.full_node.blockchain.get_peak().height 16 | return h1 <= height <= h2 17 | return False 18 | -------------------------------------------------------------------------------- /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 tad.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 tad.util.setproctitle import setproctitle 4 | 5 | 6 | class TestSetProcTitle(unittest.TestCase): 7 | def test_does_not_crash(self): 8 | setproctitle("tad test title") 9 | -------------------------------------------------------------------------------- /tests/core/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/core/util/__init__.py -------------------------------------------------------------------------------- /tests/core/util/test_cached_bls.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from blspy import AugSchemeMPL 3 | from tad.util import cached_bls 4 | from tad.util.lru_cache import LRUCache 5 | 6 | 7 | class TestCachedBLS(unittest.TestCase): 8 | def test_cached_bls(self): 9 | n_keys = 10 10 | seed = b"a" * 31 11 | sks = [AugSchemeMPL.key_gen(seed + bytes([i])) for i in range(n_keys)] 12 | pks = [sk.get_g1() for sk in sks] 13 | 14 | msgs = [("msg-%d" % (i,)).encode() for i in range(n_keys)] 15 | sigs = [AugSchemeMPL.sign(sk, msg) for sk, msg in zip(sks, msgs)] 16 | agg_sig = AugSchemeMPL.aggregate(sigs) 17 | 18 | pks_half = pks[: n_keys // 2] 19 | msgs_half = msgs[: n_keys // 2] 20 | sigs_half = sigs[: n_keys // 2] 21 | agg_sig_half = AugSchemeMPL.aggregate(sigs_half) 22 | 23 | assert AugSchemeMPL.aggregate_verify(pks, msgs, agg_sig) 24 | 25 | # Verify with empty cache and populate it 26 | assert cached_bls.aggregate_verify(pks_half, msgs_half, agg_sig_half, True) 27 | # Verify with partial cache hit 28 | assert cached_bls.aggregate_verify(pks, msgs, agg_sig, True) 29 | # Verify with full cache hit 30 | assert cached_bls.aggregate_verify(pks, msgs, agg_sig) 31 | 32 | # Use a small cache which can not accommodate all pairings 33 | local_cache = LRUCache(n_keys // 2) 34 | # Verify signatures and cache pairings one at a time 35 | for pk, msg, sig in zip(pks_half, msgs_half, sigs_half): 36 | assert cached_bls.aggregate_verify([pk], [msg], sig, True, local_cache) 37 | # Verify the same messages with aggregated signature (full cache hit) 38 | assert cached_bls.aggregate_verify(pks_half, msgs_half, agg_sig_half, False, local_cache) 39 | # Verify more messages (partial cache hit) 40 | assert cached_bls.aggregate_verify(pks, msgs, agg_sig, False, local_cache) 41 | -------------------------------------------------------------------------------- /tests/core/util/test_lru_cache.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from tad.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 tad.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 = -0b1000000111 21 | assert truncate_to_significant_bits(a, 500) == a 22 | a = 0b10101 23 | assert truncate_to_significant_bits(a, 5) == a 24 | a = 0b10101 25 | assert truncate_to_significant_bits(a, 4) == 0b10100 26 | 27 | def test_count_significant_bits(self): 28 | assert count_significant_bits(0b0001) == 1 29 | assert count_significant_bits(0b00010) == 1 30 | assert count_significant_bits(0b01010) == 3 31 | assert count_significant_bits(-0b01010) == 3 32 | assert count_significant_bits(0b0) == 0 33 | assert count_significant_bits(0b1) == 1 34 | assert count_significant_bits(0b1000010101010000) == 12 35 | 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /tests/pools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/pools/__init__.py -------------------------------------------------------------------------------- /tests/pools/test_pool_config.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: E501 2 | from pathlib import Path 3 | 4 | from blspy import AugSchemeMPL, PrivateKey 5 | 6 | from tad.pools.pool_config import PoolWalletConfig 7 | from tad.util.config import load_config, save_config, create_default_tad_config 8 | 9 | 10 | def test_pool_config(): 11 | test_root = Path("/tmp") 12 | test_path = Path("/tmp/config") 13 | eg_config = test_path / "config.yaml" 14 | to_config = test_path / "test_pool_config.yaml" 15 | 16 | create_default_tad_config(test_root, ["config.yaml"]) 17 | assert eg_config.exists() 18 | eg_config.rename(to_config) 19 | config = load_config(test_root, "test_pool_config.yaml") 20 | 21 | auth_sk: PrivateKey = AugSchemeMPL.key_gen(b"1" * 32) 22 | d = { 23 | "authentication_public_key": bytes(auth_sk.get_g1()).hex(), 24 | "owner_public_key": "84c3fcf9d5581c1ddc702cb0f3b4a06043303b334dd993ab42b2c320ebfa98e5ce558448615b3f69638ba92cf7f43da5", 25 | "p2_singleton_puzzle_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", 26 | "payout_instructions": "c2b08e41d766da4116e388357ed957d04ad754623a915f3fd65188a8746cf3e8", 27 | "pool_url": "localhost", 28 | "launcher_id": "ae4ef3b9bfe68949691281a015a9c16630fc8f66d48c19ca548fb80768791afa", 29 | "target_puzzle_hash": "344587cf06a39db471d2cc027504e8688a0a67cce961253500c956c73603fd58", 30 | } 31 | 32 | pwc = PoolWalletConfig.from_json_dict(d) 33 | 34 | config_a = config.copy() 35 | config_b = config.copy() 36 | 37 | config_a["wallet"]["pool_list"] = [d] 38 | config_b["wallet"]["pool_list"] = [pwc.to_json_dict()] 39 | 40 | print(config["wallet"]["pool_list"]) 41 | save_config(test_root, "test_pool_config_a.yaml", config_a) 42 | save_config(test_root, "test_pool_config_b.yaml", config_b) 43 | assert config_a == config_b 44 | -------------------------------------------------------------------------------- /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: 'Chia-Network/test-cache' 5 | path: '.tad' 6 | ref: '0.27.0' 7 | fetch-depth: 1 8 | -------------------------------------------------------------------------------- /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/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/simulation/__init__.py -------------------------------------------------------------------------------- /tests/simulation/config.py: -------------------------------------------------------------------------------- 1 | job_timeout = 60 2 | -------------------------------------------------------------------------------- /tests/tad-start-sim: -------------------------------------------------------------------------------- 1 | _kill_servers() { 2 | PROCS=`ps -e | grep -E 'tad|vdf_client' -v "tad-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 tad 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 tad_farmer --logging.log_stdout=True --logging.log_level=INFO 33 | _run_bg_cmd tad_harvester --logging.log_stdout=True --logging.log_level=INFO 34 | _run_bg_cmd tad_timelord --logging.log_stdout=True --logging.log_level=INFO 35 | _run_bg_cmd tad_timelord_launcher --logging.log_stdout=True --logging.log_level=INFO 36 | _run_bg_cmd tad_introducer --logging.log_stdout=True --logging.log_level=INFO 37 | _run_bg_cmd tad_full_node --port=4044 --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 tad_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 tad.daemon.server --logging.log_stdout=True --logging.log_level=INFO 41 | 42 | wait 43 | -------------------------------------------------------------------------------- /tests/testconfig.py: -------------------------------------------------------------------------------- 1 | # Github actions template config. 2 | oses = ["ubuntu", "macos"] 3 | root_test_dirs = ["blockchain", "clvm", "core", "generator", "pools", "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 | custom_vars = ["CHECK_RESOURCE_USAGE"] 11 | -------------------------------------------------------------------------------- /tests/time_out_assert.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | import time 4 | from typing import Callable 5 | 6 | from tad.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/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/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 tad.full_node.mempool_check_conditions import get_name_puzzle_conditions 4 | from tad.types.blockchain_format.coin import Coin 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.types.full_block import FullBlock 7 | from tad.types.generator_types import BlockGenerator 8 | from tad.util.generator_tools import additions_for_npc 9 | 10 | 11 | def run_and_get_removals_and_additions( 12 | block: FullBlock, max_cost: int, cost_per_byte: 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( 23 | BlockGenerator(block.transactions_generator, []), 24 | max_cost, 25 | cost_per_byte=cost_per_byte, 26 | safe_mode=safe_mode, 27 | ) 28 | # build removals list 29 | for npc in npc_result.npc_list: 30 | removals.append(npc.coin_name) 31 | additions.extend(additions_for_npc(npc_result.npc_list)) 32 | 33 | rewards = block.get_included_reward_coins() 34 | additions.extend(rewards) 35 | return removals, additions 36 | -------------------------------------------------------------------------------- /tests/util/key_tool.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from blspy import AugSchemeMPL, G2Element, PrivateKey 4 | 5 | from tad.types.blockchain_format.sized_bytes import bytes32 6 | from tad.types.coin_spend import CoinSpend 7 | from tad.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 tests.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_spend: CoinSpend, additional_data: bytes) -> AugSchemeMPL: 29 | signatures = [] 30 | err, conditions, cost = conditions_for_solution( 31 | coin_spend.puzzle_reveal, coin_spend.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_spend.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/wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/wallet/__init__.py -------------------------------------------------------------------------------- /tests/wallet/cc_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/wallet/cc_wallet/__init__.py -------------------------------------------------------------------------------- /tests/wallet/rl_wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/wallet/rl_wallet/__init__.py -------------------------------------------------------------------------------- /tests/wallet/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/tests/wallet/rpc/__init__.py -------------------------------------------------------------------------------- /tests/wallet/sync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tad-Network/tad-blockchain/5fb43935cb741a820a1c10470cd6bff552e1a161/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 tad.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 tad.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 | --------------------------------------------------------------------------------