From e9de1d047c91866c765590b57eb86e2b37d5c85d Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Tue, 7 Sep 2021 16:54:46 +0200 Subject: [PATCH 01/77] feat: add stub for validator node (#3292) --- .circleci/config.yml | 2 + Cargo.lock | 235 +++++++- Cargo.toml | 2 + .../tari_app_grpc/proto/base_node.proto | 16 + applications/tari_app_grpc/proto/types.proto | 22 + applications/tari_app_grpc/proto/wallet.proto | 28 + .../src/conversions/output_features.rs | 65 ++- .../src/conversions/transaction.rs | 5 +- .../src/conversions/transaction_input.rs | 5 +- .../src/conversions/transaction_output.rs | 18 +- .../src/conversions/unblinded_output.rs | 11 +- .../src/grpc/base_node_grpc_server.rs | 71 ++- applications/tari_console_wallet/Cargo.toml | 2 +- .../src/automation/command_parser.rs | 60 ++ .../src/automation/commands.rs | 55 +- .../src/grpc/wallet_grpc_server.rs | 90 ++- .../tari_console_wallet/src/notifier/mod.rs | 2 +- .../tari_console_wallet/src/ui/app.rs | 8 +- .../src/ui/components/assets_tab.rs | 75 +++ .../src/ui/components/base_node.rs | 4 +- .../src/ui/components/events_component.rs | 48 ++ .../src/ui/components/mod.rs | 5 +- .../src/ui/components/receive_tab.rs | 5 +- .../src/ui/components/styles.rs | 10 + .../src/ui/components/tokens_component.rs | 74 +++ .../src/ui/components/transactions_tab.rs | 12 +- .../tari_console_wallet/src/ui/mod.rs | 4 + .../src/ui/state/app_state.rs | 83 ++- .../tari_console_wallet/src/ui/state/tasks.rs | 4 +- .../src/ui/state/wallet_event_monitor.rs | 11 +- .../src/ui/widgets/table.rs | 0 applications/tari_dan_node/Cargo.toml | 41 ++ applications/tari_dan_node/build.rs | 35 ++ .../tari_dan_node/proto/dan_node.proto | 51 ++ applications/tari_dan_node/src/cmd_args.rs | 36 ++ .../tari_dan_node/src/dan_layer/dan_node.rs | 320 +++++++++++ .../tari_dan_node/src/dan_layer/mod.rs | 30 + .../src/dan_layer/models/block.rs | 33 ++ .../src/dan_layer/models/committee.rs | 59 ++ .../consensus_worker_domain_event.rs | 44 ++ .../src/dan_layer/models/domain_events/mod.rs | 24 + .../src/dan_layer/models/hot_stuff_message.rs | 158 ++++++ .../dan_layer/models/hot_stuff_tree_node.rs | 78 +++ .../src/dan_layer/models/instruction.rs | 98 ++++ .../src/dan_layer/models/instruction_set.rs | 87 +++ .../tari_dan_node/src/dan_layer/models/mod.rs | 173 ++++++ .../dan_layer/models/quorum_certificate.rs | 84 +++ .../src/dan_layer/models/replica_info.rs | 25 + .../src/dan_layer/models/view.rs | 36 ++ .../src/dan_layer/models/view_id.rs | 52 ++ .../dan_layer/services/bft_replica_service.rs | 61 ++ .../dan_layer/services/events_publisher.rs | 47 ++ .../inbound_connection_service.rs | 126 +++++ .../infrastructure_services/mocks/mod.rs | 145 +++++ .../services/infrastructure_services/mod.rs | 33 ++ .../node_addressable.rs | 32 ++ .../outbound_service.rs | 112 ++++ .../src/dan_layer/services/mempool_service.rs | 99 ++++ .../src/dan_layer/services/mocks/mod.rs | 191 +++++++ .../src/dan_layer/services/mod.rs | 41 ++ .../dan_layer/services/payload_processor.rs | 69 +++ .../dan_layer/services/payload_provider.rs | 60 ++ .../src/dan_layer/services/signing_service.rs | 52 ++ .../dan_layer/services/template_service.rs | 122 ++++ .../src/dan_layer/storage/asset_data_store.rs | 109 ++++ .../src/dan_layer/storage/mod.rs | 25 + .../src/dan_layer/template_command.rs | 32 ++ .../templates/editable_metadata_template.rs | 78 +++ .../src/dan_layer/templates/mod.rs | 23 + .../src/dan_layer/workers/consensus_worker.rs | 446 +++++++++++++++ .../src/dan_layer/workers/mod.rs | 26 + .../dan_layer/workers/states/commit_state.rs | 286 ++++++++++ .../dan_layer/workers/states/decide_state.rs | 295 ++++++++++ .../src/dan_layer/workers/states/mod.rs | 85 +++ .../src/dan_layer/workers/states/next_view.rs | 61 ++ .../workers/states/pre_commit_state.rs | 285 ++++++++++ .../src/dan_layer/workers/states/prepare.rs | 377 +++++++++++++ .../src/dan_layer/workers/states/starting.rs | 34 ++ .../tari_dan_node/src/dan_node_config.rs | 25 + .../tari_dan_node/src/digital_assets_error.rs | 33 ++ .../tari_dan_node/src/grpc/dan_grpc_server.rs | 90 +++ applications/tari_dan_node/src/grpc/mod.rs | 26 + applications/tari_dan_node/src/main.rs | 135 +++++ applications/tari_dan_node/src/p2p/mod.rs | 189 +++++++ .../p2p/proto/dan_consensus_messages.proto | 53 ++ applications/tari_dan_node/src/types.rs | 49 ++ applications/tari_explorer/app.js | 14 +- applications/tari_explorer/package-lock.json | 246 +------- applications/tari_explorer/package.json | 4 +- applications/tari_explorer/routes/assets.js | 55 ++ applications/tari_explorer/routes/blocks.js | 63 ++- applications/tari_explorer/routes/index.js | 24 +- applications/tari_explorer/views/404.hbs | 1 + applications/tari_explorer/views/assets.hbs | 19 + applications/tari_explorer/views/blocks.hbs | 10 + applications/tari_explorer/views/index.hbs | 39 +- applications/tari_json_rpc_proxy/Cargo.toml | 12 + applications/tari_json_rpc_proxy/src/main.rs | 126 +++++ .../comms_interface/comms_request.rs | 2 + .../comms_interface/comms_response.rs | 2 + .../comms_interface/inbound_handlers.rs | 73 ++- .../comms_interface/local_interface.rs | 7 + .../core/src/base_node/proto/request.rs | 3 + .../core/src/base_node/proto/response.rs | 3 + .../state_machine_service/state_machine.rs | 2 +- base_layer/core/src/blocks/genesis_block.rs | 169 ++---- .../core/src/consensus/consensus_constants.rs | 14 +- .../core/src/consensus/consensus_manager.rs | 3 +- base_layer/core/src/proto/transaction.proto | 18 + base_layer/core/src/proto/transaction.rs | 74 +++ .../core/src/transactions/coinbase_builder.rs | 7 +- base_layer/core/src/transactions/helpers.rs | 6 + .../{transaction.rs => transaction/mod.rs} | 212 ++++++- .../transactions/transaction_protocol/mod.rs | 4 +- .../proto/recipient_signed_message.rs | 4 +- .../proto/transaction_sender.proto | 2 + .../proto/transaction_sender.rs | 7 +- .../transaction_protocol/recipient.rs | 3 +- .../transaction_protocol/sender.rs | 23 +- ...=> sender_transaction_protocol_builder.rs} | 39 +- .../transaction_protocol/single_receiver.rs | 2 + .../transaction_protocol/tx_id.rs | 77 +++ base_layer/p2p/src/proto/message_type.proto | 3 +- base_layer/wallet/README.md | 4 + .../2021-07-07-121212_add_unique_id/down.sql | 0 .../2021-07-07-121212_add_unique_id/up.sql | 9 + base_layer/wallet/src/assets/asset.rs | 63 +++ base_layer/wallet/src/assets/asset_manager.rs | 204 +++++++ .../wallet/src/assets/asset_manager_handle.rs | 107 ++++ .../infrastructure/asset_manager_service.rs | 110 ++++ .../src/assets/infrastructure/initializer.rs | 94 +++ .../wallet/src/assets/infrastructure/mod.rs | 52 ++ base_layer/wallet/src/assets/mod.rs | 31 + .../wallet/src/base_node_service/config.rs | 2 +- .../wallet/src/base_node_service/handle.rs | 15 +- base_layer/wallet/src/error.rs | 7 + base_layer/wallet/src/lib.rs | 6 + base_layer/wallet/src/operation_id.rs | 77 +++ .../src/output_manager_service/error.rs | 2 + .../src/output_manager_service/handle.rs | 170 +++++- .../wallet/src/output_manager_service/mod.rs | 2 - .../recovery/standard_outputs_recoverer.rs | 6 +- .../src/output_manager_service/service.rs | 233 +++++++- .../storage/database/backend.rs | 87 +++ .../storage/{database.rs => database/mod.rs} | 115 ++-- .../src/output_manager_service/storage/mod.rs | 2 + .../output_manager_service/storage/models.rs | 20 +- .../storage/output_status.rs | 87 +++ .../{sqlite_db.rs => sqlite_db/mod.rs} | 533 ++++++------------ .../storage/sqlite_db/new_output_sql.rs | 106 ++++ .../storage/sqlite_db/output_sql.rs | 236 ++++++++ .../tasks/txo_validation_task.rs | 4 +- base_layer/wallet/src/schema.rs | 9 + .../src/tokens/infrastructure/initializer.rs | 95 ++++ .../wallet/src/tokens/infrastructure/mod.rs | 34 ++ .../infrastructure/token_manager_service.rs | 99 ++++ base_layer/wallet/src/tokens/mod.rs | 30 + base_layer/wallet/src/tokens/token.rs | 70 +++ base_layer/wallet/src/tokens/token_manager.rs | 145 +++++ .../wallet/src/tokens/token_manager_handle.rs | 51 ++ .../wallet/src/transaction_service/error.rs | 11 +- .../wallet/src/transaction_service/handle.rs | 108 ++-- .../transaction_broadcast_protocol.rs | 21 +- ...ransaction_coinbase_monitoring_protocol.rs | 24 +- .../protocols/transaction_receive_protocol.rs | 30 +- .../protocols/transaction_send_protocol.rs | 12 +- .../transaction_validation_protocol.rs | 8 +- .../wallet/src/transaction_service/service.rs | 149 ++--- .../transaction_service/storage/database.rs | 3 +- .../src/transaction_service/storage/models.rs | 16 +- .../transaction_service/storage/sqlite_db.rs | 77 +-- .../tasks/send_finalized_transaction.rs | 6 +- .../tasks/send_transaction_cancelled.rs | 6 +- .../tasks/send_transaction_reply.rs | 3 +- .../transaction_service/tasks/wait_on_dial.rs | 2 +- base_layer/wallet/src/types.rs | 28 +- .../src/utxo_scanner_service/utxo_scanning.rs | 3 +- base_layer/wallet/src/wallet.rs | 25 +- base_layer/wallet_ffi/src/callback_handler.rs | 69 ++- base_layer/wallet_ffi/src/lib.rs | 23 +- common/src/configuration/bootstrap.rs | 13 + common/src/configuration/dan_config.rs | 58 ++ common/src/configuration/global.rs | 4 +- common/src/configuration/mod.rs | 2 + common/src/configuration/network.rs | 2 +- comms/src/connection_manager/wire_mode.rs | 2 +- 186 files changed, 10131 insertions(+), 1311 deletions(-) create mode 100644 applications/tari_console_wallet/src/ui/components/assets_tab.rs create mode 100644 applications/tari_console_wallet/src/ui/components/events_component.rs create mode 100644 applications/tari_console_wallet/src/ui/components/styles.rs create mode 100644 applications/tari_console_wallet/src/ui/components/tokens_component.rs create mode 100644 applications/tari_console_wallet/src/ui/widgets/table.rs create mode 100644 applications/tari_dan_node/Cargo.toml create mode 100644 applications/tari_dan_node/build.rs create mode 100644 applications/tari_dan_node/proto/dan_node.proto create mode 100644 applications/tari_dan_node/src/cmd_args.rs create mode 100644 applications/tari_dan_node/src/dan_layer/dan_node.rs create mode 100644 applications/tari_dan_node/src/dan_layer/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/block.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/committee.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/domain_events/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/hot_stuff_message.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/hot_stuff_tree_node.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/instruction.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/instruction_set.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/quorum_certificate.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/replica_info.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/view.rs create mode 100644 applications/tari_dan_node/src/dan_layer/models/view_id.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/events_publisher.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/infrastructure_services/node_addressable.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/mempool_service.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/mocks/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/payload_processor.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/payload_provider.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/signing_service.rs create mode 100644 applications/tari_dan_node/src/dan_layer/services/template_service.rs create mode 100644 applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs create mode 100644 applications/tari_dan_node/src/dan_layer/storage/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/template_command.rs create mode 100644 applications/tari_dan_node/src/dan_layer/templates/editable_metadata_template.rs create mode 100644 applications/tari_dan_node/src/dan_layer/templates/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/consensus_worker.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/mod.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/next_view.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs create mode 100644 applications/tari_dan_node/src/dan_layer/workers/states/starting.rs create mode 100644 applications/tari_dan_node/src/dan_node_config.rs create mode 100644 applications/tari_dan_node/src/digital_assets_error.rs create mode 100644 applications/tari_dan_node/src/grpc/dan_grpc_server.rs create mode 100644 applications/tari_dan_node/src/grpc/mod.rs create mode 100644 applications/tari_dan_node/src/main.rs create mode 100644 applications/tari_dan_node/src/p2p/mod.rs create mode 100644 applications/tari_dan_node/src/p2p/proto/dan_consensus_messages.proto create mode 100644 applications/tari_dan_node/src/types.rs create mode 100644 applications/tari_explorer/routes/assets.js create mode 100644 applications/tari_explorer/views/404.hbs create mode 100644 applications/tari_explorer/views/assets.hbs create mode 100644 applications/tari_json_rpc_proxy/Cargo.toml create mode 100644 applications/tari_json_rpc_proxy/src/main.rs rename base_layer/core/src/transactions/{transaction.rs => transaction/mod.rs} (88%) rename base_layer/core/src/transactions/transaction_protocol/{transaction_initializer.rs => sender_transaction_protocol_builder.rs} (97%) create mode 100644 base_layer/core/src/transactions/transaction_protocol/tx_id.rs create mode 100644 base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/down.sql create mode 100644 base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql create mode 100644 base_layer/wallet/src/assets/asset.rs create mode 100644 base_layer/wallet/src/assets/asset_manager.rs create mode 100644 base_layer/wallet/src/assets/asset_manager_handle.rs create mode 100644 base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs create mode 100644 base_layer/wallet/src/assets/infrastructure/initializer.rs create mode 100644 base_layer/wallet/src/assets/infrastructure/mod.rs create mode 100644 base_layer/wallet/src/assets/mod.rs create mode 100644 base_layer/wallet/src/operation_id.rs create mode 100644 base_layer/wallet/src/output_manager_service/storage/database/backend.rs rename base_layer/wallet/src/output_manager_service/storage/{database.rs => database/mod.rs} (85%) create mode 100644 base_layer/wallet/src/output_manager_service/storage/output_status.rs rename base_layer/wallet/src/output_manager_service/storage/{sqlite_db.rs => sqlite_db/mod.rs} (82%) create mode 100644 base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs create mode 100644 base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs create mode 100644 base_layer/wallet/src/tokens/infrastructure/initializer.rs create mode 100644 base_layer/wallet/src/tokens/infrastructure/mod.rs create mode 100644 base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs create mode 100644 base_layer/wallet/src/tokens/mod.rs create mode 100644 base_layer/wallet/src/tokens/token.rs create mode 100644 base_layer/wallet/src/tokens/token_manager.rs create mode 100644 base_layer/wallet/src/tokens/token_manager_handle.rs create mode 100644 common/src/configuration/dan_config.rs diff --git a/.circleci/config.yml b/.circleci/config.yml index 495b0cd298..7aaeb31aba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,9 +85,11 @@ commands: TOOLCHAIN=$(cat rust-toolchain) rustup component add --toolchain $TOOLCHAIN clippy cargo clippy -- -D warnings -W clippy::cognitive_complexity + when: always - run: name: Run clippy (all targets) command: cargo clippy --all-targets -- -D warnings + when: always - save_cache: paths: - /usr/local/cargo/registry diff --git a/Cargo.lock b/Cargo.lock index cacc5c49f3..9907c8a623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,18 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +[[package]] +name = "bytecodec" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf4c9d0bbf32eea58d7c0f812058138ee8edaf0f2802b6d03561b504729a325" +dependencies = [ + "byteorder", + "serde 1.0.130", + "serde_json", + "trackable 0.2.24", +] + [[package]] name = "bytemuck" version = "1.7.2" @@ -898,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" dependencies = [ "bitflags 1.3.2", - "crossterm_winapi", + "crossterm_winapi 0.6.2", "lazy_static 1.4.0", "libc", "mio 0.7.13", @@ -907,6 +919,22 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi 0.7.0", + "lazy_static 1.4.0", + "libc", + "mio 0.7.13", + "parking_lot 0.11.2", + "signal-hook", + "winapi 0.3.9", +] + [[package]] name = "crossterm_winapi" version = "0.6.2" @@ -916,6 +944,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -1675,6 +1712,19 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log 0.4.14", + "regex", +] + [[package]] name = "h2" version = "0.2.7" @@ -2047,6 +2097,54 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpc-core" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4467ab6dfa369b69e52bd0692e480c4d117410538526a57a304a0f2250fd95e" +dependencies = [ + "futures 0.3.16", + "futures-executor", + "futures-util", + "log 0.4.14", + "serde 1.0.130", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-http-server" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522a047cac0958097ee71d047dd71cb84979fd2fa21c7a68fbe12736bef870a2" +dependencies = [ + "futures 0.3.16", + "hyper 0.13.10", + "jsonrpc-core", + "jsonrpc-server-utils", + "log 0.4.14", + "net2", + "parking_lot 0.11.2", + "unicase 2.6.0", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bce68fa279a2822b3619369cd024f8a4f8e5ce485468834f8679a3c7919aae2d" +dependencies = [ + "bytes 0.5.6", + "futures 0.3.16", + "globset", + "jsonrpc-core", + "lazy_static 1.4.0", + "log 0.4.14", + "tokio 0.2.25", + "tokio-util 0.3.1", + "unicase 2.6.0", +] + [[package]] name = "keccak" version = "0.1.0" @@ -2999,6 +3097,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" +[[package]] +name = "patricia_tree" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c4b8ef84caee22395fa083b7d8ee9351e71cdf69a46c832528acdcac402117" +dependencies = [ + "bitflags 1.3.2", + "bytecodec", + "trackable 0.2.24", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -4474,7 +4583,7 @@ dependencies = [ "sha2", "structopt", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.9.6", "tempfile", "toml 0.5.8", "tracing", @@ -4531,7 +4640,7 @@ dependencies = [ "tari_crypto", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.9.6", "tempfile", "thiserror", "tokio 1.10.1", @@ -4579,7 +4688,7 @@ dependencies = [ "tari_crypto", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.9.6", "tari_utilities", "tempfile", "thiserror", @@ -4601,7 +4710,7 @@ dependencies = [ "quote 1.0.9", "syn 1.0.75", "tari_comms", - "tari_test_utils", + "tari_test_utils 0.9.6", "tokio 1.10.1", "tower-service", ] @@ -4613,7 +4722,7 @@ dependencies = [ "bitflags 1.3.2", "chrono", "chrono-english", - "crossterm", + "crossterm 0.17.7", "futures 0.3.16", "log 0.4.14", "opentelemetry", @@ -4690,7 +4799,7 @@ dependencies = [ "tari_service_framework", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.9.6", "tempfile", "thiserror", "tokio 1.10.1", @@ -4726,6 +4835,37 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tari_dan_node" +version = "0.8.11" +dependencies = [ + "anyhow", + "async-trait", + "bytecodec", + "clap", + "digest", + "futures 0.3.16", + "log 0.4.14", + "patricia_tree", + "prost", + "prost-types", + "serde_json", + "tari_app_utilities", + "tari_common", + "tari_comms", + "tari_comms_dht", + "tari_crypto", + "tari_mmr", + "tari_p2p", + "tari_service_framework", + "tari_shutdown", + "tari_test_utils 0.8.1", + "thiserror", + "tokio 0.2.25", + "tonic", + "tonic-build", +] + [[package]] name = "tari_infra_derive" version = "0.9.6" @@ -4736,6 +4876,15 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "tari_json_rpc_proxy" +version = "0.1.0" +dependencies = [ + "jsonrpc-http-server", + "tari_app_grpc", + "tari_utilities", +] + [[package]] name = "tari_key_manager" version = "0.9.6" @@ -4867,7 +5016,7 @@ dependencies = [ "tari_service_framework", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.9.6", "tari_utilities", "tempfile", "thiserror", @@ -4888,7 +5037,7 @@ dependencies = [ "futures-test", "log 0.4.14", "tari_shutdown", - "tari_test_utils", + "tari_test_utils 0.9.6", "thiserror", "tokio 1.10.1", "tower 0.3.1", @@ -4974,6 +5123,20 @@ dependencies = [ "url 2.2.2", ] +[[package]] +name = "tari_test_utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f11c0804a3f136ad0f821981411215886f0039bf1519c4ec0ec0af8098a8def" +dependencies = [ + "futures 0.3.16", + "futures-test", + "lazy_static 1.4.0", + "rand 0.7.3", + "tempfile", + "tokio 0.2.25", +] + [[package]] name = "tari_test_utils" version = "0.9.6" @@ -5039,7 +5202,7 @@ dependencies = [ "tari_service_framework", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.9.6", "tempfile", "thiserror", "time", @@ -5067,7 +5230,7 @@ dependencies = [ "tari_key_manager", "tari_p2p", "tari_shutdown", - "tari_test_utils", + "tari_test_utils 0.9.6", "tari_utilities", "tari_wallet", "tempfile", @@ -5241,8 +5404,10 @@ dependencies = [ "lazy_static 1.4.0", "memchr", "mio 0.6.23", + "num_cpus", "pin-project-lite 0.1.12", "slab", + "tokio-macros 0.2.6", ] [[package]] @@ -5260,7 +5425,7 @@ dependencies = [ "once_cell", "pin-project-lite 0.2.7", "signal-hook-registry", - "tokio-macros", + "tokio-macros 1.3.0", "winapi 0.3.9", ] @@ -5274,6 +5439,17 @@ dependencies = [ "tokio 1.10.1", ] +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2 1.0.28", + "quote 1.0.9", + "syn 1.0.75", +] + [[package]] name = "tokio-macros" version = "1.3.0" @@ -5716,6 +5892,35 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trackable" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98abb9e7300b9ac902cc04920945a874c1973e08c310627cc4458c04b70dd32" +dependencies = [ + "trackable 1.2.0", + "trackable_derive", +] + +[[package]] +name = "trackable" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "017e2a1a93718e4e8386d037cfb8add78f1d690467f4350fb582f55af1203167" +dependencies = [ + "trackable_derive", +] + +[[package]] +name = "trackable_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" +dependencies = [ + "quote 1.0.9", + "syn 1.0.75", +] + [[package]] name = "traitobject" version = "0.1.0" @@ -5800,13 +6005,13 @@ dependencies = [ [[package]] name = "tui" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2eaeee894a1e9b90f80aa466fe59154fdb471980b5e104d8836fcea309ae17e" +checksum = "861d8f3ad314ede6219bcb2ab844054b1de279ee37a9bc38e3d606f9d3fb2a71" dependencies = [ "bitflags 1.3.2", "cassowary", - "crossterm", + "crossterm 0.19.0", "unicode-segmentation", "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index ac268c2585..4432e38b34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ members = [ "applications/tari_merge_mining_proxy", "applications/tari_stratum_transcoder", "applications/tari_mining_node", + "applications/tari_dan_node", + "applications/tari_json_rpc_proxy" ] # #[profile.release] diff --git a/applications/tari_app_grpc/proto/base_node.proto b/applications/tari_app_grpc/proto/base_node.proto index 1396613acd..967433c85a 100644 --- a/applications/tari_app_grpc/proto/base_node.proto +++ b/applications/tari_app_grpc/proto/base_node.proto @@ -82,6 +82,22 @@ service BaseNode { rpc ListConnectedPeers(Empty) returns (ListConnectedPeersResponse); // Get mempool stats rpc GetMempoolStats(Empty) returns (MempoolStatsResponse); + + rpc GetTokens(GetTokensRequest) returns (stream GetTokensResponse); +} + +message GetTokensRequest { + bytes asset_public_key = 1; + // Optionally get a set of specific unique_ids + repeated bytes unique_ids = 2; +} + +message GetTokensResponse { + bytes unique_id = 1; + bytes asset_public_key = 2; + bytes owner_commitment = 3; + bytes mined_in_block = 4; + uint64 mined_height = 5; } message SubmitBlockResponse { diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index 49306660b7..accc648c82 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -190,6 +190,11 @@ message TransactionOutput { // Metadata signature with the homomorphic commitment private values (amount and blinding factor) and the sender // offset private key ComSignature metadata_signature = 7; + // Tari script offset pubkey, K_O + + // Unique id, e.g. for NFTs + bytes unique_id = 8; + bytes parent_public_key = 9; } // Options for UTXO's @@ -199,8 +204,23 @@ message OutputFeatures { // The maturity of the specific UTXO. This is the min lock height at which an UTXO can be spend. Coinbase UTXO // require a min maturity of the Coinbase_lock_height, this should be checked on receiving new blocks. uint64 maturity = 2; + + bytes metadata= 3; + + AssetOutputFeatures asset = 16; + MintNonFungibleFeatures mint_non_fungible = 17; +} + +message AssetOutputFeatures { + bytes public_key = 1; } +message MintNonFungibleFeatures { + bytes asset_public_key = 1; + bytes asset_owner_commitment = 2; +} + + // The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, // cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should // be sorted by their Blake2b-256bit digest hash @@ -288,6 +308,8 @@ message UnblindedOutput { bytes sender_offset_public_key = 8; // UTXO signature with the script offset private key, k_O ComSignature metadata_signature = 9; + // Unique Id + bytes unique_id = 10; } // ----------------------------- Network Types ----------------------------- // diff --git a/applications/tari_app_grpc/proto/wallet.proto b/applications/tari_app_grpc/proto/wallet.proto index aad57e28c4..935fc34286 100644 --- a/applications/tari_app_grpc/proto/wallet.proto +++ b/applications/tari_app_grpc/proto/wallet.proto @@ -54,6 +54,11 @@ service Wallet { rpc ListConnectedPeers(Empty) returns (ListConnectedPeersResponse); // Cancel pending transaction rpc CancelTransaction (CancelTransactionRequest) returns (CancelTransactionResponse); + + // Mint base layer tokens for an asset + rpc MintTokens(MintTokensRequest) returns (MintTokensResponse); + + rpc GetOwnedTokens(GetOwnedTokensRequest) returns (GetOwnedTokensResponse); } message GetVersionRequest { } @@ -189,6 +194,29 @@ message ImportUtxosResponse { repeated uint64 tx_ids = 1; } +message MintTokensRequest { + bytes asset_public_key = 1; + repeated bytes unique_ids = 2; +} + +message MintTokensResponse { + repeated bytes owner_commitments =1; +} + +message GetOwnedTokensRequest { + bytes asset_public_key = 1; +} + +message GetOwnedTokensResponse { + repeated TokenUtxo tokens =1; +} + +message TokenUtxo { + bytes commitment = 1; + bytes unique_id = 2; + bytes asset_public_key = 3; +} + message CancelTransactionRequest { uint64 tx_id = 1; } diff --git a/applications/tari_app_grpc/src/conversions/output_features.rs b/applications/tari_app_grpc/src/conversions/output_features.rs index 553219b958..66fc3f3d9a 100644 --- a/applications/tari_app_grpc/src/conversions/output_features.rs +++ b/applications/tari_app_grpc/src/conversions/output_features.rs @@ -21,8 +21,10 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::tari_rpc as grpc; -use std::convert::TryFrom; -use tari_core::transactions::transaction::{OutputFeatures, OutputFlags}; +use std::convert::{TryFrom, TryInto}; +use tari_common_types::types::{Commitment, PublicKey}; +use tari_core::transactions::transaction::{AssetOutputFeatures, MintNonFungibleFeatures, OutputFeatures, OutputFlags}; +use tari_crypto::tari_utilities::ByteArray; impl TryFrom for OutputFeatures { type Error = String; @@ -32,6 +34,65 @@ impl TryFrom for OutputFeatures { flags: OutputFlags::from_bits(features.flags as u8) .ok_or_else(|| "Invalid or unrecognised output flags".to_string())?, maturity: features.maturity, + metadata: features.metadata, + asset: features.asset.map(|a| a.try_into()).transpose()?, + mint_non_fungible: features.mint_non_fungible.map(|m| m.try_into()).transpose()?, }) } } + +impl From for grpc::OutputFeatures { + fn from(features: OutputFeatures) -> Self { + Self { + flags: features.flags.bits() as u32, + maturity: features.maturity, + metadata: features.metadata, + asset: features.asset.map(|a| a.into()), + mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), + } + } +} + +impl TryFrom for AssetOutputFeatures { + type Error = String; + + fn try_from(features: grpc::AssetOutputFeatures) -> Result { + let public_key = PublicKey::from_bytes(features.public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; + + Ok(Self { public_key }) + } +} + +impl From for grpc::AssetOutputFeatures { + fn from(features: AssetOutputFeatures) -> Self { + Self { + public_key: features.public_key.as_bytes().to_vec(), + } + } +} + +impl TryFrom for MintNonFungibleFeatures { + type Error = String; + + fn try_from(value: grpc::MintNonFungibleFeatures) -> Result { + let asset_public_key = + PublicKey::from_bytes(value.asset_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; + + let asset_owner_commitment = + Commitment::from_bytes(&value.asset_owner_commitment).map_err(|err| err.to_string())?; + + Ok(Self { + asset_public_key, + asset_owner_commitment, + }) + } +} + +impl From for grpc::MintNonFungibleFeatures { + fn from(value: MintNonFungibleFeatures) -> Self { + Self { + asset_public_key: value.asset_public_key.as_bytes().to_vec(), + asset_owner_commitment: value.asset_owner_commitment.to_vec(), + } + } +} diff --git a/applications/tari_app_grpc/src/conversions/transaction.rs b/applications/tari_app_grpc/src/conversions/transaction.rs index cb9e91f63a..36e13ccb45 100644 --- a/applications/tari_app_grpc/src/conversions/transaction.rs +++ b/applications/tari_app_grpc/src/conversions/transaction.rs @@ -57,7 +57,8 @@ impl TryFrom for Transaction { #[cfg(feature = "wallet")] mod wallet { use super::*; - use tari_wallet::{output_manager_service::TxId, transaction_service::storage::models}; + use tari_core::transactions::transaction_protocol::TxId; + use tari_wallet::transaction_service::storage::models; impl From for grpc::TransactionStatus { fn from(status: models::TransactionStatus) -> Self { @@ -88,7 +89,7 @@ mod wallet { impl grpc::TransactionInfo { pub fn not_found(tx_id: TxId) -> Self { Self { - tx_id, + tx_id: tx_id.into(), status: grpc::TransactionStatus::NotFound as i32, ..Default::default() } diff --git a/applications/tari_app_grpc/src/conversions/transaction_input.rs b/applications/tari_app_grpc/src/conversions/transaction_input.rs index 48eebe04ad..fadf2114a1 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_input.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_input.rs @@ -67,10 +67,7 @@ impl From for grpc::TransactionInput { fn from(input: TransactionInput) -> Self { let hash = input.hash(); Self { - features: Some(grpc::OutputFeatures { - flags: input.features.flags.bits() as u32, - maturity: input.features.maturity, - }), + features: Some(input.features.into()), commitment: Vec::from(input.commitment.as_bytes()), hash, script: input.script.as_bytes(), diff --git a/applications/tari_app_grpc/src/conversions/transaction_output.rs b/applications/tari_app_grpc/src/conversions/transaction_output.rs index 7b783e3498..b5946251df 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_output.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_output.rs @@ -54,13 +54,24 @@ impl TryFrom for TransactionOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; + let unique_id = if output.unique_id.is_empty() { None} else { Some(output.unique_id.clone())}; + + let parent_public_key = if output.parent_public_key.is_empty() { + None + } else { + Some(PublicKey::from_bytes(output.parent_public_key.as_bytes()) + .map_err(|err| format!("parent_public_key {:?}", err))?) + }; + Ok(Self { features, + unique_id, commitment, proof: BulletRangeProof(output.range_proof), script, sender_offset_public_key, metadata_signature, + parent_public_key }) } } @@ -70,10 +81,7 @@ impl From for grpc::TransactionOutput { let hash = output.hash(); grpc::TransactionOutput { hash, - features: Some(grpc::OutputFeatures { - flags: output.features.flags.bits() as u32, - maturity: output.features.maturity, - }), + features: Some(output.features.into()), commitment: Vec::from(output.commitment.as_bytes()), range_proof: Vec::from(output.proof.as_bytes()), script: output.script.as_bytes(), @@ -83,6 +91,8 @@ impl From for grpc::TransactionOutput { signature_u: Vec::from(output.metadata_signature.u().as_bytes()), signature_v: Vec::from(output.metadata_signature.v().as_bytes()), }), + unique_id: output.unique_id.unwrap_or_default(), + parent_public_key: output.parent_public_key.map(|b| b.to_vec()).unwrap_or_default() } } } diff --git a/applications/tari_app_grpc/src/conversions/unblinded_output.rs b/applications/tari_app_grpc/src/conversions/unblinded_output.rs index bf9efa58bc..bdb2047b47 100644 --- a/applications/tari_app_grpc/src/conversions/unblinded_output.rs +++ b/applications/tari_app_grpc/src/conversions/unblinded_output.rs @@ -36,10 +36,7 @@ impl From for grpc::UnblindedOutput { grpc::UnblindedOutput { value: u64::from(output.value), spending_key: output.spending_key.as_bytes().to_vec(), - features: Some(grpc::OutputFeatures { - flags: output.features.flags.bits() as u32, - maturity: output.features.maturity, - }), + features: Some(output.features.into()), script: output.script.as_bytes(), input_data: output.input_data.as_bytes(), script_private_key: output.script_private_key.as_bytes().to_vec(), @@ -49,6 +46,7 @@ impl From for grpc::UnblindedOutput { signature_u: Vec::from(output.metadata_signature.u().as_bytes()), signature_v: Vec::from(output.metadata_signature.v().as_bytes()), }), + unique_id: output.unique_id.unwrap_or_default() } } } @@ -82,6 +80,7 @@ impl TryFrom for UnblindedOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; + let unique_id = if output.unique_id.is_empty() { None } else {Some(output.unique_id.clone())}; Ok(Self { value: MicroTari::from(output.value), spending_key, @@ -91,6 +90,10 @@ impl TryFrom for UnblindedOutput { script_private_key, sender_offset_public_key, metadata_signature, + unique_id, + + // TODO: Remove this none + parent_public_key: None }) } } diff --git a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs index e3a19ef185..9839877281 100644 --- a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs +++ b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2021. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -116,6 +116,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { type GetNetworkDifficultyStream = mpsc::Receiver>; type GetPeersStream = mpsc::Receiver>; type GetTokensInCirculationStream = mpsc::Receiver>; + type GetTokensStream = mpsc::Receiver>; type ListHeadersStream = mpsc::Receiver>; type SearchKernelsStream = mpsc::Receiver>; @@ -350,6 +351,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { .collect(); while !page.is_empty() { trace!(target: LOG_TARGET, "Page: {:?}", page); + // TODO: Better error handling let result_headers = match handler.get_headers(page).await { Err(err) => { warn!(target: LOG_TARGET, "Error communicating with base node: {}", err,); @@ -389,6 +391,73 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { Ok(Response::new(rx)) } + async fn get_tokens( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + debug!( + target: LOG_TARGET, + "Incoming GRPC request for GetTokens: asset_pub_key: {}, unique_ids: [{}]", + request.asset_public_key.to_hex(), + request + .unique_ids + .iter() + .map(|s| s.to_hex()) + .collect::>() + .join(",") + ); + let mut handler = self.node_service.clone(); + let (mut tx, rx) = mpsc::channel(50); + task::spawn(async move { + let asset_pub_key_hex = request.asset_public_key.to_hex(); + debug!( + target: LOG_TARGET, + "Starting thread to process GetTokens: asset_pub_key: {}", asset_pub_key_hex, + ); + let tokens = match handler.get_tokens(request.asset_public_key, request.unique_ids).await { + Ok(tokens) => tokens, + Err(err) => { + warn!(target: LOG_TARGET, "Error communicating with base node: {:?}", err,); + return; + }, + }; + + debug!( + target: LOG_TARGET, + "Found {} tokens for {}", + tokens.len(), + asset_pub_key_hex + ); + + for token in tokens { + match tx + .send(Ok(tari_rpc::GetTokensResponse { + asset_public_key: token.parent_public_key.map(|pk| pk.to_vec()).unwrap_or_default(), + unique_id: token.unique_id.unwrap_or_default(), + owner_commitment: token.commitment.to_vec(), + mined_in_block: vec![], + mined_height: 0, + })) + .await + { + Ok(_) => (), + Err(err) => { + warn!(target: LOG_TARGET, "Error sending token via GRPC: {}", err); + match tx.send(Err(Status::unknown("Error sending data"))).await { + Ok(_) => (), + Err(send_err) => { + warn!(target: LOG_TARGET, "Error sending error to GRPC client: {}", send_err) + }, + } + return; + }, + } + } + }); + Ok(Response::new(rx)) + } + async fn get_new_block_template( &self, request: Request, diff --git a/applications/tari_console_wallet/Cargo.toml b/applications/tari_console_wallet/Cargo.toml index 9e6fefd046..1f3b1aa996 100644 --- a/applications/tari_console_wallet/Cargo.toml +++ b/applications/tari_console_wallet/Cargo.toml @@ -51,6 +51,6 @@ default-features = false features = ["transactions", "mempool_proto", "base_node_proto"] [dependencies.tui] -version = "^0.12" +version = "^0.15" default-features = false features = ["crossterm"] diff --git a/applications/tari_console_wallet/src/automation/command_parser.rs b/applications/tari_console_wallet/src/automation/command_parser.rs index 90f0347918..c0b7421067 100644 --- a/applications/tari_console_wallet/src/automation/command_parser.rs +++ b/applications/tari_console_wallet/src/automation/command_parser.rs @@ -34,6 +34,7 @@ use tari_comms::multiaddr::Multiaddr; use tari_common_types::types::PublicKey; use tari_core::transactions::tari_amount::MicroTari; +use std::iter::Peekable; #[derive(Debug)] pub struct ParsedCommand { @@ -58,6 +59,8 @@ impl Display for ParsedCommand { SetBaseNode => "set-base-node", SetCustomBaseNode => "set-custom-base-node", ClearCustomBaseNode => "clear-custom-base-node", + RegisterAsset => "register-asset", + MintTokens=> "mint-tokens" }; let args = self @@ -125,11 +128,68 @@ pub fn parse_command(command: &str) -> Result { SetBaseNode => parse_public_key_and_address(args)?, SetCustomBaseNode => parse_public_key_and_address(args)?, ClearCustomBaseNode => Vec::new(), + RegisterAsset => parser_builder(args).text().build()?, + // mint-tokens pub_key nft_id1 nft_id2 + MintTokens => parser_builder(args).pub_key().text_array().build()? }; Ok(ParsedCommand { command, args }) } +struct ArgParser<'a> { + args: Peekable>, + result: Vec> +} + +impl<'a> ArgParser<'a> { + fn new(args: SplitWhitespace<'a>) -> Self { + Self{ + args: args.peekable(), result: vec![] + } + } + fn text(mut self) -> Self { + let text_result = self.args.next().map(|t| ParsedArgument::Text(t.to_string())) + .ok_or_else(|| ParseError::Empty("text".to_string())); + self.result.push(text_result); + self + } + fn text_array(self) -> Self { + let mut me = self; + while me.args.peek().is_some() { + me = me.text(); + } + + me + } + + fn pub_key(mut self) -> Self { + // public key/emoji id + let pubkey = self.args + .next() + .ok_or_else(|| ParseError::Empty("public key or emoji id".to_string())); + let result = pubkey.and_then(|pb| { + match parse_emoji_id_or_public_key(pb).ok_or(ParseError::PublicKey) { + Ok(pk) => Ok(ParsedArgument::PublicKey(pk)), + Err(err) => Err(err) + } + }); + self.result.push(result); + self + } + + fn build(self) -> Result, ParseError> { + let mut result = Vec::with_capacity(self.result.len()); + for r in self.result { + result.push(r?); + } + Ok(result) + } +} + +fn parser_builder(args: SplitWhitespace) -> ArgParser { + ArgParser::new(args) +} + fn parse_whois(mut args: SplitWhitespace) -> Result, ParseError> { let mut parsed_args = Vec::new(); diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index 7bbdf8da44..1f667270ed 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -51,10 +51,11 @@ use tari_core::{ transactions::{ tari_amount::{uT, MicroTari, Tari}, transaction::UnblindedOutput, + transaction_protocol::TxId, }, }; use tari_wallet::{ - output_manager_service::{handle::OutputManagerHandle, TxId}, + output_manager_service::handle::OutputManagerHandle, transaction_service::handle::{TransactionEvent, TransactionServiceHandle}, WalletSqlite, }; @@ -82,6 +83,8 @@ pub enum WalletCommand { SetBaseNode, SetCustomBaseNode, ClearCustomBaseNode, + RegisterAsset, + MintTokens, } #[derive(Debug, EnumString, PartialEq, Clone)] @@ -133,7 +136,7 @@ pub async fn send_tari( ) -> Result { let (fee_per_gram, amount, dest_pubkey, message) = get_transaction_parameters(args)?; wallet_transaction_service - .send_transaction(dest_pubkey, amount, fee_per_gram, message) + .send_transaction(dest_pubkey, amount, None, fee_per_gram, message) .await .map_err(CommandError::TransactionServiceError) } @@ -145,7 +148,7 @@ pub async fn send_one_sided( ) -> Result { let (fee_per_gram, amount, dest_pubkey, message) = get_transaction_parameters(args)?; wallet_transaction_service - .send_one_sided_transaction(dest_pubkey, amount, fee_per_gram, message) + .send_one_sided_transaction(dest_pubkey, amount, None, fee_per_gram, message) .await .map_err(CommandError::TransactionServiceError) } @@ -554,7 +557,7 @@ pub async fn command_runner( let wait_stage = TransactionStage::from_str(&config.wallet_command_send_wait_stage) .map_err(|e| CommandError::Config(e.to_string()))?; - let transaction_service = wallet.transaction_service.clone(); + let mut transaction_service = wallet.transaction_service.clone(); let mut output_service = wallet.output_manager_service.clone(); let dht_service = wallet.dht_service.discovery_service_requester().clone(); let connectivity_requester = wallet.comms.connectivity(); @@ -684,6 +687,50 @@ pub async fn command_runner( .await?; println!("Custom base node peer cleared from wallet database."); }, + RegisterAsset => { + println!("Registering asset."); + let name = parsed.args[0].to_string(); + let mut manager = wallet.asset_manager.clone(); + let (tx_id, transaction) = manager.create_registration_transaction(name).await?; + let fee = transaction.body.get_total_fee(); + let _result = transaction_service + .submit_transaction(tx_id, transaction, fee, 0.into(), "test o ramam".to_string()) + .await?; + }, + MintTokens => { + println!("Minting tokens for asset"); + let public_key = match parsed.args[0] { + ParsedArgument::PublicKey(ref key) => Ok(key.clone()), + _ => Err(CommandError::Argument), + }?; + + let unique_ids: Vec> = parsed.args[1..] + .iter() + .map(|arg| { + let s = arg.to_string(); + match &s[0..2] { + "0x" => { + let s = s[2..].to_string(); + let r: Vec = Hex::from_hex(&s).unwrap(); + r + }, + _ => s.into_bytes(), + } + }) + .collect(); + + let mut asset_manager = wallet.asset_manager.clone(); + let asset = asset_manager.get_owned_asset_by_pub_key(&public_key).await?; + println!("Found asset:{}", asset.name()); + + let (tx_id, transaction) = asset_manager + .create_minting_transaction(&public_key, asset.owner_commitment(), unique_ids) + .await?; + let fee = transaction.body.get_total_fee(); + let _result = transaction_service + .submit_transaction(tx_id, transaction, fee, 0.into(), "test mint transaction".to_string()) + .await?; + }, } } diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index c18784d82a..58b12a76b6 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -1,10 +1,11 @@ +use crate::wallet_modes::grpc_mode; use futures::{channel::mpsc, future, SinkExt}; use log::*; use std::convert::TryFrom; use tari_app_grpc::{ conversions::naive_datetime_to_timestamp, - tari_rpc, tari_rpc::{ + self, payment_recipient::PaymentType, wallet_server, CoinSplitRequest, @@ -23,6 +24,8 @@ use tari_app_grpc::{ GetVersionResponse, ImportUtxosRequest, ImportUtxosResponse, + MintTokensRequest, + MintTokensResponse, TransactionDirection, TransactionInfo, TransactionStatus, @@ -31,7 +34,7 @@ use tari_app_grpc::{ TransferResult, }, }; -use tari_common_types::types::Signature; +use tari_common_types::types::{PublicKey, Signature}; use tari_comms::{types::CommsPublicKey, CommsNode}; use tari_core::{ tari_utilities::{hex::Hex, ByteArray}, @@ -167,7 +170,7 @@ impl wallet_server::Wallet for WalletGrpcServer { ( address, transaction_service - .send_transaction(pk, amount.into(), fee_per_gram.into(), message) + .send_transaction(pk, amount.into(), None, fee_per_gram.into(), message) .await, ) }); @@ -176,7 +179,7 @@ impl wallet_server::Wallet for WalletGrpcServer { ( address, transaction_service - .send_one_sided_transaction(pk, amount.into(), fee_per_gram.into(), message) + .send_one_sided_transaction(pk, amount.into(), None, fee_per_gram.into(), message) .await, ) }); @@ -192,7 +195,7 @@ impl wallet_server::Wallet for WalletGrpcServer { .map(|(address, result)| match result { Ok(tx_id) => TransferResult { address, - transaction_id: tx_id, + transaction_id: tx_id.into(), is_success: true, failure_message: Default::default(), }, @@ -221,6 +224,7 @@ impl wallet_server::Wallet for WalletGrpcServer { let message = request.into_inner(); let queries = message.transaction_ids.into_iter().map(|tx_id| { + let tx_id = tx_id.into(); let mut transaction_service = self.get_transaction_service(); async move { transaction_service @@ -266,7 +270,7 @@ impl wallet_server::Wallet for WalletGrpcServer { for (_, txn) in transactions { let response = GetCompletedTransactionsResponse { transaction: Some(TransactionInfo { - tx_id: txn.tx_id, + tx_id: txn.tx_id.into(), source_pk: txn.source_public_key.to_vec(), dest_pk: txn.destination_public_key.to_vec(), status: TransactionStatus::from(txn.status) as i32, @@ -326,7 +330,7 @@ impl wallet_server::Wallet for WalletGrpcServer { .await .map_err(|e| Status::internal(format!("{:?}", e)))?; - Ok(Response::new(CoinSplitResponse { tx_id })) + Ok(Response::new(CoinSplitResponse { tx_id: tx_id.into() })) } async fn import_utxos( @@ -350,13 +354,75 @@ impl wallet_server::Wallet for WalletGrpcServer { wallet .import_unblinded_utxo(o.clone(), &CommsPublicKey::default(), "Imported via gRPC".to_string()) .await - .map_err(|e| Status::internal(format!("{:?}", e)))?, + .map_err(|e| Status::internal(format!("{:?}", e)))? + .into(), ); } Ok(Response::new(ImportUtxosResponse { tx_ids })) } + async fn mint_tokens(&self, request: Request) -> Result, Status> { + let mut asset_manager = self.wallet.asset_manager.clone(); + let mut transaction_service = self.wallet.transaction_service.clone(); + let message = request.into_inner(); + + // TODO: Clean up unwrap + let asset_public_key = PublicKey::from_bytes(message.asset_public_key.as_slice()).unwrap(); + let asset = asset_manager + .get_owned_asset_by_pub_key(&asset_public_key) + .await + .map_err(|e| Status::internal(e.to_string()))?; + + let (tx_id, transaction) = asset_manager + .create_minting_transaction(&asset_public_key, asset.owner_commitment(), message.unique_ids) + .await + .map_err(|e| Status::internal(e.to_string()))?; + let fee = transaction.body.get_total_fee(); + + let owner_commitments = transaction + .body + .outputs() + .iter() + .filter_map(|o| o.unique_id.as_ref().map(|_| o.commitment.to_vec())) + .collect(); + let _result = transaction_service + .submit_transaction(tx_id, transaction, fee, 0.into(), "test mint transaction".to_string()) + .await + .map_err(|e| Status::internal(e.to_string()))?; + + Ok(Response::new(MintTokensResponse { owner_commitments })) + } + + async fn get_owned_tokens( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + let request_public_key = PublicKey::from_bytes(&request.asset_public_key) + .map_err(|e| Status::invalid_argument(format!("asset_public key was not a valid public key: {}", e)))?; + let mut token_manager = self.wallet.token_manager.clone(); + let owned = token_manager + .list_owned_tokens() + .await + .map_err(|e| Status::internal(e.to_string()))?; + let owned = owned + .into_iter() + .filter_map(|t| { + if t.asset_public_key() == &request_public_key { + Some(tari_rpc::TokenUtxo { + asset_public_key: Vec::from(t.asset_public_key().as_bytes()), + unique_id: Vec::from(t.unique_id()), + commitment: Vec::from(t.owner_commitment().as_bytes()), + }) + } else { + None + } + }) + .collect(); + Ok(Response::new(tari_rpc::GetOwnedTokensResponse { tokens: owned })) + } + async fn get_network_status( &self, _: Request, @@ -422,7 +488,7 @@ impl wallet_server::Wallet for WalletGrpcServer { ); let mut transaction_service = self.get_transaction_service(); - match transaction_service.cancel_transaction(message.tx_id).await { + match transaction_service.cancel_transaction(message.tx_id.into()).await { Ok(_) => { return Ok(Response::new(tari_rpc::CancelTransactionResponse { is_success: true, @@ -446,7 +512,7 @@ fn convert_wallet_transaction_into_transaction_info( use models::WalletTransaction::*; match tx { PendingInbound(tx) => TransactionInfo { - tx_id: tx.tx_id, + tx_id: tx.tx_id.into(), source_pk: tx.source_public_key.to_vec(), dest_pk: wallet_pk.to_vec(), status: TransactionStatus::from(tx.status) as i32, @@ -460,7 +526,7 @@ fn convert_wallet_transaction_into_transaction_info( valid: true, }, PendingOutbound(tx) => TransactionInfo { - tx_id: tx.tx_id, + tx_id: tx.tx_id.into(), source_pk: wallet_pk.to_vec(), dest_pk: tx.destination_public_key.to_vec(), status: TransactionStatus::from(tx.status) as i32, @@ -474,7 +540,7 @@ fn convert_wallet_transaction_into_transaction_info( valid: true, }, Completed(tx) => TransactionInfo { - tx_id: tx.tx_id, + tx_id: tx.tx_id.into(), source_pk: tx.source_public_key.to_vec(), dest_pk: tx.destination_public_key.to_vec(), status: TransactionStatus::from(tx.status) as i32, diff --git a/applications/tari_console_wallet/src/notifier/mod.rs b/applications/tari_console_wallet/src/notifier/mod.rs index 4ee45191f1..bd6e3b0436 100644 --- a/applications/tari_console_wallet/src/notifier/mod.rs +++ b/applications/tari_console_wallet/src/notifier/mod.rs @@ -28,7 +28,6 @@ use std::{ }; use tari_core::tari_utilities::hex::Hex; use tari_wallet::{ - output_manager_service::TxId, transaction_service::storage::models::{ CompletedTransaction, InboundTransaction, @@ -38,6 +37,7 @@ use tari_wallet::{ WalletSqlite, }; use tokio::runtime::Handle; +use tari_core::transactions::transaction_protocol::TxId; pub const LOG_TARGET: &str = "wallet::notifier"; const RECEIVED: &str = "received"; diff --git a/applications/tari_console_wallet/src/ui/app.rs b/applications/tari_console_wallet/src/ui/app.rs index 3cc3a80075..a582b5b50e 100644 --- a/applications/tari_console_wallet/src/ui/app.rs +++ b/applications/tari_console_wallet/src/ui/app.rs @@ -24,7 +24,9 @@ use crate::{ notifier::Notifier, ui::{ components::{ + assets_tab::AssetsTab, base_node::BaseNode, + events_component::EventsComponent, log_tab::LogTab, menu::Menu, network_tab::NetworkTab, @@ -32,6 +34,7 @@ use crate::{ receive_tab::ReceiveTab, send_tab::SendTab, tabs_container::TabsContainer, + tokens_component::TokensComponent, transactions_tab::TransactionsTab, Component, }, @@ -88,6 +91,9 @@ impl App { .add("Send".into(), Box::new(SendTab::new())) .add("Receive".into(), Box::new(ReceiveTab::new())) .add("Network".into(), Box::new(NetworkTab::new(base_node_selected))) + .add("Assets".into(), Box::new(AssetsTab::new())) + .add("Tokens".into(), Box::new(TokensComponent::new())) + .add("Events".into(), Box::new(EventsComponent::new())) .add("Log".into(), Box::new(LogTab::new())) .add("Notifications".into(), Box::new(NotificationTab::new())); @@ -166,7 +172,7 @@ impl App { .split(max_width_layout[0]); let title_halves = Layout::default() .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(55), Constraint::Percentage(45)].as_ref()) + .constraints([Constraint::Percentage(65), Constraint::Percentage(35)].as_ref()) .split(title_chunks[0]); self.tabs.draw_titles(f, title_halves[0], &self.app_state); diff --git a/applications/tari_console_wallet/src/ui/components/assets_tab.rs b/applications/tari_console_wallet/src/ui/components/assets_tab.rs new file mode 100644 index 0000000000..f8a1edaa7e --- /dev/null +++ b/applications/tari_console_wallet/src/ui/components/assets_tab.rs @@ -0,0 +1,75 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +use crate::ui::components::{Component, styles}; +use tui::backend::Backend; +use tui::Frame; +use tui::layout::{Rect, Constraint}; +use crate::ui::state::AppState; +use tui::widgets::{Table, Block, Row, TableState, Borders}; +use tari_crypto::tari_utilities::hex::Hex; + +pub struct AssetsTab { + table_state: TableState, +} + +impl AssetsTab { + + pub fn new() -> Self { + Self{ table_state: TableState::default(),} + } +} + +impl Component for AssetsTab { + fn draw(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) { + + let assets = app_state.get_owned_assets(); + + + let assets :Vec<_>= assets.iter().map(|r| (r.name().to_string(), r.registration_output_status().to_string(), r.public_key().to_hex(), r.owner_commitment().to_hex())).collect(); + let rows : Vec<_>= assets.iter().map(|v| Row::new(vec![v.0.as_str(), v.1.as_str(), v.2.as_str(), v.3.as_str()])).collect(); + let table = Table::new(rows) + .header(Row::new(vec!["Name", "Status" , "Pub Key", "Owner"]).style(styles::header_row())).block(Block::default().title("Assets").borders(Borders::ALL)).widths(&[Constraint::Length(30), Constraint::Length(20), Constraint::Length(64), Constraint::Length(64)]).highlight_style(styles::highlight()).highlight_symbol(">>"); + f.render_stateful_widget(table, area, &mut self.table_state) + } + + fn on_up(&mut self, _app_state: &mut AppState) { + let index =self.table_state.selected().unwrap_or_default(); + if index == 0 { + self.table_state.select(None); + } else { + self.table_state.select(Some(index - 1)); + } + } + + fn on_down(&mut self, app_state: &mut AppState) { + let index =self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let assets = app_state.get_owned_assets(); + if index > assets.len().saturating_sub(1) { + self.table_state.select(None); + } else { + self.table_state.select(Some(index)); + } + } +} + diff --git a/applications/tari_console_wallet/src/ui/components/base_node.rs b/applications/tari_console_wallet/src/ui/components/base_node.rs index ade233b90c..2fb35f0c9b 100644 --- a/applications/tari_console_wallet/src/ui/components/base_node.rs +++ b/applications/tari_console_wallet/src/ui/components/base_node.rs @@ -80,9 +80,9 @@ impl Component for BaseNode { Span::raw(" "), Span::styled(format!("#{}", tip), Style::default().fg(tip_color)), Span::raw(" "), - Span::styled(sync_text.to_string(), Style::default().fg(Color::DarkGray)), + Span::styled(sync_text.to_string(), Style::default().fg(Color::White)), Span::raw(" "), - Span::styled("Latency", Style::default().fg(Color::DarkGray)), + Span::styled("Latency", Style::default().fg(Color::White)), Span::raw(" "), Span::styled(latency.to_string(), Style::default().fg(latency_color)), Span::styled(" ms", Style::default().fg(Color::DarkGray)), diff --git a/applications/tari_console_wallet/src/ui/components/events_component.rs b/applications/tari_console_wallet/src/ui/components/events_component.rs new file mode 100644 index 0000000000..fc0ffc3962 --- /dev/null +++ b/applications/tari_console_wallet/src/ui/components/events_component.rs @@ -0,0 +1,48 @@ +use tui::backend::Backend; +use crate::ui::components::{Component, styles}; +use tui::Frame; +use tui::layout::{Rect, Constraint}; +use crate::ui::state::AppState; +use tui::widgets::{Row, Table, Block, Borders, TableState}; + +pub struct EventsComponent { + table_state: TableState, +} + + +impl EventsComponent { + pub fn new() -> Self{ + Self { + table_state: TableState::default(), + } + } +} + +impl Component for EventsComponent { + fn draw(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) { + let events = app_state.get_all_events(); + let rows :Vec<_>= events.iter().map(|e| Row::new(vec![e.event_type.as_str(), e.desc.as_str()])).collect(); + let table = Table::new(rows) + .header(Row::new(vec!["Type", "Desc"]).style(styles::header_row())).block(Block::default().title("Events").borders(Borders::ALL)).widths(&[Constraint::Length(20), Constraint::Length(120)]).highlight_style(styles::highlight()).highlight_symbol(">>"); + f.render_stateful_widget(table, area, &mut self.table_state) + } + + fn on_up(&mut self, _app_state: &mut AppState) { + let index =self.table_state.selected().unwrap_or_default(); + if index == 0 { + self.table_state.select(None); + } else { + self.table_state.select(Some(index - 1)); + } + } + + fn on_down(&mut self, app_state: &mut AppState) { + let index =self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let events = app_state.get_all_events(); + if index > events.len() - 1 { + self.table_state.select(None); + } else { + self.table_state.select(Some(index)); + } + } +} diff --git a/applications/tari_console_wallet/src/ui/components/mod.rs b/applications/tari_console_wallet/src/ui/components/mod.rs index d4b18bc637..50123e7700 100644 --- a/applications/tari_console_wallet/src/ui/components/mod.rs +++ b/applications/tari_console_wallet/src/ui/components/mod.rs @@ -31,8 +31,11 @@ pub mod receive_tab; pub mod send_tab; pub mod tabs_container; pub mod transactions_tab; - +pub mod tokens_component; +pub mod assets_tab; +mod styles; pub use self::component::*; +pub mod events_component; #[derive(PartialEq, Eq)] pub enum KeyHandled { diff --git a/applications/tari_console_wallet/src/ui/components/receive_tab.rs b/applications/tari_console_wallet/src/ui/components/receive_tab.rs index 3bb265ae58..1a4667b1d7 100644 --- a/applications/tari_console_wallet/src/ui/components/receive_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/receive_tab.rs @@ -62,7 +62,8 @@ impl ReceiveTab { .constraints([Constraint::Length(1)].as_ref()) .margin(1) .split(info_chunks[1]); - let public_key = Paragraph::new(app_state.get_identity().public_key.as_str()); + // Put a space in front of pub key so it's easy to select + let public_key = Paragraph::new(format!(" {}", app_state.get_identity().public_key)); f.render_widget(public_key, label_layout[0]); // NodeId @@ -86,7 +87,7 @@ impl ReceiveTab { .constraints([Constraint::Length(1)].as_ref()) .margin(1) .split(info_chunks[3]); - let public_address = Paragraph::new(app_state.get_identity().public_address.as_str()); + let public_address = Paragraph::new(format!(" {}", app_state.get_identity().public_address)); f.render_widget(public_address, label_layout[0]); // Emoji ID diff --git a/applications/tari_console_wallet/src/ui/components/styles.rs b/applications/tari_console_wallet/src/ui/components/styles.rs new file mode 100644 index 0000000000..b09c4b0880 --- /dev/null +++ b/applications/tari_console_wallet/src/ui/components/styles.rs @@ -0,0 +1,10 @@ +use tui::style::{Style, Color, Modifier}; + +pub fn header_row() -> Style { + Style::default().fg(Color::Magenta) +} + +pub fn highlight() -> Style { + Style::default().add_modifier(Modifier::BOLD).fg(Color::Magenta) +} + diff --git a/applications/tari_console_wallet/src/ui/components/tokens_component.rs b/applications/tari_console_wallet/src/ui/components/tokens_component.rs new file mode 100644 index 0000000000..3bd9597b67 --- /dev/null +++ b/applications/tari_console_wallet/src/ui/components/tokens_component.rs @@ -0,0 +1,74 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ui::components::{Component, styles}; +use tui::backend::Backend; +use tui::Frame; +use tui::layout::{Rect, Constraint}; +use crate::ui::state::AppState; +use tui::widgets::{Table, Block, Row, TableState, Borders}; +use tari_crypto::tari_utilities::hex::Hex; + +pub struct TokensComponent { + table_state: TableState, +} + +impl TokensComponent { + + pub fn new() -> Self { + Self{ table_state: TableState::default(),} + } +} + +impl Component for TokensComponent { + fn draw(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) { + + let tokens = app_state.get_owned_tokens(); + + + let tokens :Vec<_>= tokens.iter().map(|r| (r.name().to_string(), r.output_status().to_string(), r.asset_public_key().to_hex(), Vec::from(r.unique_id()).to_hex(), r.owner_commitment().to_hex())).collect(); + let rows : Vec<_>= tokens.iter().map(|v| Row::new(vec![v.0.as_str(), v.1.as_str(), v.2.as_str(), v.3.as_str(), v.4.as_str()])).collect(); + let table = Table::new(rows) + .header(Row::new(vec!["Name", "Status" , "Asset Pub Key", "Unique ID", "Owner"]).style(styles::header_row())).block(Block::default().title("Tokens").borders(Borders::ALL)).widths(&[Constraint::Length(30), Constraint::Length(20), Constraint::Length(32), Constraint::Length(32), Constraint::Length(64)]).highlight_style(styles::highlight()).highlight_symbol(">>"); + f.render_stateful_widget(table, area, &mut self.table_state) + } + + fn on_up(&mut self, _app_state: &mut AppState) { + let index =self.table_state.selected().unwrap_or_default(); + if index == 0 { + self.table_state.select(None); + } else { + self.table_state.select(Some(index - 1)); + } + } + + fn on_down(&mut self, app_state: &mut AppState) { + let index =self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let tokens = app_state.get_owned_tokens(); + if index > tokens.len().saturating_sub(1) { + self.table_state.select(None); + } else { + self.table_state.select(Some(index)); + } + } +} + diff --git a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs index b3ca589452..efcc084895 100644 --- a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs @@ -22,6 +22,7 @@ use tui::{ widgets::{Block, Borders, ListItem, Paragraph, Wrap}, Frame, }; +use crate::ui::components::styles; pub struct TransactionsTab { balance: Balance, @@ -131,8 +132,8 @@ impl TransactionsTab { } let column_list = MultiColumnList::new() - .highlight_style(Style::default().add_modifier(Modifier::BOLD).fg(Color::Magenta)) - .heading_style(Style::default().fg(Color::Magenta)) + .highlight_style(styles::highlight()) + .heading_style(styles::header_row()) .max_width(MAX_WIDTH) .add_column(Some("Source/Destination Public Key"), Some(67), column0_items) .add_column(Some("Amount"), Some(18), column1_items) @@ -445,7 +446,7 @@ impl Component for TransactionsTab { span_vec.push(Span::styled("A", Style::default().add_modifier(Modifier::BOLD))); span_vec.push(Span::raw(" shows abandoned coinbase Txs, ")); span_vec.push(Span::styled("Esc", Style::default().add_modifier(Modifier::BOLD))); - span_vec.push(Span::raw(" exits the list.")); + span_vec.push(Span::raw(" exits the list. R: Rebroadcast all in Broadcast")); let instructions = Paragraph::new(Spans::from(span_vec)).wrap(Wrap { trim: true }); f.render_widget(instructions, areas[1]); @@ -531,6 +532,11 @@ impl Component for TransactionsTab { self.confirmation_dialog = true; } }, + // Rebroadcast + 'r' => { + // TODO: use this result + let _res = Handle::current().block_on(app_state.rebroadcast_all()); + } 'a' => app_state.toggle_abandoned_coinbase_filter(), '\n' => match self.selected_tx_list { SelectedTransactionList::None => {}, diff --git a/applications/tari_console_wallet/src/ui/mod.rs b/applications/tari_console_wallet/src/ui/mod.rs index b59238ca34..7dcd1e60d2 100644 --- a/applications/tari_console_wallet/src/ui/mod.rs +++ b/applications/tari_console_wallet/src/ui/mod.rs @@ -60,6 +60,10 @@ pub fn run(app: App>) -> Result<(), ExitCodes> { app.app_state.refresh_contacts_state().await?; trace!(target: LOG_TARGET, "Refreshing connected peers state"); app.app_state.refresh_connected_peers_state().await?; + trace!(target: LOG_TARGET, "Refreshing assets"); + app.app_state.refresh_assets_state().await?; + trace!(target: LOG_TARGET, "Refreshing tokens"); + app.app_state.refresh_tokens_state().await?; trace!(target: LOG_TARGET, "Starting app state event monitor"); app.app_state.start_event_monitor(app.notifier.clone()).await; Result::<_, UiError>::Ok(()) diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index c1374970b9..ea84d8ef24 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -52,7 +52,7 @@ use tari_wallet::{ base_node_service::{handle::BaseNodeEventReceiver, service::BaseNodeState}, connectivity_service::WalletConnectivityHandle, contacts_service::storage::database::Contact, - output_manager_service::{handle::OutputManagerEventReceiver, service::Balance, TxId, TxoValidationType}, + output_manager_service::{handle::OutputManagerEventReceiver, service::Balance, TxoValidationType}, transaction_service::{ handle::TransactionEventReceiver, storage::models::{CompletedTransaction, TransactionStatus}, @@ -74,6 +74,10 @@ use crate::{ utils::db::{CUSTOM_BASE_NODE_ADDRESS_KEY, CUSTOM_BASE_NODE_PUBLIC_KEY_KEY}, wallet_modes::PeerConfig, }; +use tari_wallet::assets::Asset; +use tari_wallet::tokens::Token; +use tari_core::transactions::transaction_protocol::TxId; +use std::collections::VecDeque; const LOG_TARGET: &str = "wallet::console_wallet::app_state"; @@ -117,6 +121,10 @@ impl AppState { tokio::spawn(event_monitor.run(notifier)); } + pub fn get_all_events(&self) -> VecDeque { + self.cached_data.all_events.to_owned() + } + pub async fn refresh_transaction_state(&mut self) -> Result<(), UiError> { let mut inner = self.inner.write().await; inner.refresh_full_transaction_state().await?; @@ -141,6 +149,23 @@ impl AppState { Ok(()) } + pub async fn refresh_assets_state(&mut self) -> Result<(), UiError> { + let mut inner = self.inner.write().await; + inner.refresh_assets_state().await?; + if let Some(data) = inner.get_updated_app_state() { + self.cached_data = data; + } + Ok(()) + } + + pub async fn refresh_tokens_state(&mut self) -> Result<(), UiError> { + let mut inner = self.inner.write().await; + inner.refresh_tokens_state().await?; + if let Some(data) = inner.get_updated_app_state() { + self.cached_data = data; + } + Ok(()) + } pub async fn update_cache(&mut self) { let update = match self.cache_update_cooldown { Some(last_update) => last_update.elapsed() > self.config.cache_update_cooldown, @@ -272,12 +297,28 @@ impl AppState { Ok(()) } + pub async fn rebroadcast_all(&mut self) -> Result<(), UiError> { + let inner = self.inner.write().await; + let mut tx_service = inner.wallet.transaction_service.clone(); + tx_service.restart_broadcast_protocols().await?; + Ok(()) + + } + pub fn get_identity(&self) -> &MyIdentity { &self.cached_data.my_identity } - pub fn get_contacts(&self) -> &Vec { - &self.cached_data.contacts + pub fn get_owned_assets(&self) -> &[Asset] { + self.cached_data.owned_assets.as_slice() + } + + pub fn get_owned_tokens(&self) -> &[Token] { + self.cached_data.owned_tokens.as_slice() + } + + pub fn get_contacts(&self) -> &[UiContact] { + self.cached_data.contacts.as_slice() } pub fn get_contact(&self, index: usize) -> Option<&UiContact> { @@ -460,6 +501,14 @@ impl AppStateInner { } } + pub fn add_event(&mut self, event : EventListItem) { + if self.data.all_events.len() > 30 { + self.data.all_events.pop_back(); + } + self.data.all_events.push_front(event); + self.updated = true; + } + /// If there has been an update to the state since the last call to this function it will provide a cloned snapshot /// of the data for caching, if there has been no change then returns None fn get_updated_app_state(&mut self) -> Option { @@ -641,6 +690,21 @@ impl AppStateInner { Ok(()) } + + pub async fn refresh_assets_state(&mut self) -> Result<(), UiError> { + let asset_utxos = self.wallet.asset_manager.list_owned_assets().await?; + self.data.owned_assets = asset_utxos; + self.updated = true; + Ok(()) + } + + pub async fn refresh_tokens_state(&mut self) -> Result<(), UiError> { + let token_utxos = self.wallet.token_manager.list_owned_tokens().await?; + self.data.owned_tokens = token_utxos; + self.updated = true; + Ok(()) + } + pub async fn refresh_balance(&mut self) -> Result<(), UiError> { let balance = self.wallet.output_manager_service.get_balance().await?; self.data.balance = balance; @@ -841,6 +905,8 @@ impl AppStateInner { #[derive(Clone)] struct AppStateData { + owned_assets: Vec, + owned_tokens: Vec, pending_txs: Vec, completed_txs: Vec, confirmations: HashMap, @@ -853,10 +919,18 @@ struct AppStateData { base_node_previous: Peer, base_node_list: Vec<(String, Peer)>, base_node_peer_custom: Option, + all_events: VecDeque, notifications: Vec<(DateTime, String)>, new_notification_count: u32, } + +#[derive(Clone)] +pub struct EventListItem{ + pub event_type: String, + pub desc: String +} + impl AppStateData { pub fn new( node_identity: &NodeIdentity, @@ -907,6 +981,8 @@ impl AppStateData { } AppStateData { + owned_assets: Vec::new(), + owned_tokens: Vec::new(), pending_txs: Vec::new(), completed_txs: Vec::new(), confirmations: HashMap::new(), @@ -919,6 +995,7 @@ impl AppStateData { base_node_previous, base_node_list, base_node_peer_custom: base_node_config.base_node_custom, + all_events: VecDeque::new(), notifications: Vec::new(), new_notification_count: 0, } diff --git a/applications/tari_console_wallet/src/ui/state/tasks.rs b/applications/tari_console_wallet/src/ui/state/tasks.rs index 85243660e6..0d39599dde 100644 --- a/applications/tari_console_wallet/src/ui/state/tasks.rs +++ b/applications/tari_console_wallet/src/ui/state/tasks.rs @@ -41,7 +41,7 @@ pub async fn send_transaction_task( let mut send_direct_received_result = (false, false); let mut send_saf_received_result = (false, false); match transaction_service_handle - .send_transaction(public_key, amount, fee_per_gram, message) + .send_transaction(public_key, amount, None, fee_per_gram, message) .await { Err(e) => { @@ -114,7 +114,7 @@ pub async fn send_one_sided_transaction_task( let _ = result_tx.send(UiTransactionSendStatus::Initiated); let mut event_stream = transaction_service_handle.get_event_stream(); match transaction_service_handle - .send_one_sided_transaction(public_key, amount, fee_per_gram, message) + .send_one_sided_transaction(public_key, amount, None, fee_per_gram, message) .await { Err(e) => { diff --git a/applications/tari_console_wallet/src/ui/state/wallet_event_monitor.rs b/applications/tari_console_wallet/src/ui/state/wallet_event_monitor.rs index 9c5b724b10..32d4cd06a2 100644 --- a/applications/tari_console_wallet/src/ui/state/wallet_event_monitor.rs +++ b/applications/tari_console_wallet/src/ui/state/wallet_event_monitor.rs @@ -20,13 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{notifier::Notifier, ui::state::AppStateInner}; +use crate::{ + notifier::Notifier, + ui::state::{AppStateInner, EventListItem}, +}; use log::*; use std::sync::Arc; use tari_comms::{connectivity::ConnectivityEvent, peer_manager::Peer}; +use tari_core::transactions::transaction_protocol::TxId; use tari_wallet::{ base_node_service::{handle::BaseNodeEvent, service::BaseNodeState}, - output_manager_service::{handle::OutputManagerEvent, TxId}, + output_manager_service::handle::OutputManagerEvent, transaction_service::handle::TransactionEvent, }; use tokio::sync::{broadcast, RwLock}; @@ -72,6 +76,7 @@ impl WalletEventMonitor { match result { Ok(msg) => { trace!(target: LOG_TARGET, "Wallet Event Monitor received wallet transaction service event {:?}", msg); + self.app_state_inner.write().await.add_event(EventListItem{event_type: "TransactionEvent".to_string(), desc: (&*msg).to_string() }); match (*msg).clone() { TransactionEvent::ReceivedFinalizedTransaction(tx_id) => { self.trigger_tx_state_refresh(tx_id).await; @@ -157,6 +162,7 @@ impl WalletEventMonitor { match result { Ok(msg) => { trace!(target: LOG_TARGET, "Wallet Event Monitor received base node event {:?}", msg); + self.app_state_inner.write().await.add_event(EventListItem{event_type: "BaseNodeEvent".to_string(), desc: (&*msg).to_string() }); match (*msg).clone() { BaseNodeEvent::BaseNodeStateChanged(state) => { self.trigger_base_node_state_refresh(state).await; @@ -176,6 +182,7 @@ impl WalletEventMonitor { match result { Ok(msg) => { trace!(target: LOG_TARGET, "Output Manager Service Callback Handler event {:?}", msg); + self.app_state_inner.write().await.add_event(EventListItem{event_type: "OutputManagerEvent".to_string(), desc: (&*msg).to_string() }); if let OutputManagerEvent::TxoValidationSuccess(_,_) = &*msg { self.trigger_balance_refresh().await; } diff --git a/applications/tari_console_wallet/src/ui/widgets/table.rs b/applications/tari_console_wallet/src/ui/widgets/table.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/applications/tari_dan_node/Cargo.toml b/applications/tari_dan_node/Cargo.toml new file mode 100644 index 0000000000..32ad408437 --- /dev/null +++ b/applications/tari_dan_node/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "tari_dan_node" +authors = ["The Tari Development Community"] +description = "The tari digital assets network node implementation" +repository = "https://github.com/tari-project/tari" +license = "BSD-3-Clause" +version = "0.8.11" +edition = "2018" + +[dependencies] +tari_shutdown = { path = "../../infrastructure/shutdown"} +tari_crypto = "0.11.1" +tari_test_utils = "0.8.1" +tari_app_utilities = {path ="../tari_app_utilities"} +tari_common = {path = "../../common"} +tari_comms = {path = "../../comms"} +tari_service_framework = { path= "../../base_layer/service_framework"} +tari_p2p = {path = "../../base_layer/p2p"} +tari_comms_dht = {path= "../../comms/dht"} +tari_mmr = {path = "../../base_layer/mmr"} + +thiserror = "^1.0.20" +log = { version = "0.4.8", features = ["std"] } +anyhow = "1.0.32" +tokio = { version="0.2.10", features = ["macros", "sync", "time"]} +tonic = "0.5.2" +prost = "0.8" +prost-types = "0.8" +futures = { version = "^0.3.1"} +clap = "2.33.3" +async-trait = "0.1.50" +digest= "0.9.0" + +# saving of patricia tree +patricia_tree = {version = "0.3.0", features =["binary-format"] } +bytecodec = { version ="0.4.14", features = ["json_codec"] } +serde_json = "1.0.64" + +[build-dependencies] +tonic-build = "0.5.2" +tari_common = { path="../../common", features = ["build"]} diff --git a/applications/tari_dan_node/build.rs b/applications/tari_dan_node/build.rs new file mode 100644 index 0000000000..ab5de164b3 --- /dev/null +++ b/applications/tari_dan_node/build.rs @@ -0,0 +1,35 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() -> Result<(), Box> { + tonic_build::configure() + .build_server(true) + .format(false) + .compile(&["proto/dan_node.proto"], &["proto"])?; + + tari_common::build::ProtobufCompiler::new() + .proto_paths(&["src/p2p/proto"]) + .emit_rerun_if_changed_directives() + .compile() + .unwrap(); + Ok(()) +} diff --git a/applications/tari_dan_node/proto/dan_node.proto b/applications/tari_dan_node/proto/dan_node.proto new file mode 100644 index 0000000000..0c52320429 --- /dev/null +++ b/applications/tari_dan_node/proto/dan_node.proto @@ -0,0 +1,51 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +syntax = "proto3"; + +package tari.dan.rpc; + +service DanNode { + rpc GetTokenData(GetTokenDataRequest) returns (GetTokenDataResponse); + rpc ExecuteInstruction(ExecuteInstructionRequest) returns (ExecuteInstructionResponse); +} + +message GetTokenDataRequest { + bytes asset_pub_key = 1; + bytes unique_id = 2; +} + +message GetTokenDataResponse { + +} + +message ExecuteInstructionRequest{ + bytes asset_public_key =1; + string method =2; + repeated bytes args = 3; + bytes from = 4; + bytes signature = 5; + uint64 id = 6; +} + +message ExecuteInstructionResponse { + string status = 1; +} diff --git a/applications/tari_dan_node/src/cmd_args.rs b/applications/tari_dan_node/src/cmd_args.rs new file mode 100644 index 0000000000..da996ccbee --- /dev/null +++ b/applications/tari_dan_node/src/cmd_args.rs @@ -0,0 +1,36 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use clap::App; + +pub fn get_operation_mode() -> OperationMode { + let matches = App::new("Tari DAN node").version("1.0").get_matches(); + OperationMode::Run +} + +pub enum OperationMode { + Run +} + +pub struct DanNodeConfig { + +} diff --git a/applications/tari_dan_node/src/dan_layer/dan_node.rs b/applications/tari_dan_node/src/dan_layer/dan_node.rs new file mode 100644 index 0000000000..0d03f5f21f --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/dan_node.rs @@ -0,0 +1,320 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{Committee, HotStuffMessage, InstructionSet, TemplateId}, + services::{ + infrastructure_services::{TariCommsInboundConnectionService, TariCommsOutboundService}, + ConcreteBftReplicaService, + ConcreteMempoolService, + ConcreteTemplateService, + InstructionSetProcessor, + LoggingEventsPublisher, + MemoryInstructionLog, + MempoolPayloadProvider, + MempoolService, + NodeIdentitySigningService, + }, + storage::FileAssetDataStore, + workers::ConsensusWorker, + }, + digital_assets_error::DigitalAssetError, + ExitCodes, +}; +use anyhow::anyhow; +use log::*; +use std::{ + fs, + sync::{Arc, Mutex}, + time::Duration, +}; +use tari_app_utilities::{ + identity_management, + identity_management::{load_from_json, setup_node_identity}, + utilities, + utilities::convert_socks_authentication, +}; +use tari_common::{CommsTransport, ConfigBootstrap, GlobalConfig, TorControlAuthentication}; +use tari_comms::{ + peer_manager::PeerFeatures, + socks, + tor, + tor::TorIdentity, + transports::SocksConfig, + types::CommsPublicKey, + utils::multiaddr::multiaddr_to_socketaddr, + NodeIdentity, + UnspawnedCommsNode, +}; +use tari_comms_dht::{DbConnectionUrl, Dht, DhtConfig}; +use tari_crypto::tari_utilities::hex::{Hex, HexError}; +use tari_p2p::{ + comms_connector::{pubsub_connector, PubsubDomainConnector, SubscriptionFactory}, + initialization::{spawn_comms_using_transport, CommsConfig, P2pInitializer}, + tari_message::TariMessageType, + transport::{TorConfig, TransportType}, +}; +use tari_service_framework::{ServiceHandles, StackBuilder}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use tokio::{runtime::Handle, task}; + +const LOG_TARGET: &str = "tari::dan::dan_node"; + +pub struct DanNode { + config: GlobalConfig, +} + +impl DanNode { + pub fn new(config: GlobalConfig) -> Self { + Self { config } + } + + pub async fn start( + &self, + create_id: bool, + shutdown: ShutdownSignal, + mempool_service: TMempoolService, + ) -> Result<(), ExitCodes> { + fs::create_dir_all(&self.config.peer_db_path).map_err(|err| ExitCodes::ConfigError(err.to_string()))?; + let node_identity = setup_node_identity( + &self.config.base_node_identity_file, + &self.config.public_address, + create_id, + PeerFeatures::NONE, + )?; + + let (handles, subscription_factory) = self + .build_service_and_comms_stack(shutdown.clone(), node_identity.clone()) + .await?; + + let bft_replica_service = ConcreteBftReplicaService::new(node_identity.as_ref().clone(), vec![]); + + let mempool_payload_provider = MempoolPayloadProvider::new(mempool_service.clone()); + + let mut inbound = TariCommsInboundConnectionService::new(); + let receiver = inbound.take_receiver().unwrap(); + let loopback = inbound.clone_sender(); + let shutdown_2 = shutdown.clone(); + task::spawn(async move { + let topic_subscription = + subscription_factory.get_subscription(TariMessageType::DanConsensusMessage, "HotStufMessages"); + inbound.run(shutdown_2, topic_subscription).await + }); + let dht = handles.expect_handle::(); + let outbound = TariCommsOutboundService::new(dht.outbound_requester(), loopback); + + let dan_config = self + .config + .dan_node + .as_ref() + .ok_or(ExitCodes::ConfigError("Missing dan section".to_string()))?; + + let committee: Vec = dan_config + .committee + .iter() + .map(|s| { + CommsPublicKey::from_hex(s) + .map_err(|e| ExitCodes::ConfigError(format!("could not convert to hex:{}", e))) + }) + .collect::, _>>()?; + + let committee = Committee::new(committee); + let events_publisher = LoggingEventsPublisher::new(); + let signing_service = NodeIdentitySigningService::new(node_identity.as_ref().clone()); + let data_store = FileAssetDataStore::load_or_create(self.config.data_dir.join("asset_data")); + let instruction_log = MemoryInstructionLog::default(); + let template_service = + ConcreteTemplateService::new(data_store, instruction_log, TemplateId::parse(&dan_config.template_id)); + let payload_processor = InstructionSetProcessor::new(template_service, mempool_service); + let mut consensus_worker = ConsensusWorker::new( + bft_replica_service, + receiver, + outbound, + committee, + node_identity.public_key().clone(), + mempool_payload_provider, + events_publisher, + signing_service, + payload_processor, + Duration::from_secs(dan_config.phase_timeout), + ); + consensus_worker + .run(shutdown.clone(), None) + .await + .map_err(|err| ExitCodes::ConfigError(err.to_string())) + } + + async fn build_service_and_comms_stack( + &self, + shutdown: ShutdownSignal, + node_identity: Arc, + ) -> Result<(ServiceHandles, SubscriptionFactory), ExitCodes> { + // this code is duplicated from the base node + let comms_config = self.create_comms_config(node_identity.clone()); + + let (publisher, peer_message_subscriptions) = pubsub_connector(100, self.config.buffer_rate_limit_base_node); + let mut handles = StackBuilder::new(shutdown.clone()) + .add_initializer(P2pInitializer::new(comms_config, publisher)) + .build() + .await + .map_err(|err| ExitCodes::ConfigError(err.to_string()))?; + + let comms = handles + .take_handle::() + .expect("P2pInitializer was not added to the stack or did not add UnspawnedCommsNode"); + let comms = spawn_comms_using_transport(comms, self.create_transport_type()) + .await + .map_err(|e| ExitCodes::ConfigError(format!("Could not spawn using transport:{}", e)))?; + + // Save final node identity after comms has initialized. This is required because the public_address can be + // changed by comms during initialization when using tor. + identity_management::save_as_json(&self.config.base_node_identity_file, &*comms.node_identity()) + .map_err(|e| ExitCodes::ConfigError(format!("Failed to save node identity: {}", e)))?; + if let Some(hs) = comms.hidden_service() { + identity_management::save_as_json(&self.config.base_node_tor_identity_file, hs.tor_identity()) + .map_err(|e| ExitCodes::ConfigError(format!("Failed to save tor identity: {}", e)))?; + } + + handles.register(comms); + Ok((handles, peer_message_subscriptions)) + } + + fn create_comms_config(&self, node_identity: Arc) -> CommsConfig { + CommsConfig { + network: self.config.network, + node_identity: node_identity.clone(), + transport_type: self.create_transport_type(), + datastore_path: self.config.peer_db_path.clone(), + peer_database_name: "peers".to_string(), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: DhtConfig { + database_url: DbConnectionUrl::File(self.config.data_dir.join("dht.db")), + auto_join: true, + allow_test_addresses: self.config.allow_test_addresses, + flood_ban_max_msg_count: self.config.flood_ban_max_msg_count, + saf_msg_validity: self.config.saf_expiry_duration, + ..Default::default() + }, + allow_test_addresses: self.config.allow_test_addresses, + listener_liveness_allowlist_cidrs: self.config.listener_liveness_allowlist_cidrs.clone(), + listener_liveness_max_sessions: self.config.listnener_liveness_max_sessions, + user_agent: format!("tari/dannode/{}", env!("CARGO_PKG_VERSION")), + // Also add sync peers to the peer seed list. Duplicates are acceptable. + peer_seeds: self + .config + .peer_seeds + .iter() + .cloned() + .chain(self.config.force_sync_peers.clone()) + .collect(), + dns_seeds: self.config.dns_seeds.clone(), + dns_seeds_name_server: self.config.dns_seeds_name_server, + dns_seeds_use_dnssec: self.config.dns_seeds_use_dnssec, + auxilary_tcp_listener_address: self.config.auxilary_tcp_listener_address.clone(), + } + } + + /// Creates a transport type from the given configuration + /// + /// ## Paramters + /// `config` - The reference to the configuration in which to set up the comms stack, see [GlobalConfig] + /// + /// ##Returns + /// TransportType based on the configuration + fn create_transport_type(&self) -> TransportType { + debug!( + target: LOG_TARGET, + "Transport is set to '{:?}'", self.config.comms_transport + ); + match self.config.comms_transport.clone() { + CommsTransport::Tcp { + listener_address, + tor_socks_address, + tor_socks_auth, + } => TransportType::Tcp { + listener_address, + tor_socks_config: tor_socks_address.map(|proxy_address| SocksConfig { + proxy_address, + authentication: tor_socks_auth.map(convert_socks_authentication).unwrap_or_default(), + proxy_bypass_addresses: vec![], + }), + }, + CommsTransport::TorHiddenService { + control_server_address, + socks_address_override, + forward_address, + auth, + onion_port, + tor_proxy_bypass_addresses, + } => { + let identity = Some(&self.config.base_node_tor_identity_file) + .filter(|p| p.exists()) + .and_then(|p| { + // If this fails, we can just use another address + load_from_json::<_, TorIdentity>(p).ok() + }); + info!( + target: LOG_TARGET, + "Tor identity at path '{}' {:?}", + self.config.base_node_tor_identity_file.to_string_lossy(), + identity + .as_ref() + .map(|ident| format!("loaded for address '{}.onion'", ident.service_id)) + .or_else(|| Some("not found".to_string())) + .unwrap() + ); + + let forward_addr = multiaddr_to_socketaddr(&forward_address).expect("Invalid tor forward address"); + TransportType::Tor(TorConfig { + control_server_addr: control_server_address, + control_server_auth: { + match auth { + TorControlAuthentication::None => tor::Authentication::None, + TorControlAuthentication::Password(password) => { + tor::Authentication::HashedPassword(password) + }, + } + }, + identity: identity.map(Box::new), + port_mapping: (onion_port, forward_addr).into(), + socks_address_override, + socks_auth: socks::Authentication::None, + tor_proxy_bypass_addresses, + }) + }, + CommsTransport::Socks5 { + proxy_address, + listener_address, + auth, + } => TransportType::Socks { + socks_config: SocksConfig { + proxy_address, + authentication: convert_socks_authentication(auth), + proxy_bypass_addresses: vec![], + }, + listener_address, + }, + } + } +} diff --git a/applications/tari_dan_node/src/dan_layer/mod.rs b/applications/tari_dan_node/src/dan_layer/mod.rs new file mode 100644 index 0000000000..fd15be8183 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/mod.rs @@ -0,0 +1,30 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod template_command; +pub mod templates; + +pub mod dan_node; +pub mod models; +pub mod services; +pub mod storage; +pub mod workers; diff --git a/applications/tari_dan_node/src/dan_layer/models/block.rs b/applications/tari_dan_node/src/dan_layer/models/block.rs new file mode 100644 index 0000000000..cbbb693cf8 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/block.rs @@ -0,0 +1,33 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::Instruction; + +pub struct Block { + instructions: Vec, +} + +impl Block { + pub fn new() -> Self { + Self { instructions: vec![] } + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/committee.rs b/applications/tari_dan_node/src/dan_layer/models/committee.rs new file mode 100644 index 0000000000..d8f2a38057 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/committee.rs @@ -0,0 +1,59 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::{models::ViewId, services::infrastructure_services::NodeAddressable}; + +#[derive(Clone)] +pub struct Committee { + // TODO: encapsulate + pub members: Vec, +} + +impl Committee { + pub fn new(members: Vec) -> Self { + Self { members } + } + + pub fn leader_for_view(&self, view_id: ViewId) -> &TAddr { + let pos = view_id.current_leader(self.members.len()); + &self.members[pos] + } + + pub fn consensus_threshold(&self) -> usize { + let len = self.members.len(); + let max_failures = (len - 1) / 3; + len - max_failures + } + + pub fn len(&self) -> usize { + self.members.len() + } +} + +impl IntoIterator for Committee { + type IntoIter = std::vec::IntoIter; + type Item = TAddr; + + fn into_iter(self) -> Self::IntoIter { + self.members.into_iter() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs b/applications/tari_dan_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs new file mode 100644 index 0000000000..977fb69dab --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs @@ -0,0 +1,44 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{ConsensusWorkerState, Event}; +use std::{fmt, fmt::Formatter}; + +#[derive(Debug, Clone, PartialEq)] +pub enum ConsensusWorkerDomainEvent { + StateChanged { + old: ConsensusWorkerState, + new: ConsensusWorkerState, + }, +} + +impl Event for ConsensusWorkerDomainEvent {} + +impl fmt::Display for ConsensusWorkerDomainEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ConsensusWorkerDomainEvent::StateChanged { old, new } => { + write!(f, "State changed from {:?} to {:?}", old, new) + }, + } + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/domain_events/mod.rs b/applications/tari_dan_node/src/dan_layer/models/domain_events/mod.rs new file mode 100644 index 0000000000..dc47246b27 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/domain_events/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod consensus_worker_domain_event; + +pub use consensus_worker_domain_event::ConsensusWorkerDomainEvent; diff --git a/applications/tari_dan_node/src/dan_layer/models/hot_stuff_message.rs b/applications/tari_dan_node/src/dan_layer/models/hot_stuff_message.rs new file mode 100644 index 0000000000..61ce610120 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/hot_stuff_message.rs @@ -0,0 +1,158 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{HotStuffMessageType, HotStuffTreeNode, Payload, QuorumCertificate, Signature, ViewId}; +use digest::Digest; +use std::hash::Hash; +use tari_crypto::common::Blake256; + +#[derive(Debug, Clone)] +pub struct HotStuffMessage { + view_number: ViewId, + message_type: HotStuffMessageType, + justify: Option>, + node: Option>, + partial_sig: Option, +} + +impl HotStuffMessage { + pub fn new( + view_number: ViewId, + message_type: HotStuffMessageType, + justify: Option>, + node: Option>, + partial_sig: Option, + ) -> Self { + HotStuffMessage { + view_number, + message_type, + justify, + node, + partial_sig, + } + } + + pub fn new_view(prepare_qc: QuorumCertificate, view_number: ViewId) -> Self { + Self { + message_type: HotStuffMessageType::NewView, + view_number, + justify: Some(prepare_qc), + node: None, + partial_sig: None, + } + } + + pub fn prepare( + proposal: HotStuffTreeNode, + high_qc: Option>, + view_number: ViewId, + ) -> Self { + Self { + message_type: HotStuffMessageType::Prepare, + node: Some(proposal), + justify: high_qc, + view_number, + partial_sig: None, + } + } + + pub fn pre_commit( + node: Option>, + prepare_qc: Option>, + view_number: ViewId, + ) -> Self { + Self { + message_type: HotStuffMessageType::PreCommit, + node, + justify: prepare_qc, + view_number, + partial_sig: None, + } + } + + pub fn commit( + node: Option>, + pre_commit_qc: Option>, + view_number: ViewId, + ) -> Self { + Self { + message_type: HotStuffMessageType::Commit, + node, + justify: pre_commit_qc, + view_number, + partial_sig: None, + } + } + + pub fn decide( + node: Option>, + commit_qc: Option>, + view_number: ViewId, + ) -> Self { + Self { + message_type: HotStuffMessageType::Commit, + node, + justify: commit_qc, + view_number, + partial_sig: None, + } + } + + pub fn create_signature_challenge(&self) -> Vec { + let mut b = Blake256::new() + .chain(&[self.message_type.as_u8()]) + .chain(self.view_number.as_u64().to_le_bytes()); + if let Some(ref node) = self.node { + b = b.chain(node.calculate_hash().as_bytes()); + } + b.finalize().to_vec() + } + + pub fn view_number(&self) -> ViewId { + self.view_number + } + + pub fn node(&self) -> Option<&HotStuffTreeNode> { + self.node.as_ref() + } + + pub fn message_type(&self) -> &HotStuffMessageType { + &self.message_type + } + + pub fn justify(&self) -> Option<&QuorumCertificate> { + self.justify.as_ref() + } + + pub fn matches(&self, message_type: HotStuffMessageType, view_id: ViewId) -> bool { + // from hotstuf spec + self.message_type() == &message_type && view_id == self.view_number() + } + + pub fn add_partial_sig(&mut self, signature: Signature) { + self.partial_sig = Some(signature) + } + + pub fn partial_sig(&self) -> Option<&Signature> { + self.partial_sig.as_ref() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/hot_stuff_tree_node.rs b/applications/tari_dan_node/src/dan_layer/models/hot_stuff_tree_node.rs new file mode 100644 index 0000000000..f3b6cfc262 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/hot_stuff_tree_node.rs @@ -0,0 +1,78 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{Payload, TreeNodeHash}; +use digest::Digest; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, +}; +use tari_crypto::common::Blake256; + +#[derive(Debug, Clone, Hash, PartialEq)] +pub struct HotStuffTreeNode { + parent: TreeNodeHash, + payload: TPayload, + hash: TreeNodeHash, +} + +impl HotStuffTreeNode { + pub fn new(parent: TreeNodeHash, payload: TPayload) -> Self { + let mut s = HotStuffTreeNode { + parent, + payload, + hash: TreeNodeHash(vec![]), + }; + s.hash = s.calculate_hash(); + s + } + + pub fn genesis(payload: TPayload) -> HotStuffTreeNode { + let mut s = Self { + parent: TreeNodeHash(vec![0u8; 32]), + payload, + hash: TreeNodeHash(vec![]), + }; + s.hash = s.calculate_hash(); + s + } + + pub fn from_parent(parent: &HotStuffTreeNode, payload: TPayload) -> HotStuffTreeNode { + Self::new(parent.calculate_hash(), payload) + } + + pub fn calculate_hash(&self) -> TreeNodeHash { + let result = Blake256::new() + .chain(self.parent.0.as_slice()) + .chain(self.payload.as_ref()) + .finalize(); + TreeNodeHash(result.to_vec()) + } + + pub fn parent(&self) -> &TreeNodeHash { + &self.parent + } + + pub fn payload(&self) -> &TPayload { + &self.payload + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/instruction.rs b/applications/tari_dan_node/src/dan_layer/models/instruction.rs new file mode 100644 index 0000000000..a115b28edd --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/instruction.rs @@ -0,0 +1,98 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::models::TokenId, + types::{com_sig_to_bytes, ComSig, PublicKey}, +}; +use digest::Digest; +use tari_crypto::{common::Blake256, tari_utilities::ByteArray}; + +#[derive(Clone, Debug, Hash)] +pub struct Instruction { + asset_id: PublicKey, + method: String, + args: Vec>, + from: TokenId, + signature: ComSig, + hash: Vec, +} + +impl PartialEq for Instruction { + fn eq(&self, other: &Self) -> bool { + self.hash.eq(&other.hash) + } +} + +impl Instruction { + pub fn new(asset_id: PublicKey, method: String, args: Vec>, from: TokenId, signature: ComSig) -> Self { + let mut s = Self { + asset_id, + method, + args, + from, + // TODO: this is obviously wrong + signature: ComSig::default(), + hash: vec![], + }; + s.hash = s.calculate_hash(); + s + } + + pub fn asset_id(&self) -> &PublicKey { + &self.asset_id + } + + pub fn method(&self) -> &str { + &self.method + } + + pub fn args(&self) -> &[Vec] { + &self.args + } + + // TODO: rename to avoid use of from + pub fn from_owner(&self) -> &TokenId { + &self.from + } + + pub fn signature(&self) -> &ComSig { + &self.signature + } + + pub fn hash(&self) -> &[u8] { + &self.hash + } + + pub fn calculate_hash(&self) -> Vec { + let mut b = Blake256::new() + .chain(self.asset_id.as_bytes()) + .chain(self.method.as_bytes()); + for a in &self.args { + b = b.chain(a); + } + b.chain(self.from.as_bytes()) + .chain(com_sig_to_bytes(&self.signature)) + .finalize() + .to_vec() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs b/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs new file mode 100644 index 0000000000..3b6d748757 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs @@ -0,0 +1,87 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{Instruction, Payload}; +use std::hash::{Hash, Hasher}; +use tari_crypto::common::Blake256; +use tari_mmr::MerkleMountainRange; + +#[derive(PartialEq, Clone, Debug, Hash)] +pub struct InstructionSetHash(Vec); + +impl InstructionSetHash { + pub fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } +} + +// TODO: Implement hash properly +#[derive(Clone, Debug, Hash)] +pub struct InstructionSet { + hash: InstructionSetHash, + instructions: Vec, +} + +impl InstructionSet { + pub fn empty() -> Self { + Self::from_slice(&vec![]) + } + + pub fn from_slice(instructions: &[Instruction]) -> Self { + let ins = Vec::from(instructions); + let mut result = Self { + instructions: ins, + hash: InstructionSetHash(vec![]), + }; + result.hash = result.calculate_hash(); + result + } + + pub fn calculate_hash(&self) -> InstructionSetHash { + let mut mmr = MerkleMountainRange::::new(Vec::default()); + // assume instructions are sorted + for instruction in &self.instructions { + mmr.push(instruction.calculate_hash()); + } + + InstructionSetHash(mmr.get_merkle_root().unwrap()) + } + + pub fn instructions(&self) -> &[Instruction] { + self.instructions.as_slice() + } +} + +impl Payload for InstructionSet {} + +// TODO: Not really the correct trait, it should be AsHash +impl AsRef<[u8]> for InstructionSet { + fn as_ref(&self) -> &[u8] { + self.hash.as_bytes() + } +} + +impl PartialEq for InstructionSet { + fn eq(&self, other: &Self) -> bool { + self.hash.eq(&other.hash) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/mod.rs b/applications/tari_dan_node/src/dan_layer/models/mod.rs new file mode 100644 index 0000000000..d708955cbf --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/mod.rs @@ -0,0 +1,173 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cmp::Ordering; + +mod block; +mod committee; +pub mod domain_events; +mod hot_stuff_message; +mod hot_stuff_tree_node; +mod instruction; +mod instruction_set; +mod quorum_certificate; +mod replica_info; +mod view; +mod view_id; + +pub use block::Block; +pub use committee::Committee; +pub use hot_stuff_message::HotStuffMessage; +pub use hot_stuff_tree_node::HotStuffTreeNode; +pub use instruction::Instruction; +pub use instruction_set::InstructionSet; +pub use quorum_certificate::QuorumCertificate; +pub use replica_info::ReplicaInfo; +use std::{ + convert::TryFrom, + fmt, + fmt::{Debug, Formatter}, + hash::Hash, +}; +pub use view::View; +pub use view_id::ViewId; + +// TODO: encapsulate +pub struct InstructionId(pub u64); + +// TODO: encapsulate +pub struct InstructionCaller { + pub owner_token_id: TokenId, +} + +impl InstructionCaller { + pub fn owner_token_id(&self) -> &TokenId { + &self.owner_token_id + } +} + +#[derive(Copy, Clone)] +pub enum TemplateId { + EditableMetadata, +} + +impl TemplateId { + pub fn parse(s: &str) -> TemplateId { + match s { + "EditableMetadata" => TemplateId::EditableMetadata, + _ => { + // TODO: Propagate errr instead + dbg!("Unrecognised template"); + TemplateId::EditableMetadata + }, + } + } +} + +#[derive(Clone, Debug, Hash)] +pub struct TokenId(pub Vec); + +impl TokenId { + pub fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl AsRef<[u8]> for TokenId { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum HotStuffMessageType { + NewView, + Prepare, + PreCommit, + Commit, + // Special type + Genesis, +} + +impl HotStuffMessageType { + pub fn as_u8(&self) -> u8 { + match self { + HotStuffMessageType::NewView => 1, + HotStuffMessageType::Prepare => 2, + HotStuffMessageType::PreCommit => 3, + HotStuffMessageType::Commit => 4, + HotStuffMessageType::Genesis => 255, + } + } +} + +impl TryFrom for HotStuffMessageType { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(HotStuffMessageType::NewView), + 2 => Ok(HotStuffMessageType::Prepare), + 3 => Ok(HotStuffMessageType::PreCommit), + 4 => Ok(HotStuffMessageType::Commit), + 255 => Ok(HotStuffMessageType::Genesis), + _ => Err("Not a value message type".to_string()), + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq)] +pub struct TreeNodeHash(pub Vec); + +impl TreeNodeHash { + pub(crate) fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } +} + +// TODO: Perhaps should be CoW instead of Clone +pub trait Payload: Hash + Debug + Clone + AsRef<[u8]> + Send + Sync + PartialEq {} + +impl Payload for &str {} + +impl Payload for String {} + +pub trait Event: Clone + Send + Sync {} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ConsensusWorkerState { + Starting, + Prepare, + PreCommit, + Commit, + Decide, + NextView, +} + +#[derive(Clone, Debug)] +pub struct Signature {} + +impl Signature { + pub fn combine(&self, other: &Signature) -> Signature { + other.clone() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/quorum_certificate.rs b/applications/tari_dan_node/src/dan_layer/models/quorum_certificate.rs new file mode 100644 index 0000000000..fc4a1a07ec --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/quorum_certificate.rs @@ -0,0 +1,84 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{HotStuffMessageType, HotStuffTreeNode, Payload, Signature, ViewId}; + +#[derive(Debug, Clone)] +pub struct QuorumCertificate { + message_type: HotStuffMessageType, + node: HotStuffTreeNode, + view_number: ViewId, + signature: Option, +} + +impl QuorumCertificate { + pub fn new( + message_type: HotStuffMessageType, + view_number: ViewId, + node: HotStuffTreeNode, + signature: Option, + ) -> Self { + Self { + message_type, + node, + view_number, + signature, + } + } + + pub fn genesis(payload: TPayload) -> Self { + Self { + message_type: HotStuffMessageType::Genesis, + node: HotStuffTreeNode::genesis(payload), + view_number: 0.into(), + signature: None, + } + } + + pub fn node(&self) -> &HotStuffTreeNode { + &self.node + } + + pub fn view_number(&self) -> ViewId { + self.view_number + } + + pub fn message_type(&self) -> HotStuffMessageType { + self.message_type + } + + pub fn signature(&self) -> Option<&Signature> { + self.signature.as_ref() + } + + pub fn combine_sig(&mut self, partial_sig: &Signature) { + self.signature = match &self.signature { + None => Some(partial_sig.clone()), + Some(s) => Some(s.combine(partial_sig)), + }; + } + + pub fn matches(&self, message_type: HotStuffMessageType, view_id: ViewId) -> bool { + // from hotstuf spec + self.message_type() == message_type && view_id == self.view_number() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/replica_info.rs b/applications/tari_dan_node/src/dan_layer/models/replica_info.rs new file mode 100644 index 0000000000..e9aa54aa5c --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/replica_info.rs @@ -0,0 +1,25 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub struct ReplicaInfo { + +} diff --git a/applications/tari_dan_node/src/dan_layer/models/view.rs b/applications/tari_dan_node/src/dan_layer/models/view.rs new file mode 100644 index 0000000000..f5a8e96427 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/view.rs @@ -0,0 +1,36 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::ViewId; + +// TODO: Encapsulate +#[derive(Clone)] +pub struct View { + pub view_id: ViewId, + pub is_leader: bool, +} + +impl View { + pub fn is_leader(&self) -> bool { + self.is_leader + } +} diff --git a/applications/tari_dan_node/src/dan_layer/models/view_id.rs b/applications/tari_dan_node/src/dan_layer/models/view_id.rs new file mode 100644 index 0000000000..7ed8297ef7 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/models/view_id.rs @@ -0,0 +1,52 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cmp::Ordering; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ViewId(pub u64); + +impl ViewId { + pub fn current_leader(&self, committee_size: usize) -> usize { + (self.0 % committee_size as u64) as usize + } + + pub fn next(&self) -> ViewId { + ViewId(self.0 + 1) + } + + pub fn as_u64(&self) -> u64 { + self.0 + } +} + +impl PartialOrd for ViewId { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl From for ViewId { + fn from(v: u64) -> Self { + Self(v) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs b/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs new file mode 100644 index 0000000000..0b2d27d592 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs @@ -0,0 +1,61 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{ViewId, View}; +use tari_comms::NodeIdentity; +use tari_comms::peer_manager::NodeId; + +pub trait BftReplicaService { + fn current_view(&self ) -> View; +} + +pub struct ConcreteBftReplicaService { + current_view: ViewId, + node_identity: NodeIdentity, + committee: Vec, + position_in_committee: usize +} + +impl ConcreteBftReplicaService { + pub fn new(node_identity: NodeIdentity, committee: Vec) -> Self { + let mut committee = committee; + if !committee.contains(node_identity.node_id()) { + committee.push(node_identity.node_id().clone()); + } + + committee.sort(); + let position_in_committee = committee.iter().position(|n| n == node_identity.node_id()).expect("NodeID should always be present since we add it"); + Self { current_view: ViewId(0), node_identity, committee, position_in_committee} + } + + +} + +impl BftReplicaService for ConcreteBftReplicaService{ + fn current_view(&self) -> View { + View { + view_id: self.current_view, + is_leader: self.current_view.current_leader(self.committee.len()) == self.position_in_committee + } + } +} + diff --git a/applications/tari_dan_node/src/dan_layer/services/events_publisher.rs b/applications/tari_dan_node/src/dan_layer/services/events_publisher.rs new file mode 100644 index 0000000000..3d17826d30 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/events_publisher.rs @@ -0,0 +1,47 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::Event; +use std::{ + fmt::{Debug, Display}, + marker::PhantomData, +}; + +pub trait EventsPublisher { + fn publish(&mut self, event: TEvent); +} + +pub struct LoggingEventsPublisher { + // TODO: remove + phantom: PhantomData, +} + +impl LoggingEventsPublisher { + pub fn new() -> Self { + Self { phantom: PhantomData } + } +} +impl EventsPublisher for LoggingEventsPublisher { + fn publish(&mut self, event: TEvent) { + println!("[Event] Event received:{}", event); + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs new file mode 100644 index 0000000000..ddde53dadf --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs @@ -0,0 +1,126 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::{HotStuffMessage, InstructionSet, Payload, ViewId}; + +use crate::{ + dan_layer::services::infrastructure_services::NodeAddressable, + digital_assets_error::DigitalAssetError, + p2p::dan_p2p, +}; +use async_trait::async_trait; +use futures::{self, pin_mut, Stream, StreamExt}; +use std::{convert::TryInto, marker::PhantomData, sync::Arc}; +use tari_comms::types::CommsPublicKey; +use tari_p2p::{comms_connector::PeerMessage, domain_message::DomainMessage}; +use tari_shutdown::ShutdownSignal; +use tokio::sync::mpsc::{channel, Receiver, Sender}; + +#[async_trait] +pub trait InboundConnectionService { + async fn receive_message(&mut self) -> (TAddr, HotStuffMessage); +} + +pub struct TariCommsInboundConnectionService { + // TODO: remove + receiver: Option>, + sender: Sender<(CommsPublicKey, HotStuffMessage)>, +} + +impl TariCommsInboundConnectionService { + pub fn new() -> Self { + let (receiver, sender) = TariCommsInboundReceiver::new(); + Self { + receiver: Some(receiver), + sender, + } + } + + pub fn clone_sender(&self) -> Sender<(CommsPublicKey, HotStuffMessage)> { + self.sender.clone() + } + + pub fn take_receiver(&mut self) -> Option> { + // Takes the receiver, can only be done once + if let Some(receiver) = self.receiver.take() { + Some(receiver) + } else { + None + } + } + + pub async fn run( + &mut self, + shutdown_signal: ShutdownSignal, + inbound_stream: impl Stream>, + ) -> Result<(), DigitalAssetError> { + let inbound_stream = inbound_stream.fuse(); + pin_mut!(inbound_stream); + loop { + futures::select! { + message = inbound_stream.select_next_some() => { + self.forward_message(message).await?; + } + complete => { + dbg!("Tari inbound connector shutting down"); + return Ok(()); + } + // _ = shutdown_signal => { + // dbg!("Shutdown received"); + // return Ok(()) + // } + } + } + } + + async fn forward_message(&mut self, message: Arc) -> Result<(), DigitalAssetError> { + // let from = message.authenticated_origin.as_ref().unwrap().clone(); + let from = message.source_peer.public_key.clone(); + // TODO: Convert hotstuff + let proto_message: dan_p2p::HotStuffMessage = message.decode_message().unwrap(); + let hot_stuff_message = proto_message + .try_into() + .map_err(|s| DigitalAssetError::ConversionError(s))?; + self.sender.send((from, hot_stuff_message)).await.unwrap(); + Ok(()) + } +} + +// TODO: Perhaps this is a hack, and should be moved to a better structure. This struct exists to create a Sync+ Send +// inbound service that can be given to the consensus worker +pub struct TariCommsInboundReceiver { + receiver: Receiver<(CommsPublicKey, HotStuffMessage)>, +} + +impl TariCommsInboundReceiver { + fn new() -> (Self, Sender<(CommsPublicKey, HotStuffMessage)>) { + let (sender, receiver) = channel(1000); + (Self { receiver }, sender) + } +} +#[async_trait] +impl InboundConnectionService for TariCommsInboundReceiver { + async fn receive_message(&mut self) -> (CommsPublicKey, HotStuffMessage) { + // TODO: handle errors + self.receiver.recv().await.unwrap() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs new file mode 100644 index 0000000000..a1a0528a7b --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs @@ -0,0 +1,145 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{Committee, HotStuffMessage}, + services::infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, + }, + digital_assets_error::DigitalAssetError, +}; +use async_trait::async_trait; +use std::collections::{HashMap, VecDeque}; +use tokio::sync::mpsc::{channel, Receiver, Sender}; + +pub fn mock_inbound() -> MockInboundConnectionService { + MockInboundConnectionService::new() +} + +#[derive()] +pub struct MockInboundConnectionService { + messages: ( + Sender<(TAddr, HotStuffMessage)>, + Receiver<(TAddr, HotStuffMessage)>, + ), +} + +#[async_trait] +impl InboundConnectionService + for MockInboundConnectionService +{ + async fn receive_message(&mut self) -> (TAddr, HotStuffMessage) { + self.messages.1.recv().await.unwrap() + } +} + +impl MockInboundConnectionService { + pub fn new() -> Self { + Self { messages: channel(10) } + } + + pub fn push(&mut self, from: TAddr, message: HotStuffMessage) { + self.messages.0.try_send((from, message)).unwrap() + } + + pub fn create_sender(&self) -> Sender<(TAddr, HotStuffMessage)> { + self.messages.0.clone() + } +} + +pub fn mock_outbound( + committee: Vec, +) -> MockOutboundService { + MockOutboundService::new(committee) +} + +pub struct MockOutboundService { + inbound_senders: HashMap)>>, + inbounds: HashMap>, +} + +impl Clone for MockOutboundService { + fn clone(&self) -> Self { + MockOutboundService { + inbound_senders: self.inbound_senders.clone(), + inbounds: HashMap::new(), + } + } +} +impl MockOutboundService { + pub fn new(committee: Vec) -> Self { + let mut inbounds = HashMap::new(); + let mut inbound_senders = HashMap::new(); + for member in committee { + let inbound = mock_inbound(); + inbound_senders.insert(member.clone(), inbound.messages.0.clone()); + inbounds.insert(member.clone(), inbound); + } + Self { + inbounds, + inbound_senders, + } + } + + pub fn take_inbound(&mut self, member: &TAddr) -> Option> { + self.inbounds.remove(member) + } +} + +use crate::dan_layer::models::Payload; +use std::{fmt::Debug, hash::Hash}; + +#[async_trait] +impl OutboundService + for MockOutboundService +{ + async fn send( + &mut self, + from: TAddr, + to: TAddr, + message: HotStuffMessage, + ) -> Result<(), DigitalAssetError> { + let t = &to; + println!( + "[mock] Sending message: {:?} {:?} sig:{:?}", + &to, + &message.message_type(), + &message.partial_sig() + ); + // intentionally swallow error here because the other end can die in tests + let _ = self.inbound_senders.get_mut(t).unwrap().send((from, message)).await; + Ok(()) + } + + async fn broadcast( + &mut self, + from: TAddr, + _committee: &[TAddr], + message: HotStuffMessage, + ) -> Result<(), DigitalAssetError> { + let receivers: Vec = self.inbound_senders.keys().map(|k| k.clone()).collect(); + for receiver in receivers { + self.send(from.clone(), receiver.clone(), message.clone()).await? + } + Ok(()) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mod.rs b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mod.rs new file mode 100644 index 0000000000..9accfbc2fb --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod inbound_connection_service; +mod node_addressable; +mod outbound_service; + +pub use inbound_connection_service::{InboundConnectionService, TariCommsInboundConnectionService}; +pub use node_addressable::NodeAddressable; +pub use outbound_service::{OutboundService, TariCommsOutboundService}; +use std::{fmt::Debug, hash::Hash}; + +#[cfg(test)] +pub mod mocks; diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/node_addressable.rs b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/node_addressable.rs new file mode 100644 index 0000000000..476609256c --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/node_addressable.rs @@ -0,0 +1,32 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{fmt::Debug, hash::Hash}; +use tari_comms::types::CommsPublicKey; + +pub trait NodeAddressable: Eq + Hash + Clone + Debug + Send + Sync {} + +impl NodeAddressable for String {} + +impl NodeAddressable for &str {} + +impl NodeAddressable for CommsPublicKey {} diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs new file mode 100644 index 0000000000..98b6e7f6af --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs @@ -0,0 +1,112 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{HotStuffMessage, InstructionSet, Payload}, + services::infrastructure_services::{InboundConnectionService, NodeAddressable}, + }, + digital_assets_error::DigitalAssetError, + p2p, +}; +use async_trait::async_trait; +use std::marker::PhantomData; +use tari_comms::types::CommsPublicKey; +use tari_comms_dht::{domain_message::OutboundDomainMessage, outbound::OutboundMessageRequester}; +use tari_p2p::tari_message::TariMessageType; +use tokio::sync::mpsc::Sender; + +#[async_trait] +pub trait OutboundService { + async fn send( + &mut self, + from: TAddr, + to: TAddr, + message: HotStuffMessage, + ) -> Result<(), DigitalAssetError>; + + async fn broadcast( + &mut self, + from: TAddr, + committee: &[TAddr], + message: HotStuffMessage, + ) -> Result<(), DigitalAssetError>; +} + +pub struct TariCommsOutboundService { + outbound_message_requester: OutboundMessageRequester, + loopback_service: Sender<(CommsPublicKey, HotStuffMessage)>, + // TODO: Remove + phantom: PhantomData, +} + +impl TariCommsOutboundService { + pub fn new( + outbound_message_requester: OutboundMessageRequester, + loopback_service: Sender<(CommsPublicKey, HotStuffMessage)>, + ) -> Self { + Self { + outbound_message_requester, + loopback_service, + phantom: PhantomData, + } + } +} + +#[async_trait] +impl OutboundService for TariCommsOutboundService { + async fn send( + &mut self, + from: CommsPublicKey, + to: CommsPublicKey, + message: HotStuffMessage, + ) -> Result<(), DigitalAssetError> { + // Tari comms does allow sending to itself + if from == to { + self.loopback_service.send((from, message)).await.unwrap(); + return Ok(()); + } + + let inner: p2p::dan_p2p::HotStuffMessage = (&message).into(); + let tari_message = OutboundDomainMessage::new(TariMessageType::DanConsensusMessage, inner); + + self.outbound_message_requester + .send_direct(to, tari_message) + .await + .unwrap(); + Ok(()) + } + + async fn broadcast( + &mut self, + from: CommsPublicKey, + committee: &[CommsPublicKey], + message: HotStuffMessage, + ) -> Result<(), DigitalAssetError> { + for committee_member in committee { + // TODO: send in parallel + self.send(from.clone(), committee_member.clone(), message.clone()) + .await?; + } + Ok(()) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/mempool_service.rs b/applications/tari_dan_node/src/dan_layer/services/mempool_service.rs new file mode 100644 index 0000000000..dcb0859b05 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/mempool_service.rs @@ -0,0 +1,99 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{dan_layer::models::Instruction, digital_assets_error::DigitalAssetError}; +use std::{ + ops::Index, + sync::{Arc, Mutex}, +}; + +pub trait MempoolService { + fn submit_instruction(&mut self, instruction: Instruction) -> Result<(), DigitalAssetError>; + fn read_block(&self, limit: usize) -> Result, DigitalAssetError>; + fn remove_instructions(&mut self, instructions: &[Instruction]) -> Result<(), DigitalAssetError>; + fn size(&self) -> usize; +} + +pub struct ConcreteMempoolService { + instructions: Vec, +} + +impl ConcreteMempoolService { + pub fn new() -> Self { + Self { instructions: vec![] } + } +} + +impl MempoolService for ConcreteMempoolService { + fn submit_instruction(&mut self, instruction: Instruction) -> Result<(), DigitalAssetError> { + self.instructions.push(instruction); + Ok(()) + } + + fn read_block(&self, limit: usize) -> Result, DigitalAssetError> { + Ok(self.instructions.clone()) + } + + fn remove_instructions(&mut self, instructions: &[Instruction]) -> Result<(), DigitalAssetError> { + let mut result = self.instructions.clone(); + for i in instructions { + if let Some(position) = result.iter().position(|r| r == i) { + result.remove(position); + } + } + self.instructions = result; + Ok(()) + } + + fn size(&self) -> usize { + self.instructions.len() + } +} + +#[derive(Clone)] +pub struct MempoolServiceHandle { + mempool: Arc>, +} + +impl MempoolServiceHandle { + pub fn new(mempool: Arc>) -> Self { + Self { mempool } + } +} + +impl MempoolService for MempoolServiceHandle { + fn submit_instruction(&mut self, instruction: Instruction) -> Result<(), DigitalAssetError> { + self.mempool.lock().unwrap().submit_instruction(instruction) + } + + fn read_block(&self, limit: usize) -> Result, DigitalAssetError> { + self.mempool.lock().unwrap().read_block(limit) + } + + fn remove_instructions(&mut self, instructions: &[Instruction]) -> Result<(), DigitalAssetError> { + self.mempool.lock().unwrap().remove_instructions(instructions) + } + + fn size(&self) -> usize { + self.mempool.lock().unwrap().size() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/mocks/mod.rs b/applications/tari_dan_node/src/dan_layer/services/mocks/mod.rs new file mode 100644 index 0000000000..ffc464f948 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/mocks/mod.rs @@ -0,0 +1,191 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{Event, Instruction, Payload, Signature, View, ViewId}, + services::{ + infrastructure_services::NodeAddressable, + BftReplicaService, + EventsPublisher, + MempoolService, + PayloadProcessor, + PayloadProvider, + SigningService, + TemplateService, + }, + }, + digital_assets_error::DigitalAssetError, +}; +use async_trait::async_trait; +use std::{ + collections::{vec_deque::Iter, VecDeque}, + marker::PhantomData, + sync::{Arc, Mutex}, +}; + +pub struct MockMempoolService {} + +impl MempoolService for MockMempoolService { + fn submit_instruction(&mut self, instruction: Instruction) -> Result<(), DigitalAssetError> { + todo!() + } + + fn read_block(&self, limit: usize) -> Result, DigitalAssetError> { + todo!() + } + + fn remove_instructions(&mut self, instructions: &[Instruction]) -> Result<(), DigitalAssetError> { + todo!() + } + + fn size(&self) -> usize { + 0 + } +} + +pub fn mock_mempool() -> MockMempoolService { + MockMempoolService {} +} + +pub struct MockBftReplicaService { + current_view: View, +} + +impl MockBftReplicaService { + pub fn new() -> Self { + Self { + current_view: View { + view_id: ViewId(0), + is_leader: false, + }, + } + } +} + +impl BftReplicaService for MockBftReplicaService { + fn current_view(&self) -> View { + self.current_view.clone() + } +} + +pub fn mock_bft() -> MockBftReplicaService { + MockBftReplicaService::new() +} + +pub fn mock_static_payload_provider( + static_payload: TPayload, +) -> MockStaticPayloadProvider { + MockStaticPayloadProvider { static_payload } +} + +pub struct MockStaticPayloadProvider { + static_payload: TPayload, +} + +impl PayloadProvider for MockStaticPayloadProvider { + fn create_payload(&self) -> Result { + Ok(self.static_payload.clone()) + } + + fn create_genesis_payload(&self) -> TPayload { + self.static_payload.clone() + } + + fn get_payload_queue(&self) -> usize { + 1 + } +} + +pub fn mock_payload_provider() -> MockStaticPayloadProvider<&'static str> { + MockStaticPayloadProvider { + static_payload: "", + } +} + +pub fn mock_events_publisher() -> MockEventsPublisher { + MockEventsPublisher::new() +} + +#[derive(Clone)] +pub struct MockEventsPublisher { + events: Arc>>, +} + +impl MockEventsPublisher { + pub fn new() -> Self { + Self { + events: Arc::new(Mutex::new(VecDeque::new())), + } + } + + pub fn to_vec(&self) -> Vec { + self.events.lock().unwrap().iter().map(|s| s.clone()).collect() + } +} + +impl EventsPublisher for MockEventsPublisher { + fn publish(&mut self, event: TEvent) { + self.events.lock().unwrap().push_back(event) + } +} + +pub fn mock_signing_service() -> MockSigningService { + MockSigningService:: { p: PhantomData } +} + +pub struct MockSigningService { + p: PhantomData, +} + +impl SigningService for MockSigningService { + fn sign(&self, identity: &TAddr, challenge: &[u8]) -> Result { + Ok(Signature {}) + } +} + +pub fn mock_template_service() -> MockTemplateService { + MockTemplateService {} +} + +pub struct MockTemplateService {} + +#[async_trait] +impl TemplateService for MockTemplateService { + async fn execute_instruction(&mut self, instruction: &Instruction) -> Result<(), DigitalAssetError> { + dbg!("Executing instruction as mock"); + Ok(()) + } +} + +pub fn mock_payload_processor() -> MockPayloadProcessor { + MockPayloadProcessor {} +} + +pub struct MockPayloadProcessor {} + +#[async_trait] +impl PayloadProcessor for MockPayloadProcessor { + async fn process_payload(&mut self, _payload: &TPayload) -> Result<(), DigitalAssetError> { + Ok(()) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/mod.rs b/applications/tari_dan_node/src/dan_layer/services/mod.rs new file mode 100644 index 0000000000..99694083b7 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/mod.rs @@ -0,0 +1,41 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod bft_replica_service; +mod events_publisher; +pub mod infrastructure_services; +mod mempool_service; +mod payload_processor; +mod payload_provider; +mod signing_service; +mod template_service; + +pub use bft_replica_service::{BftReplicaService, ConcreteBftReplicaService}; +pub use events_publisher::{EventsPublisher, LoggingEventsPublisher}; +pub use mempool_service::{ConcreteMempoolService, MempoolService, MempoolServiceHandle}; +pub use payload_processor::{InstructionSetProcessor, PayloadProcessor}; +pub use payload_provider::{MempoolPayloadProvider, PayloadProvider}; +pub use signing_service::{NodeIdentitySigningService, SigningService}; +pub use template_service::{ConcreteTemplateService, MemoryInstructionLog, TemplateService}; + +#[cfg(test)] +pub mod mocks; diff --git a/applications/tari_dan_node/src/dan_layer/services/payload_processor.rs b/applications/tari_dan_node/src/dan_layer/services/payload_processor.rs new file mode 100644 index 0000000000..794ce5960a --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/payload_processor.rs @@ -0,0 +1,69 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{InstructionSet, Payload}, + services::{MempoolService, TemplateService}, + }, + digital_assets_error::DigitalAssetError, +}; +use async_trait::async_trait; + +#[async_trait] +pub trait PayloadProcessor { + async fn process_payload(&mut self, payload: &TPayload) -> Result<(), DigitalAssetError>; +} + +pub struct InstructionSetProcessor { + template_service: TTemplateService, + mempool_service: TMempoolService, +} + +impl + InstructionSetProcessor +{ + pub fn new(template_service: TTemplateService, mempool_service: TMempoolService) -> Self { + Self { + template_service, + mempool_service, + } + } +} + +#[async_trait] +impl PayloadProcessor + for InstructionSetProcessor +{ + async fn process_payload(&mut self, payload: &InstructionSet) -> Result<(), DigitalAssetError> { + for instruction in payload.instructions() { + dbg!("Executing instruction"); + dbg!(&instruction); + // TODO: Should we swallow + log the error instead of propagating it? + self.template_service.execute_instruction(instruction).await?; + } + + self.mempool_service.remove_instructions(payload.instructions())?; + + Ok(()) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/payload_provider.rs b/applications/tari_dan_node/src/dan_layer/services/payload_provider.rs new file mode 100644 index 0000000000..0881c73de7 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/payload_provider.rs @@ -0,0 +1,60 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{InstructionSet, Payload}, + services::MempoolService, + }, + digital_assets_error::DigitalAssetError, +}; + +pub trait PayloadProvider { + fn create_payload(&self) -> Result; + fn create_genesis_payload(&self) -> TPayload; + fn get_payload_queue(&self) -> usize; +} + +pub struct MempoolPayloadProvider { + mempool: TMempoolService, +} + +impl MempoolPayloadProvider { + pub fn new(mempool: TMempoolService) -> Self { + Self { mempool } + } +} + +impl PayloadProvider for MempoolPayloadProvider { + fn create_payload(&self) -> Result { + let instructions = self.mempool.read_block(100)?; + Ok(InstructionSet::from_slice(&instructions)) + } + + fn create_genesis_payload(&self) -> InstructionSet { + InstructionSet::empty() + } + + fn get_payload_queue(&self) -> usize { + self.mempool.size() + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/signing_service.rs b/applications/tari_dan_node/src/dan_layer/services/signing_service.rs new file mode 100644 index 0000000000..f27dd7b11b --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/signing_service.rs @@ -0,0 +1,52 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{models::Signature, services::infrastructure_services::NodeAddressable}, + digital_assets_error::DigitalAssetError, +}; +use tari_comms::{types::CommsPublicKey, NodeIdentity}; + +pub trait SigningService { + fn sign(&self, identity: &TAddr, challenge: &[u8]) -> Result; +} + +pub struct NodeIdentitySigningService { + node_identity: NodeIdentity, +} + +impl NodeIdentitySigningService { + pub fn new(node_identity: NodeIdentity) -> Self { + Self { node_identity } + } +} + +impl SigningService for NodeIdentitySigningService { + fn sign(&self, identity: &CommsPublicKey, challenge: &[u8]) -> Result { + if identity != self.node_identity.public_key() { + return Err(DigitalAssetError::InvalidSignature); + } + + // TODO better sig + Ok(Signature {}) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/services/template_service.rs b/applications/tari_dan_node/src/dan_layer/services/template_service.rs new file mode 100644 index 0000000000..f6f8f89251 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/services/template_service.rs @@ -0,0 +1,122 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{Instruction, InstructionCaller, InstructionId, TemplateId}, + storage::{AssetDataStore, FileAssetDataStore}, + template_command::{ExecutionResult, TemplateCommand}, + templates::editable_metadata_template::EditableMetadataTemplate, + }, + digital_assets_error::DigitalAssetError, + types::PublicKey, +}; +use async_trait::async_trait; + +// TODO: Better name needed +#[async_trait] +pub trait TemplateService { + async fn execute_instruction(&mut self, instruction: &Instruction) -> Result<(), DigitalAssetError>; +} + +pub struct ConcreteTemplateService { + template_id: TemplateId, + template_factory: TemplateFactory, + instruction_log: TInstructionLog, + data_store: TAssetDataStore, +} + +#[async_trait] +impl TemplateService + for ConcreteTemplateService +{ + async fn execute_instruction(&mut self, instruction: &Instruction) -> Result<(), DigitalAssetError> { + self.execute( + instruction.method().to_owned(), + instruction.args().to_owned(), + InstructionCaller { + owner_token_id: instruction.from_owner().to_owned(), + }, + // TODO: put in instruction + InstructionId(0), + ) + } +} + +impl + ConcreteTemplateService +{ + pub fn new(data_store: TAssetDataStore, instruction_log: TInstructionLog, template_id: TemplateId) -> Self { + Self { + template_factory: TemplateFactory {}, + instruction_log, + template_id, + data_store, + } + } + + pub fn execute( + &mut self, + method: String, + args: Vec>, + caller: InstructionCaller, + id: InstructionId, + ) -> Result<(), DigitalAssetError> { + let instruction = self + .template_factory + .create_command(self.template_id, method, args, caller)?; + let result = instruction.try_execute(&mut self.data_store)?; + self.instruction_log.store(id, result); + Ok(()) + } +} + +pub struct TemplateFactory {} + +impl TemplateFactory { + pub fn create_command( + &self, + template: TemplateId, + method: String, + args: Vec>, + caller: InstructionCaller, + ) -> Result { + match template { + TemplateId::EditableMetadata => EditableMetadataTemplate::create_command(method, args, caller), + } + } +} + +pub trait InstructionLog { + fn store(&mut self, id: InstructionId, result: ExecutionResult); +} + +#[derive(Default)] +pub struct MemoryInstructionLog { + log: Vec<(InstructionId, ExecutionResult)>, +} + +impl InstructionLog for MemoryInstructionLog { + fn store(&mut self, id: InstructionId, result: ExecutionResult) { + self.log.push((id, result)) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs b/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs new file mode 100644 index 0000000000..606d9bae7c --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs @@ -0,0 +1,109 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::dan_layer::models::TokenId; +use patricia_tree::PatriciaMap; +use patricia_tree::node::{NodeEncoder, NodeDecoder, Node}; +use bytecodec::null::{NullEncoder, NullDecoder}; +use bytecodec::Encode; +use bytecodec::io::{IoEncodeExt, IoDecodeExt}; +use std::path::PathBuf; +use crate::digital_assets_error::DigitalAssetError; +use std::fs::File; +use bytecodec::bytes::{BytesEncoder, BytesDecoder, CopyableBytesDecoder}; +use serde_json::Value; +use bytecodec::json_codec::{JsonDecoder, JsonEncoder}; + +pub trait AssetDataStore { + fn replace_metadata(&mut self, token_id:&TokenId, metadata: Vec) -> Result<(), DigitalAssetError>; +} + +pub struct FileAssetDataStore { + token_metadata: PatriciaMap, + // None if dirty and must be regenerated + merkle_root: Option>, + output_file: PathBuf + +} + +impl FileAssetDataStore { + + pub fn load_or_create(output_file: PathBuf) -> Self { + dbg!(&output_file); + let token_metadata = match File::open(output_file.as_path()) { + Ok(f) => { + let mut decoder = NodeDecoder::new(JsonDecoder::new()); + let node = decoder.decode_exact(f).unwrap(); + let set = PatriciaMap::from(node); + set + }, + Err(_) => PatriciaMap::new() + }; + + Self { + token_metadata, + merkle_root: None, + output_file + } + } + + pub fn save_to_file(&self) -> Result<(), DigitalAssetError> { + let mut encoder = NodeEncoder::new(JsonEncoder::new()); + // TODO: sort unwraps + encoder.start_encoding(self.token_metadata.clone().into()).unwrap(); + let mut output_file = File::create(self.output_file.as_path()).unwrap(); + encoder.encode_all(output_file).unwrap(); + Ok(()) + } +} + +impl AssetDataStore for FileAssetDataStore { + fn replace_metadata(&mut self, token_id: &TokenId, metadata: Vec) -> Result<(), DigitalAssetError> { + let json = String::from_utf8(metadata).unwrap(); + dbg!(&json); + let value = serde_json::from_str(&json).unwrap(); + self.token_metadata.insert(token_id, value); + self.save_to_file() + } +} + +#[cfg(test)] +mod test{ + use super::*; + use std::path::PathBuf; + use std::env::consts::OS; + use tari_test_utils::paths::{temp_tari_path, create_temporary_data_path}; + use crate::dan_layer::models::TokenId; + + #[test] + fn test_create() { + let temp_path = create_temporary_data_path().join("file-asset-data-store"); + { + let mut store = FileAssetDataStore::load_or_create(temp_path.clone()); + store.replace_metadata(&TokenId(vec![11u8]), Vec::from("[1,2,3]".as_bytes())).unwrap(); + } + let store2 = FileAssetDataStore::load_or_create(temp_path); + dbg!(store2.token_metadata); + + } + +} diff --git a/applications/tari_dan_node/src/dan_layer/storage/mod.rs b/applications/tari_dan_node/src/dan_layer/storage/mod.rs new file mode 100644 index 0000000000..c6d488dbcd --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/storage/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod asset_data_store; + +pub use asset_data_store::{AssetDataStore, FileAssetDataStore}; diff --git a/applications/tari_dan_node/src/dan_layer/template_command.rs b/applications/tari_dan_node/src/dan_layer/template_command.rs new file mode 100644 index 0000000000..82ad119c64 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/template_command.rs @@ -0,0 +1,32 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{dan_layer::storage::AssetDataStore, digital_assets_error::DigitalAssetError}; + +pub trait TemplateCommand { + fn try_execute(&self, data_store: &mut AssetDataStore) -> Result; +} + +pub enum ExecutionResult { + Ok, + Error, +} diff --git a/applications/tari_dan_node/src/dan_layer/templates/editable_metadata_template.rs b/applications/tari_dan_node/src/dan_layer/templates/editable_metadata_template.rs new file mode 100644 index 0000000000..7e7a2cc1e7 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/templates/editable_metadata_template.rs @@ -0,0 +1,78 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{InstructionCaller, TokenId}, + storage::AssetDataStore, + template_command::{ExecutionResult, TemplateCommand}, + }, + digital_assets_error::DigitalAssetError, +}; + +pub struct EditableMetadataTemplate {} + +impl EditableMetadataTemplate { + pub fn create_command( + method: String, + args: Vec>, + caller: InstructionCaller, + ) -> Result { + match method.as_str() { + "update" => { + let token_id = caller.owner_token_id().clone(); + let metadata = args.first().ok_or_else(|| DigitalAssetError::MissingArgument { + argument_name: "metadata".to_string(), + position: 0, + })?; + // TODO: check for too many args + + Ok(UpdateMetadataCommand::new(token_id, metadata.clone(), caller)) + }, + _ => Err(DigitalAssetError::UnknownMethod { + method_name: method.clone(), + }), + } + } +} +pub struct UpdateMetadataCommand { + token_id: TokenId, + metadata: Vec, + caller: InstructionCaller, +} + +impl UpdateMetadataCommand { + pub fn new(token_id: TokenId, metadata: Vec, caller: InstructionCaller) -> Self { + Self { + token_id, + metadata, + caller, + } + } +} +impl TemplateCommand for UpdateMetadataCommand { + fn try_execute(&self, data_store: &mut AssetDataStore) -> Result { + // TODO: put in concurrency check, perhaps nonce? + data_store.replace_metadata(&self.token_id, self.metadata.clone())?; + Ok(ExecutionResult::Ok) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/templates/mod.rs b/applications/tari_dan_node/src/dan_layer/templates/mod.rs new file mode 100644 index 0000000000..706309d967 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/templates/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod editable_metadata_template; diff --git a/applications/tari_dan_node/src/dan_layer/workers/consensus_worker.rs b/applications/tari_dan_node/src/dan_layer/workers/consensus_worker.rs new file mode 100644 index 0000000000..c09a94ff62 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/consensus_worker.rs @@ -0,0 +1,446 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{ + domain_events::ConsensusWorkerDomainEvent, + Committee, + ConsensusWorkerState, + Payload, + QuorumCertificate, + View, + ViewId, + }, + services::{ + infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, + BftReplicaService, + EventsPublisher, + MempoolService, + PayloadProcessor, + PayloadProvider, + SigningService, + }, + workers::{ + states, + states::{ConsensusWorkerStateEvent, Prepare, Starting}, + }, + }, + digital_assets_error::DigitalAssetError, +}; +use log::*; +use std::{ + marker::PhantomData, + sync::{Arc, Mutex}, +}; +use tari_shutdown::ShutdownSignal; +use tokio::time::Duration; + +const LOG_TARGET: &str = "tari::dan::consensus_worker"; + +pub struct ConsensusWorker< + TBftReplicaService, + TInboundConnectionService, + TOutboundService, + TAddr, + TPayload, + TPayloadProvider, + TEventsPublisher, + TSigningService, + TPayloadProcessor, +> where + TBftReplicaService: BftReplicaService, + TInboundConnectionService: InboundConnectionService, + TOutboundService: OutboundService, + TAddr: NodeAddressable + Clone + Send, + TPayload: Payload, + TPayloadProvider: PayloadProvider, + TEventsPublisher: EventsPublisher, + TSigningService: SigningService, + TPayloadProcessor: PayloadProcessor, +{ + bft_replica_service: TBftReplicaService, + inbound_connections: TInboundConnectionService, + outbound_service: TOutboundService, + state: ConsensusWorkerState, + current_view_id: ViewId, + committee: Committee, + timeout: Duration, + node_id: TAddr, + payload_provider: TPayloadProvider, + prepare_qc: Arc>, + events_publisher: TEventsPublisher, + locked_qc: Arc>, + signing_service: TSigningService, + payload_processor: TPayloadProcessor, +} + +impl< + TBftReplicaService, + TInboundConnectionService, + TOutboundService, + TAddr, + TPayload, + TPayloadProvider, + TEventsPublisher, + TSigningService, + TPayloadProcessor, + > + ConsensusWorker< + TBftReplicaService, + TInboundConnectionService, + TOutboundService, + TAddr, + TPayload, + TPayloadProvider, + TEventsPublisher, + TSigningService, + TPayloadProcessor, + > +where + TBftReplicaService: BftReplicaService, + TInboundConnectionService: InboundConnectionService + 'static + Send + Sync, + TOutboundService: OutboundService, + TAddr: NodeAddressable + Clone + Send + Sync, + TPayload: Payload, + TPayloadProvider: PayloadProvider, + TEventsPublisher: EventsPublisher, + TSigningService: SigningService, + TPayloadProcessor: PayloadProcessor, +{ + pub fn new( + bft_replica_service: TBftReplicaService, + inbound_connections: TInboundConnectionService, + outbound_service: TOutboundService, + committee: Committee, + node_id: TAddr, + payload_provider: TPayloadProvider, + events_publisher: TEventsPublisher, + signing_service: TSigningService, + payload_processor: TPayloadProcessor, + timeout: Duration, + ) -> Self { + let prepare_qc = Arc::new(QuorumCertificate::genesis(payload_provider.create_genesis_payload())); + + Self { + bft_replica_service, + inbound_connections, + state: ConsensusWorkerState::Starting, + current_view_id: ViewId(0), + timeout, + outbound_service, + committee, + node_id, + locked_qc: prepare_qc.clone(), + prepare_qc, + payload_provider, + events_publisher, + signing_service, + payload_processor, + } + } + + fn get_current_view(&self) -> View { + View { + view_id: self.current_view_id, + is_leader: self.committee.leader_for_view(self.current_view_id) == &self.node_id, + } + } + + pub async fn run( + &mut self, + shutdown: ShutdownSignal, + max_views_to_process: Option, + ) -> Result<(), DigitalAssetError> { + use ConsensusWorkerState::*; + + let starting_view = self.current_view_id; + loop { + if let Some(max) = max_views_to_process { + if max <= self.current_view_id.0 - starting_view.0 { + break; + } + } + let next_event = self.next_state_event(&shutdown).await?; + if next_event.must_shutdown() { + info!( + target: LOG_TARGET, + "Consensus worker is shutting down because {}", + next_event.shutdown_reason().unwrap_or_default() + ); + break; + } + let trns = self.transition(next_event)?; + info!(target: LOG_TARGET, "Transitioning from {:?} to {:?}", trns.0, trns.1); + + self.events_publisher.publish(ConsensusWorkerDomainEvent::StateChanged { + old: trns.0, + new: trns.1, + }); + } + + Ok(()) + } + + async fn next_state_event( + &mut self, + shutdown: &ShutdownSignal, + ) -> Result { + use ConsensusWorkerState::*; + match &mut self.state { + Starting => states::Starting {}.next_event().await, + Prepare => { + let mut p = states::Prepare::new(self.node_id.clone(), self.locked_qc.clone()); + p.next_event( + &self.get_current_view(), + self.timeout, + &self.committee, + &mut self.inbound_connections, + &mut self.outbound_service, + &self.payload_provider, + &self.signing_service, + ) + .await + }, + PreCommit => { + let mut state = states::PreCommitState::new(self.node_id.clone(), self.committee.clone()); + let (res, prepare_qc) = state + .next_event( + self.timeout, + &self.get_current_view(), + &mut self.inbound_connections, + &mut self.outbound_service, + &self.signing_service, + ) + .await?; + if let Some(prepare_qc) = prepare_qc { + self.prepare_qc = Arc::new(prepare_qc); + } + Ok(res) + }, + + Commit => { + let mut state = states::CommitState::new(self.node_id.clone(), self.committee.clone()); + let (res, locked_qc) = state + .next_event( + self.timeout, + &self.get_current_view(), + &mut self.inbound_connections, + &mut self.outbound_service, + &self.signing_service, + ) + .await?; + if let Some(locked_qc) = locked_qc { + self.locked_qc = Arc::new(locked_qc); + } + Ok(res) + }, + Decide => { + let mut state = states::DecideState::new(self.node_id.clone(), self.committee.clone()); + state + .next_event( + self.timeout, + &self.get_current_view(), + &mut self.inbound_connections, + &mut self.outbound_service, + &self.signing_service, + &mut self.payload_processor, + ) + .await + }, + NextView => { + println!("Status: {} in mempool ", self.payload_provider.get_payload_queue(),); + let mut state = states::NextViewState::new(); + state + .next_event( + &self.get_current_view(), + self.prepare_qc.as_ref().clone(), + &mut self.outbound_service, + &self.committee, + self.node_id.clone(), + shutdown, + ) + .await + }, + } + } + + fn transition( + &mut self, + event: ConsensusWorkerStateEvent, + ) -> Result<(ConsensusWorkerState, ConsensusWorkerState), DigitalAssetError> { + use ConsensusWorkerState::*; + use ConsensusWorkerStateEvent::*; + let from = self.state; + self.state = match (&self.state, event) { + (Starting, Initialized) => Prepare, + (_, TimedOut) => NextView, + (NextView, NewView { .. }) => { + self.current_view_id = self.current_view_id.next(); + Prepare + }, + (Prepare, Prepared) => PreCommit, + (PreCommit, PreCommitted) => Commit, + (Commit, Committed) => Decide, + (Decide, Decided) => NextView, + (s, e) => { + dbg!(&s); + dbg!(&e); + unimplemented!("State machine transition not implemented") + }, + }; + Ok((from, self.state)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::dan_layer::services::{ + infrastructure_services::mocks::mock_inbound, + mocks::{mock_bft, mock_mempool}, + }; + + use crate::dan_layer::services::{ + infrastructure_services::mocks::{mock_outbound, MockInboundConnectionService, MockOutboundService}, + mocks::{ + mock_events_publisher, + mock_payload_processor, + mock_signing_service, + mock_static_payload_provider, + MockEventsPublisher, + }, + }; + use futures::task; + use std::collections::HashMap; + use tari_shutdown::Shutdown; + use tokio::task::JoinHandle; + + fn start_replica( + inbound: MockInboundConnectionService<&'static str, &'static str>, + outbound: MockOutboundService<&'static str, &'static str>, + committee: Committee<&'static str>, + node_id: &'static str, + shutdown_signal: ShutdownSignal, + events_publisher: MockEventsPublisher, + ) -> JoinHandle<()> { + let mut replica_a = ConsensusWorker::new( + mock_bft(), + inbound, + outbound, + committee, + node_id, + mock_static_payload_provider("Hello"), + events_publisher, + mock_signing_service(), + mock_payload_processor(), + Duration::from_secs(5), + ); + tokio::spawn(async move { + let res = replica_a.run(shutdown_signal, Some(2)).await; + }) + } + + #[tokio::test(threaded_scheduler)] + async fn test_simple_case() { + let mut shutdown = Shutdown::new(); + let signal = shutdown.to_signal(); + + let committee = Committee::new(vec!["A", "B"]); + let mut outbound = mock_outbound(committee.members.clone()); + + let inbound_a = outbound.take_inbound(&"A").unwrap(); + let inbound_b = outbound.take_inbound(&"B").unwrap(); + // let inbound_c = outbound.take_inbound(&"C").unwrap(); + // let inbound_d = outbound.take_inbound(&"D").unwrap(); + + let events = [ + mock_events_publisher(), + mock_events_publisher(), + mock_events_publisher(), + mock_events_publisher(), + ]; + + let task_a = start_replica( + inbound_a, + outbound.clone(), + committee.clone(), + "A", + signal.clone(), + events[0].clone(), + ); + let task_b = start_replica( + inbound_b, + outbound.clone(), + committee.clone(), + "B", + signal.clone(), + events[1].clone(), + ); + // let task_c = start_replica( + // inbound_c, + // outbound.clone(), + // committee.clone(), + // "C", + // signal.clone(), + // events[2].clone(), + // ); + // let task_d = start_replica( + // inbound_d, + // outbound.clone(), + // committee.clone(), + // "D", + // signal.clone(), + // events[3].clone(), + // ); + shutdown.trigger().unwrap(); + task_a.await.unwrap(); + task_b.await.unwrap(); + // task_c.await.unwrap(); + // task_d.await.unwrap(); + use crate::dan_layer::models::ConsensusWorkerState::*; + // assert_eq!(events[0].to_vec(), vec![ConsensusWorkerDomainEvent::StateChanged { + // old: Starting, + // new: Prepare + // }]); + + assert_state_change(&events[0].to_vec(), vec![ + Prepare, NextView, Prepare, PreCommit, Commit, Decide, NextView, Prepare, PreCommit, Commit, Decide, + NextView, + ]); + assert_state_change(&events[1].to_vec(), vec![ + Prepare, NextView, Prepare, PreCommit, Commit, Decide, NextView, Prepare, PreCommit, Commit, Decide, + NextView, + ]); + } + + fn assert_state_change(events: &[ConsensusWorkerDomainEvent], states: Vec) { + dbg!(events); + let mapped_events = events.iter().filter_map(|e| match e { + ConsensusWorkerDomainEvent::StateChanged { old, new } => Some(new), + _ => None, + }); + for (state, event) in states.iter().zip(mapped_events) { + assert_eq!(state, event) + } + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/mod.rs b/applications/tari_dan_node/src/dan_layer/workers/mod.rs new file mode 100644 index 0000000000..9ad4bd59b5 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/mod.rs @@ -0,0 +1,26 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod consensus_worker; +pub mod states; + +pub use consensus_worker::ConsensusWorker; diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs b/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs new file mode 100644 index 0000000000..d0d851ecb5 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs @@ -0,0 +1,286 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{ + Committee, + HotStuffMessage, + HotStuffMessageType, + HotStuffTreeNode, + Payload, + QuorumCertificate, + View, + ViewId, + }, + services::{ + infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, + SigningService, + }, + workers::states::ConsensusWorkerStateEvent, + }, + digital_assets_error::DigitalAssetError, +}; +use std::{any::Any, collections::HashMap, marker::PhantomData, time::Instant}; +use tokio::time::{delay_for, Duration}; + +// TODO: This is very similar to pre-commit state +pub struct CommitState +where + TInboundConnectionService: InboundConnectionService, + TAddr: NodeAddressable, + TPayload: Payload, + TOutboundService: OutboundService, + TSigningService: SigningService, +{ + node_id: TAddr, + committee: Committee, + phantom_inbound: PhantomData, + phantom_outbound: PhantomData, + ta: PhantomData, + p_p: PhantomData, + p_s: PhantomData, + received_new_view_messages: HashMap>, + pre_commit_qc: Option>, + locked_qc: Option>, +} + +impl + CommitState +where + TInboundConnectionService: InboundConnectionService, + TOutboundService: OutboundService, + TAddr: NodeAddressable, + TPayload: Payload, + TSigningService: SigningService, +{ + pub fn new(node_id: TAddr, committee: Committee) -> Self { + Self { + node_id, + committee, + phantom_inbound: PhantomData, + phantom_outbound: PhantomData, + ta: PhantomData, + p_p: PhantomData, + received_new_view_messages: HashMap::new(), + pre_commit_qc: None, + locked_qc: None, + p_s: PhantomData, + } + } + + pub async fn next_event( + &mut self, + timeout: Duration, + current_view: &View, + inbound_services: &mut TInboundConnectionService, + outbound_service: &mut TOutboundService, + signing_service: &TSigningService, + ) -> Result<(ConsensusWorkerStateEvent, Option>), DigitalAssetError> { + let mut next_event_result = ConsensusWorkerStateEvent::Errored { + reason: "loop ended without setting this event".to_string(), + }; + + self.received_new_view_messages.clear(); + // TODO: rather change the loop below to inside the wait for message + let started = Instant::now(); + loop { + tokio::select! { + (from, message) = self.wait_for_message(inbound_services) => { + if current_view.is_leader() { + if let Some(result) = self.process_leader_message(¤t_view, message.clone(), &from, outbound_service + ).await?{ + next_event_result = result; + break; + } + + } + let leader= self.committee.leader_for_view(current_view.view_id).clone(); + if let Some(result) = self.process_replica_message(&message, ¤t_view, &from, &leader, outbound_service, &signing_service).await? { + next_event_result = result; + break; + } + + } + _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + // TODO: perhaps this should be from the time the state was entered + next_event_result = ConsensusWorkerStateEvent::TimedOut; + break; + } + } + } + Ok((next_event_result, self.locked_qc.clone())) + } + + async fn wait_for_message( + &self, + inbound_connection: &mut TInboundConnectionService, + ) -> (TAddr, HotStuffMessage) { + inbound_connection.receive_message().await + } + + async fn process_leader_message( + &mut self, + current_view: &View, + message: HotStuffMessage, + sender: &TAddr, + outbound: &mut TOutboundService, + ) -> Result, DigitalAssetError> { + if !message.matches(HotStuffMessageType::PreCommit, current_view.view_id) { + return Ok(None); + } + + // TODO: This might need to be checked in the QC rather + if self.received_new_view_messages.contains_key(&sender) { + dbg!("Already received message from {:?}", &sender); + return Ok(None); + } + + self.received_new_view_messages.insert(sender.clone(), message); + + if self.received_new_view_messages.len() >= self.committee.consensus_threshold() { + println!( + "[COMMIT] Consensus has been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + self.committee.len() + ); + + if let Some(qc) = self.create_qc(¤t_view) { + self.pre_commit_qc = Some(qc.clone()); + self.broadcast(outbound, qc, current_view.view_id).await?; + // return Ok(Some(ConsensusWorkerStateEvent::PreCommitted)); + return Ok(None); // Replica will move this on + } + dbg!("committee did not agree on node"); + return Ok(None); + + // let high_qc = self.find_highest_qc(); + // let proposal = self.create_proposal(high_qc.node(), payload_provider); + // self.broadcast_proposal(outbound, proposal, high_qc, current_view.view_id) + // .await?; + // Ok(Some(ConsensusWorkerStateEvent::Prepared)) + } else { + println!( + "[COMMIT] Consensus has NOT YET been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + self.committee.len() + ); + return Ok(None); + } + } + + async fn broadcast( + &self, + outbound: &mut TOutboundService, + pre_commit_qc: QuorumCertificate, + view_number: ViewId, + ) -> Result<(), DigitalAssetError> { + let message = HotStuffMessage::commit(None, Some(pre_commit_qc), view_number); + outbound + .broadcast(self.node_id.clone(), self.committee.members.as_slice(), message) + .await + } + + fn create_qc(&self, current_view: &View) -> Option> { + // TODO: This can be done in one loop instead of two + let mut node = None; + for message in self.received_new_view_messages.values() { + node = match node { + None => message.node().map(|n| n.clone()), + Some(n) => { + if let Some(m_node) = message.node() { + if &n != m_node { + unimplemented!("Nodes did not match"); + } + Some(m_node.clone()) + } else { + Some(n) + } + }, + }; + } + + let node = node.unwrap(); + let mut qc = QuorumCertificate::new(HotStuffMessageType::PreCommit, current_view.view_id, node, None); + for message in self.received_new_view_messages.values() { + qc.combine_sig(message.partial_sig().unwrap()) + } + Some(qc) + } + + async fn process_replica_message( + &mut self, + message: &HotStuffMessage, + current_view: &View, + from: &TAddr, + view_leader: &TAddr, + outbound: &mut TOutboundService, + signing_service: &TSigningService, + ) -> Result, DigitalAssetError> { + if let Some(justify) = message.justify() { + if !justify.matches(HotStuffMessageType::PreCommit, current_view.view_id) { + dbg!( + "Wrong justify message type received, log", + &self.node_id, + &justify.message_type(), + current_view.view_id + ); + return Ok(None); + } + // if message.node().is_none() { + // unimplemented!("Empty message"); + // } + + if from != view_leader { + dbg!("Message not from leader"); + return Ok(None); + } + + self.locked_qc = Some(justify.clone()); + self.send_vote_to_leader( + justify.node(), + outbound, + view_leader, + current_view.view_id, + &signing_service, + ) + .await?; + return Ok(Some(ConsensusWorkerStateEvent::Committed)); + } else { + dbg!("received non justify message"); + Ok(None) + } + } + + async fn send_vote_to_leader( + &self, + node: &HotStuffTreeNode, + outbound: &mut TOutboundService, + view_leader: &TAddr, + view_number: ViewId, + signing_service: &TSigningService, + ) -> Result<(), DigitalAssetError> { + let mut message = HotStuffMessage::commit(Some(node.clone()), None, view_number); + message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + outbound.send(self.node_id.clone(), view_leader.clone(), message).await + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs b/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs new file mode 100644 index 0000000000..76297ac264 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs @@ -0,0 +1,295 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{ + Committee, + HotStuffMessage, + HotStuffMessageType, + HotStuffTreeNode, + Payload, + QuorumCertificate, + View, + ViewId, + }, + services::{ + infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, + PayloadProcessor, + SigningService, + }, + workers::states::ConsensusWorkerStateEvent, + }, + digital_assets_error::DigitalAssetError, +}; +use std::{any::Any, collections::HashMap, marker::PhantomData, time::Instant}; +use tokio::time::{delay_for, Duration}; + +// TODO: This is very similar to pre-commit, and commit state +pub struct DecideState +where + TInboundConnectionService: InboundConnectionService, + TAddr: NodeAddressable, + TPayload: Payload, + TOutboundService: OutboundService, + TSigningService: SigningService, + TPayloadProcessor: PayloadProcessor, +{ + node_id: TAddr, + committee: Committee, + phantom_inbound: PhantomData, + phantom_outbound: PhantomData, + ta: PhantomData, + p_p: PhantomData, + p_s: PhantomData, + p_t: PhantomData, + received_new_view_messages: HashMap>, + commit_qc: Option>, + locked_qc: Option>, +} + +impl + DecideState +where + TInboundConnectionService: InboundConnectionService, + TOutboundService: OutboundService, + TAddr: NodeAddressable, + TPayload: Payload, + TSigningService: SigningService, + TPayloadProcessor: PayloadProcessor, +{ + pub fn new(node_id: TAddr, committee: Committee) -> Self { + Self { + node_id, + committee, + phantom_inbound: PhantomData, + phantom_outbound: PhantomData, + ta: PhantomData, + p_p: PhantomData, + received_new_view_messages: HashMap::new(), + commit_qc: None, + locked_qc: None, + p_s: PhantomData, + p_t: PhantomData, + } + } + + pub async fn next_event( + &mut self, + timeout: Duration, + current_view: &View, + inbound_services: &mut TInboundConnectionService, + outbound_service: &mut TOutboundService, + signing_service: &TSigningService, + payload_processor: &mut TPayloadProcessor, + ) -> Result { + let mut next_event_result = ConsensusWorkerStateEvent::Errored { + reason: "loop ended without setting this event".to_string(), + }; + + self.received_new_view_messages.clear(); + // TODO: rather change the loop below to inside the wait for message + let started = Instant::now(); + loop { + tokio::select! { + (from, message) = self.wait_for_message(inbound_services) => { + if current_view.is_leader() { + if let Some(result) = self.process_leader_message(¤t_view, message.clone(), &from, outbound_service + ).await?{ + next_event_result = result; + break; + } + + } + let leader= self.committee.leader_for_view(current_view.view_id).clone(); + if let Some(result) = self.process_replica_message(&message, ¤t_view, &from, &leader, payload_processor).await? { + next_event_result = result; + break; + } + + } + _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + // TODO: perhaps this should be from the time the state was entered + next_event_result = ConsensusWorkerStateEvent::TimedOut; + break; + } + } + } + Ok(next_event_result) + } + + async fn wait_for_message( + &self, + inbound_connection: &mut TInboundConnectionService, + ) -> (TAddr, HotStuffMessage) { + inbound_connection.receive_message().await + } + + async fn process_leader_message( + &mut self, + current_view: &View, + message: HotStuffMessage, + sender: &TAddr, + outbound: &mut TOutboundService, + ) -> Result, DigitalAssetError> { + if !message.matches(HotStuffMessageType::Commit, current_view.view_id) { + return Ok(None); + } + + // TODO: This might need to be checked in the QC rather + if self.received_new_view_messages.contains_key(&sender) { + dbg!("Already received message from {:?}", &sender); + return Ok(None); + } + + self.received_new_view_messages.insert(sender.clone(), message); + + if self.received_new_view_messages.len() >= self.committee.consensus_threshold() { + println!( + "[DECIDE] Consensus has been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + self.committee.len() + ); + + if let Some(qc) = self.create_qc(¤t_view) { + self.commit_qc = Some(qc.clone()); + self.broadcast(outbound, qc, current_view.view_id).await?; + // return Ok(Some(ConsensusWorkerStateEvent::PreCommitted)); + return Ok(None); // Replica will move this on + } + dbg!("committee did not agree on node"); + return Ok(None); + + // let high_qc = self.find_highest_qc(); + // let proposal = self.create_proposal(high_qc.node(), payload_provider); + // self.broadcast_proposal(outbound, proposal, high_qc, current_view.view_id) + // .await?; + // Ok(Some(ConsensusWorkerStateEvent::Prepared)) + } else { + println!( + "[DECIDE] Consensus has NOT YET been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + self.committee.len() + ); + return Ok(None); + } + } + + async fn broadcast( + &self, + outbound: &mut TOutboundService, + commit_qc: QuorumCertificate, + view_number: ViewId, + ) -> Result<(), DigitalAssetError> { + let message = HotStuffMessage::decide(None, Some(commit_qc), view_number); + outbound + .broadcast(self.node_id.clone(), self.committee.members.as_slice(), message) + .await + } + + fn create_qc(&self, current_view: &View) -> Option> { + // TODO: This can be done in one loop instead of two + let mut node = None; + for message in self.received_new_view_messages.values() { + node = match node { + None => message.node().map(|n| n.clone()), + Some(n) => { + if let Some(m_node) = message.node() { + if &n != m_node { + unimplemented!("Nodes did not match"); + } + Some(m_node.clone()) + } else { + Some(n) + } + }, + }; + } + + let node = node.unwrap(); + let mut qc = QuorumCertificate::new(HotStuffMessageType::Commit, current_view.view_id, node, None); + for message in self.received_new_view_messages.values() { + qc.combine_sig(message.partial_sig().unwrap()) + } + Some(qc) + } + + async fn process_replica_message( + &mut self, + message: &HotStuffMessage, + current_view: &View, + from: &TAddr, + view_leader: &TAddr, + payload_processor: &mut TPayloadProcessor, + ) -> Result, DigitalAssetError> { + if let Some(justify) = message.justify() { + if !justify.matches(HotStuffMessageType::Commit, current_view.view_id) { + dbg!( + "Wrong justify message type received, log", + &self.node_id, + &justify.message_type(), + current_view.view_id + ); + return Ok(None); + } + // if message.node().is_none() { + // unimplemented!("Empty message"); + // } + + if from != view_leader { + dbg!("Message not from leader"); + return Ok(None); + } + + // self.locked_qc = Some(justify.clone()); + // self.send_vote_to_leader( + // justify.node(), + // outbound, + // view_leader, + // current_view.view_id, + // &signing_service, + // ) + // .await?; + dbg!("Going to apply txs: ", justify.node().payload()); + + payload_processor.process_payload(justify.node().payload()).await?; + + return Ok(Some(ConsensusWorkerStateEvent::Decided)); + } else { + dbg!("received non justify message"); + Ok(None) + } + } + + async fn send_vote_to_leader( + &self, + node: &HotStuffTreeNode, + outbound: &mut TOutboundService, + view_leader: &TAddr, + view_number: ViewId, + signing_service: &TSigningService, + ) -> Result<(), DigitalAssetError> { + let mut message = HotStuffMessage::commit(Some(node.clone()), None, view_number); + message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + outbound.send(self.node_id.clone(), view_leader.clone(), message).await + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/mod.rs b/applications/tari_dan_node/src/dan_layer/workers/states/mod.rs new file mode 100644 index 0000000000..d91e408a76 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/mod.rs @@ -0,0 +1,85 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{View, ViewId}, + workers::ConsensusWorker, + }, + digital_assets_error::DigitalAssetError, +}; +use async_trait::async_trait; + +use tari_shutdown::ShutdownSignal; + +// #[async_trait] +// pub trait State { +// async fn next_event( +// &mut self, +// current_view: &View, +// shutdown: &ShutdownSignal, +// ) -> Result; +// } +use crate::dan_layer::models::QuorumCertificate; + +mod commit_state; +mod decide_state; +mod next_view; +mod pre_commit_state; +mod prepare; +mod starting; + +pub use commit_state::CommitState; +pub use decide_state::DecideState; +pub use next_view::NextViewState; +pub use pre_commit_state::PreCommitState; +pub use prepare::Prepare; +pub use starting::Starting; + +#[derive(Debug, PartialEq)] +pub enum ConsensusWorkerStateEvent { + Initialized, + Errored { reason: String }, + Prepared, + PreCommitted, + Committed, + Decided, + ShutdownReceived, + TimedOut, + NewView { new_view: ViewId }, +} + +impl ConsensusWorkerStateEvent { + pub fn must_shutdown(&self) -> bool { + match self { + ConsensusWorkerStateEvent::Errored { .. } => true, + _ => false, + } + } + + pub fn shutdown_reason(&self) -> Option<&str> { + match self { + ConsensusWorkerStateEvent::Errored { reason } => Some(reason.as_str()), + _ => None, + } + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/next_view.rs b/applications/tari_dan_node/src/dan_layer/workers/states/next_view.rs new file mode 100644 index 0000000000..eb590ec015 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/next_view.rs @@ -0,0 +1,61 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{Committee, HotStuffMessage, Payload, QuorumCertificate, View}, + services::infrastructure_services::{NodeAddressable, OutboundService}, + workers::states::ConsensusWorkerStateEvent, + }, + digital_assets_error::DigitalAssetError, +}; +use tari_shutdown::ShutdownSignal; + +pub struct NextViewState {} + +impl NextViewState { + pub fn new() -> Self { + Self {} + } + + pub async fn next_event< + TPayload: Payload, + TOutboundService: OutboundService, + TAddr: NodeAddressable + Clone + Send, + >( + &mut self, + current_view: &View, + prepare_qc: QuorumCertificate, + broadcast: &mut TOutboundService, + committee: &Committee, + node_id: TAddr, + shutdown: &ShutdownSignal, + ) -> Result { + let message = HotStuffMessage::new_view(prepare_qc, current_view.view_id); + let next_view = current_view.view_id.next(); + let leader = committee.leader_for_view(next_view); + broadcast.send(node_id, leader.clone(), message).await?; + println!("End of view: {}", current_view.view_id.0); + println!("--------------------------------"); + Ok(ConsensusWorkerStateEvent::NewView { new_view: next_view }) + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs b/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs new file mode 100644 index 0000000000..db7450a7b0 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs @@ -0,0 +1,285 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{ + Committee, + HotStuffMessage, + HotStuffMessageType, + HotStuffTreeNode, + Payload, + QuorumCertificate, + View, + ViewId, + }, + services::{ + infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, + SigningService, + }, + workers::states::ConsensusWorkerStateEvent, + }, + digital_assets_error::DigitalAssetError, +}; +use std::{any::Any, collections::HashMap, marker::PhantomData, time::Instant}; +use tokio::time::{delay_for, Duration}; + +pub struct PreCommitState +where + TInboundConnectionService: InboundConnectionService, + TAddr: NodeAddressable, + TPayload: Payload, + TOutboundService: OutboundService, + TSigningService: SigningService, +{ + node_id: TAddr, + committee: Committee, + phantom_inbound: PhantomData, + phantom_outbound: PhantomData, + ta: PhantomData, + p_p: PhantomData, + p_s: PhantomData, + received_new_view_messages: HashMap>, + prepare_qc: Option>, +} + +impl + PreCommitState +where + TInboundConnectionService: InboundConnectionService, + TOutboundService: OutboundService, + TAddr: NodeAddressable, + TPayload: Payload, + TSigningService: SigningService, +{ + pub fn new(node_id: TAddr, committee: Committee) -> Self { + Self { + node_id, + committee, + phantom_inbound: PhantomData, + phantom_outbound: PhantomData, + ta: PhantomData, + p_p: PhantomData, + received_new_view_messages: HashMap::new(), + prepare_qc: None, + p_s: PhantomData, + } + } + + pub async fn next_event( + &mut self, + timeout: Duration, + current_view: &View, + inbound_services: &mut TInboundConnectionService, + outbound_service: &mut TOutboundService, + signing_service: &TSigningService, + ) -> Result<(ConsensusWorkerStateEvent, Option>), DigitalAssetError> { + let mut next_event_result = ConsensusWorkerStateEvent::Errored { + reason: "loop ended without setting this event".to_string(), + }; + + self.received_new_view_messages.clear(); + // TODO: rather change the loop below to inside the wait for message + let started = Instant::now(); + loop { + tokio::select! { + (from, message) = self.wait_for_message(inbound_services) => { + if current_view.is_leader() { + if let Some(result) = self.process_leader_message(¤t_view, message.clone(), &from, outbound_service + ).await?{ + next_event_result = result; + break; + } + + } + let leader= self.committee.leader_for_view(current_view.view_id).clone(); + if let Some(result) = self.process_replica_message(&message, ¤t_view, &from, &leader, outbound_service, &signing_service).await? { + next_event_result = result; + break; + } + + } + _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + // TODO: perhaps this should be from the time the state was entered + next_event_result = ConsensusWorkerStateEvent::TimedOut; + break; + } + } + } + Ok((next_event_result, self.prepare_qc.clone())) + } + + async fn wait_for_message( + &self, + inbound_connection: &mut TInboundConnectionService, + ) -> (TAddr, HotStuffMessage) { + inbound_connection.receive_message().await + } + + async fn process_leader_message( + &mut self, + current_view: &View, + message: HotStuffMessage, + sender: &TAddr, + outbound: &mut TOutboundService, + ) -> Result, DigitalAssetError> { + if !message.matches(HotStuffMessageType::Prepare, current_view.view_id) { + return Ok(None); + } + + // TODO: This might need to be checked in the QC rather + if self.received_new_view_messages.contains_key(&sender) { + dbg!("Already received message from {:?}", &sender); + return Ok(None); + } + + self.received_new_view_messages.insert(sender.clone(), message); + + if self.received_new_view_messages.len() >= self.committee.consensus_threshold() { + println!( + "[PRECOMMIT] Consensus has been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + self.committee.len() + ); + + if let Some(qc) = self.create_qc(¤t_view) { + self.prepare_qc = Some(qc.clone()); + self.broadcast(outbound, &self.committee, qc, current_view.view_id) + .await?; + // return Ok(Some(ConsensusWorkerStateEvent::PreCommitted)); + return Ok(None); + } + dbg!("committee did not agree on node"); + return Ok(None); + + // let high_qc = self.find_highest_qc(); + // let proposal = self.create_proposal(high_qc.node(), payload_provider); + // self.broadcast_proposal(outbound, proposal, high_qc, current_view.view_id) + // .await?; + // Ok(Some(ConsensusWorkerStateEvent::Prepared)) + } else { + println!( + "[PRECOMMIT] Consensus has NOT YET been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + self.committee.len() + ); + return Ok(None); + } + } + + async fn broadcast( + &self, + outbound: &mut TOutboundService, + committee: &Committee, + prepare_qc: QuorumCertificate, + view_number: ViewId, + ) -> Result<(), DigitalAssetError> { + let message = HotStuffMessage::pre_commit(None, Some(prepare_qc), view_number); + outbound + .broadcast(self.node_id.clone(), committee.members.as_slice(), message) + .await + } + + fn create_qc(&self, current_view: &View) -> Option> { + // TODO: This can be done in one loop instead of two + let mut node = None; + for message in self.received_new_view_messages.values() { + node = match node { + None => message.node().map(|n| n.clone()), + Some(n) => { + if let Some(m_node) = message.node() { + if &n != m_node { + unimplemented!("Nodes did not match"); + } + Some(m_node.clone()) + } else { + Some(n) + } + }, + }; + } + + let node = node.unwrap(); + let mut qc = QuorumCertificate::new(HotStuffMessageType::Prepare, current_view.view_id, node, None); + for message in self.received_new_view_messages.values() { + qc.combine_sig(message.partial_sig().unwrap()) + } + Some(qc) + } + + async fn process_replica_message( + &mut self, + message: &HotStuffMessage, + current_view: &View, + from: &TAddr, + view_leader: &TAddr, + outbound: &mut TOutboundService, + signing_service: &TSigningService, + ) -> Result, DigitalAssetError> { + if let Some(justify) = message.justify() { + if !justify.matches(HotStuffMessageType::Prepare, current_view.view_id) { + dbg!( + "Wrong justify message type received, log", + &self.node_id, + &justify.message_type(), + current_view.view_id + ); + return Ok(None); + } + // if message.node().is_none() { + // unimplemented!("Empty message"); + // } + + if from != view_leader { + dbg!("Message not from leader"); + return Ok(None); + } + + self.prepare_qc = Some(justify.clone()); + self.send_vote_to_leader( + justify.node(), + outbound, + view_leader, + current_view.view_id, + &signing_service, + ) + .await?; + return Ok(Some(ConsensusWorkerStateEvent::PreCommitted)); + } else { + // dbg!("received non justify message"); + Ok(None) + } + } + + async fn send_vote_to_leader( + &self, + node: &HotStuffTreeNode, + outbound: &mut TOutboundService, + view_leader: &TAddr, + view_number: ViewId, + signing_service: &TSigningService, + ) -> Result<(), DigitalAssetError> { + let mut message = HotStuffMessage::pre_commit(Some(node.clone()), None, view_number); + message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + outbound.send(self.node_id.clone(), view_leader.clone(), message).await + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs b/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs new file mode 100644 index 0000000000..2fdb06c1d5 --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs @@ -0,0 +1,377 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::{ + models::{ + Block, + Committee, + HotStuffMessage, + HotStuffMessageType, + HotStuffTreeNode, + Instruction, + Payload, + QuorumCertificate, + View, + ViewId, + }, + services::{ + infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, + BftReplicaService, + MempoolService, + PayloadProvider, + SigningService, + }, + workers::states::ConsensusWorkerStateEvent, + }, + digital_assets_error::DigitalAssetError, +}; +use async_trait::async_trait; +use futures::StreamExt; +use std::{ + any::Any, + collections::HashMap, + hash::Hash, + marker::PhantomData, + sync::{Arc, Mutex}, + time::Instant, +}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use tokio::time::{delay_for, Duration}; + +pub struct Prepare +where + TInboundConnectionService: InboundConnectionService + Send, + TOutboundService: OutboundService, + TAddr: NodeAddressable, + TSigningService: SigningService, + TPayload: Payload, + TPayloadProvider: PayloadProvider, +{ + node_id: TAddr, + locked_qc: Arc>, + // bft_service: Box, + // TODO remove this hack + phantom: PhantomData, + phantom_payload_provider: PhantomData, + phantom_outbound: PhantomData, + phantom_signing: PhantomData, + received_new_view_messages: HashMap>, +} + +impl + Prepare +where + TInboundConnectionService: InboundConnectionService + Send, + TOutboundService: OutboundService, + TAddr: NodeAddressable, + TSigningService: SigningService, + TPayload: Payload, + TPayloadProvider: PayloadProvider, +{ + pub fn new(node_id: TAddr, locked_qc: Arc>) -> Self { + Self { + node_id, + locked_qc, + phantom: PhantomData, + phantom_payload_provider: PhantomData, + phantom_outbound: PhantomData, + phantom_signing: PhantomData, + received_new_view_messages: HashMap::new(), + } + } + + pub async fn next_event( + &mut self, + current_view: &View, + timeout: Duration, + committee: &Committee, + inbound_services: &mut TInboundConnectionService, + outbound_service: &mut TOutboundService, + payload_provider: &TPayloadProvider, + signing_service: &TSigningService, + ) -> Result { + self.received_new_view_messages.clear(); + + let mut next_event_result = ConsensusWorkerStateEvent::Errored { + reason: "loop ended without setting this event".to_string(), + }; + + // TODO: rather change the loop below to inside the wait for message + let started = Instant::now(); + + loop { + tokio::select! { + (from, message) = self.wait_for_message(inbound_services) => { + if current_view.is_leader() { + if let Some(result) = self.process_leader_message(¤t_view, message.clone(), &from, &committee, &payload_provider, outbound_service).await?{ + next_event_result = result; + break; + } + + } + if let Some(result) = self.process_replica_message(&message, ¤t_view, &from, committee.leader_for_view(current_view.view_id), outbound_service, &signing_service).await? { + next_event_result = result; + break; + } + + }, + _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + // TODO: perhaps this should be from the time the state was entered + next_event_result = ConsensusWorkerStateEvent::TimedOut; + break; + } + // _ = shutdown => { + // return Ok(ConsensusWorkerStateEvent::ShutdownReceived) + // } + } + } + Ok(next_event_result) + } + + async fn wait_for_message( + &self, + inbound_connection: &mut TInboundConnectionService, + ) -> (TAddr, HotStuffMessage) { + inbound_connection.receive_message().await + } + + async fn process_leader_message( + &mut self, + current_view: &View, + message: HotStuffMessage, + sender: &TAddr, + committee: &Committee, + payload_provider: &TPayloadProvider, + outbound: &mut TOutboundService, + ) -> Result, DigitalAssetError> { + if message.message_type() != &HotStuffMessageType::NewView { + return Ok(None); + } + + // TODO: This might need to be checked in the QC rather + if self.received_new_view_messages.contains_key(&sender) { + dbg!("Already received message from {:?}", &sender); + return Ok(None); + } + + self.received_new_view_messages.insert(sender.clone(), message); + + if self.received_new_view_messages.len() >= committee.consensus_threshold() { + println!( + "[PREPARE] Consensus has been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + committee.len() + ); + let high_qc = self.find_highest_qc(); + let proposal = self.create_proposal(high_qc.node(), payload_provider).await?; + self.broadcast_proposal(outbound, &committee, proposal, high_qc, current_view.view_id) + .await?; + // Ok(Some(ConsensusWorkerStateEvent::Prepared)) + Ok(None) // Will move to pre-commit when it receives the message as a replica + } else { + println!( + "[PREPARE] Consensus has NOT YET been reached with {:?} out of {} votes", + self.received_new_view_messages.len(), + committee.len() + ); + return Ok(None); + } + } + + async fn process_replica_message( + &self, + message: &HotStuffMessage, + current_view: &View, + from: &TAddr, + view_leader: &TAddr, + outbound: &mut TOutboundService, + signing_service: &TSigningService, + ) -> Result, DigitalAssetError> { + if !message.matches(HotStuffMessageType::Prepare, current_view.view_id) { + // println!( + // "Wrong message type received, log. {:?} {:?} View {:?}", + // &self.node_id, + // &message.message_type(), + // current_view.view_id + // ); + return Ok(None); + } + if message.node().is_none() { + unimplemented!("Empty message"); + } + if from != view_leader { + dbg!("Message not from leader"); + return Ok(None); + } + let node = message.node().unwrap(); + if let Some(justify) = message.justify() { + if self.does_extend(node, justify.node()) { + if !self.is_safe_node(node, justify) { + unimplemented!("Node is not safe") + } + + self.send_vote_to_leader(node, outbound, view_leader, current_view.view_id, &signing_service) + .await?; + return Ok(Some(ConsensusWorkerStateEvent::Prepared)); + } else { + unimplemented!("Did not extend from qc.justify.node") + } + } else { + unimplemented!("unexpected Null justify ") + } + } + + fn find_highest_qc(&self) -> QuorumCertificate { + let mut max_qc = None; + for (sender, message) in &self.received_new_view_messages { + match &max_qc { + None => max_qc = message.justify().map(|qc| qc.clone()), + Some(qc) => { + if let Some(justify) = message.justify() { + if qc.view_number() < justify.view_number() { + max_qc = Some(justify.clone()) + } + } + }, + } + } + // TODO: this will panic if nothing found + max_qc.unwrap() + } + + async fn create_proposal( + &self, + parent: &HotStuffTreeNode, + payload_provider: &TPayloadProvider, + ) -> Result, DigitalAssetError> { + // TODO: Artificial delay here to set the block time + delay_for(Duration::from_secs(3)).await; + + let payload = payload_provider.create_payload()?; + dbg!(&payload); + Ok(HotStuffTreeNode::from_parent(parent, payload)) + } + + async fn broadcast_proposal( + &self, + outbound: &mut TOutboundService, + committee: &Committee, + proposal: HotStuffTreeNode, + high_qc: QuorumCertificate, + view_number: ViewId, + ) -> Result<(), DigitalAssetError> { + let message = HotStuffMessage::prepare(proposal, Some(high_qc), view_number); + outbound + .broadcast(self.node_id.clone(), committee.members.as_slice(), message) + .await + } + + fn does_extend(&self, node: &HotStuffTreeNode, from: &HotStuffTreeNode) -> bool { + &from.calculate_hash() == node.parent() + } + + fn is_safe_node( + &self, + node: &HotStuffTreeNode, + quorum_certificate: &QuorumCertificate, + ) -> bool { + self.does_extend(node, self.locked_qc.node()) || quorum_certificate.view_number() > self.locked_qc.view_number() + } + + async fn send_vote_to_leader( + &self, + node: &HotStuffTreeNode, + outbound: &mut TOutboundService, + view_leader: &TAddr, + view_number: ViewId, + signing_service: &TSigningService, + ) -> Result<(), DigitalAssetError> { + let mut message = HotStuffMessage::prepare(node.clone(), None, view_number); + message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + outbound.send(self.node_id.clone(), view_leader.clone(), message).await + } +} + +#[cfg(test)] +mod test { + + use super::*; + use crate::dan_layer::{ + models::ViewId, + services::{ + infrastructure_services::mocks::{mock_inbound, mock_outbound}, + mocks::{mock_payload_provider, mock_signing_service}, + }, + }; + use tokio::time::Duration; + + #[tokio::test(threaded_scheduler)] + async fn basic_test_as_leader() { + // let mut inbound = mock_inbound(); + // let mut sender = inbound.create_sender(); + let locked_qc = QuorumCertificate::genesis("Hello world"); + let mut state = Prepare::new("B", Arc::new(locked_qc)); + let view = View { + view_id: ViewId(1), + is_leader: true, + }; + let committee = Committee::new(vec!["A", "B", "C", "D"]); + let mut outbound = mock_outbound(committee.members.clone()); + let mut outbound2 = outbound.clone(); + let mut inbound = outbound.take_inbound(&"B").unwrap(); + let payload_provider = mock_payload_provider(); + let signing = mock_signing_service(); + let task = state.next_event( + &view, + Duration::from_secs(10), + &committee, + &mut inbound, + &mut outbound, + &payload_provider, + &signing, + ); + outbound2 + .send( + "A", + "B", + HotStuffMessage::new_view(QuorumCertificate::genesis("empty"), ViewId(0)), + ) + .await; + outbound2 + .send( + "C", + "B", + HotStuffMessage::new_view(QuorumCertificate::genesis("empty"), ViewId(0)), + ) + .await; + outbound2 + .send( + "D", + "B", + HotStuffMessage::new_view(QuorumCertificate::genesis("empty"), ViewId(0)), + ) + .await; + let event = task.await.unwrap(); + assert_eq!(event, ConsensusWorkerStateEvent::Prepared); + } +} diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs b/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs new file mode 100644 index 0000000000..280f2ab1ce --- /dev/null +++ b/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs @@ -0,0 +1,34 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::digital_assets_error::DigitalAssetError; +use crate::dan_layer::workers::states::ConsensusWorkerStateEvent; + +pub struct Starting { + +} + +impl Starting { + pub async fn next_event(&self ) -> Result { + Ok(ConsensusWorkerStateEvent::Initialized) + } +} diff --git a/applications/tari_dan_node/src/dan_node_config.rs b/applications/tari_dan_node/src/dan_node_config.rs new file mode 100644 index 0000000000..19179cc83b --- /dev/null +++ b/applications/tari_dan_node/src/dan_node_config.rs @@ -0,0 +1,25 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub struct DanNodeConfig { + +} diff --git a/applications/tari_dan_node/src/digital_assets_error.rs b/applications/tari_dan_node/src/digital_assets_error.rs new file mode 100644 index 0000000000..3aeb74bdef --- /dev/null +++ b/applications/tari_dan_node/src/digital_assets_error.rs @@ -0,0 +1,33 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use thiserror::Error; +#[derive(Debug, Error)] +pub enum DigitalAssetError { + #[error("Unknown method: {method_name}")] + UnknownMethod { method_name: String }, + #[error("Missing argument at position {position} (name: {argument_name}")] + MissingArgument { argument_name: String, position: usize }, + #[error("Invalid sig, tODO: fill in deets")] + InvalidSignature, + #[error("Error converting something: {0}")] + ConversionError(String), +} diff --git a/applications/tari_dan_node/src/grpc/dan_grpc_server.rs b/applications/tari_dan_node/src/grpc/dan_grpc_server.rs new file mode 100644 index 0000000000..f3fe9697c7 --- /dev/null +++ b/applications/tari_dan_node/src/grpc/dan_grpc_server.rs @@ -0,0 +1,90 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::{ + dan_layer::{ + models::{Instruction, TokenId}, + services::{ConcreteMempoolService, MempoolService}, + }, + grpc::dan_rpc, + types::{create_com_sig_from_bytes, ComSig, PublicKey}, +}; +use std::sync::{Arc, Mutex}; +use tari_crypto::tari_utilities::ByteArray; +use tokio::sync::RwLock; +use tonic::{Request, Response, Status}; + +pub struct DanGrpcServer { + mempool_service: TMempoolService, +} + +impl DanGrpcServer { + pub fn new(mempool_service: TMempoolService) -> Self { + Self { mempool_service } + } +} + +#[tonic::async_trait] +impl dan_rpc::dan_node_server::DanNode + for DanGrpcServer +{ + async fn get_token_data( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + dbg!(&request); + Err(Status::internal("Oh noes")) + } + + async fn execute_instruction( + &self, + request: Request, + ) -> Result, Status> { + dbg!(&request); + let request = request.into_inner(); + let instruction = Instruction::new( + PublicKey::from_bytes(&request.asset_public_key) + .map_err(|err| Status::invalid_argument("asset_public_key was not a valid public key"))?, + request.method.clone(), + request.args.clone(), + TokenId(request.from.clone()), + // TODO: put signature in here + ComSig::default() + // create_com_sig_from_bytes(&request.signature) + // .map_err(|err| Status::invalid_argument("signature was not a valid comsig"))?, + ); + + // TODO: Find a way to get around this clone + let mut mempool_service = self.mempool_service.clone(); + match mempool_service.submit_instruction(instruction) { + Ok(_) => { + return Ok(Response::new(dan_rpc::ExecuteInstructionResponse { + status: "Accepted".to_string(), + })) + }, + Err(_) => { + return Ok(Response::new(dan_rpc::ExecuteInstructionResponse { + status: "Errored".to_string(), + })) + }, + } + } +} diff --git a/applications/tari_dan_node/src/grpc/mod.rs b/applications/tari_dan_node/src/grpc/mod.rs new file mode 100644 index 0000000000..bfcbf97e63 --- /dev/null +++ b/applications/tari_dan_node/src/grpc/mod.rs @@ -0,0 +1,26 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub(crate) mod dan_grpc_server; + +pub mod dan_rpc { + tonic::include_proto!("tari.dan.rpc"); +} diff --git a/applications/tari_dan_node/src/main.rs b/applications/tari_dan_node/src/main.rs new file mode 100644 index 0000000000..7bad9a91e9 --- /dev/null +++ b/applications/tari_dan_node/src/main.rs @@ -0,0 +1,135 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod cmd_args; +mod dan_layer; +mod digital_assets_error; +mod grpc; +mod p2p; +mod types; + +use crate::grpc::dan_grpc_server::DanGrpcServer; +use anyhow; +use futures::FutureExt; +use log::*; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + process, +}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use thiserror::Error; +use tokio::{runtime, stream::StreamExt, task}; +use tonic::transport::Server; + +use crate::{ + cmd_args::OperationMode, + dan_layer::{ + dan_node::DanNode, + services::{ConcreteMempoolService, MempoolService, MempoolServiceHandle}, + }, + grpc::dan_rpc::dan_node_server::DanNodeServer, +}; +use std::sync::{Arc, Mutex}; +use tari_app_utilities::{initialization::init_configuration, utilities::ExitCodes}; +use tari_common::{configuration::bootstrap::ApplicationType, GlobalConfig}; +use tokio::runtime::Runtime; + +const LOG_TARGET: &str = "dan_node::app"; + +fn main() { + if let Err(exit_code) = main_inner() { + eprintln!("{:?}", exit_code); + error!( + target: LOG_TARGET, + "Exiting with code ({}): {:?}", + exit_code.as_i32(), + exit_code + ); + process::exit(exit_code.as_i32()); + } +} + +fn main_inner() -> Result<(), ExitCodes> { + let (bootstrap, node_config, _) = init_configuration(ApplicationType::DanNode)?; + + // let operation_mode = cmd_args::get_operation_mode(); + // match operation_mode { + // OperationMode::Run => { + let mut runtime = build_runtime()?; + runtime.block_on(run_node(node_config))?; + // } + // } + + Ok(()) +} + +async fn run_node(config: GlobalConfig) -> Result<(), ExitCodes> { + let shutdown = Shutdown::new(); + + let mempool_service = MempoolServiceHandle::new(Arc::new(Mutex::new(ConcreteMempoolService::new()))); + + let grpc_server = DanGrpcServer::new(mempool_service.clone()); + let grpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 18080); + // task::spawn(run_grpc(grpc_server, grpc_addr, shutdown.to_signal())); + + task::spawn(run_grpc(grpc_server, grpc_addr, shutdown.to_signal())); + run_dan_node(shutdown.to_signal(), config, mempool_service).await?; + Ok(()) +} + +fn build_runtime() -> Result { + let mut builder = runtime::Builder::new(); + builder + .threaded_scheduler() + .enable_all() + .build() + .map_err(|e| ExitCodes::UnknownError) +} + +async fn run_dan_node( + shutdown_signal: ShutdownSignal, + config: GlobalConfig, + mempool_service: TMempoolService, +) -> Result<(), ExitCodes> { + let node = DanNode::new(config); + node.start(true, shutdown_signal, mempool_service).await +} + +async fn run_grpc( + grpc_server: DanGrpcServer, + grpc_address: SocketAddr, + shutdown_signal: ShutdownSignal, +) -> Result<(), anyhow::Error> { + info!(target: LOG_TARGET, "Starting GRPC on {}", grpc_address); + + Server::builder() + .add_service(DanNodeServer::new(grpc_server)) + .serve_with_shutdown(grpc_address, shutdown_signal.map(|_| ())) + .await + .map_err(|err| { + error!(target: LOG_TARGET, "GRPC encountered an error:{}", err); + err + })?; + + info!(target: LOG_TARGET, "Stopping GRPC"); + Ok(()) +} diff --git a/applications/tari_dan_node/src/p2p/mod.rs b/applications/tari_dan_node/src/p2p/mod.rs new file mode 100644 index 0000000000..a276ca089e --- /dev/null +++ b/applications/tari_dan_node/src/p2p/mod.rs @@ -0,0 +1,189 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + dan_layer::models::{ + HotStuffMessage, + HotStuffMessageType, + HotStuffTreeNode, + Instruction, + InstructionSet, + Payload, + QuorumCertificate, + Signature, + TokenId, + TreeNodeHash, + ViewId, + }, + types::{com_sig_to_bytes, create_com_sig_from_bytes, PublicKey}, +}; +use std::convert::{TryFrom, TryInto}; +use tari_crypto::tari_utilities::ByteArray; + +#[allow(clippy::large_enum_variant)] +pub mod dan_p2p { + include!(concat!(env!("OUT_DIR"), "/tari.dan_p2p.rs")); +} + +impl From<&HotStuffMessage> for dan_p2p::HotStuffMessage { + fn from(source: &HotStuffMessage) -> Self { + Self { + message_type: source.message_type().as_u8() as i32, + node: source.node().map(|n| n.into()), + justify: source.justify().map(|j| j.into()), + partial_sig: source.partial_sig().map(|s| s.into()), + view_number: source.view_number().as_u64(), + } + } +} + +impl From<&HotStuffTreeNode> for dan_p2p::HotStuffTreeNode { + fn from(source: &HotStuffTreeNode) -> Self { + Self { + parent: Vec::from(source.parent().as_bytes()), + payload: Some(source.payload().into()), + } + } +} + +impl From<&QuorumCertificate> for dan_p2p::QuorumCertificate { + fn from(source: &QuorumCertificate) -> Self { + Self { + message_type: source.message_type().as_u8() as i32, + node: Some(source.node().into()), + view_number: source.view_number().as_u64(), + signature: source.signature().map(|s| s.into()), + } + } +} + +impl From<&Signature> for dan_p2p::Signature { + fn from(s: &Signature) -> Self { + Self {} + } +} + +impl From<&InstructionSet> for dan_p2p::InstructionSet { + fn from(source: &InstructionSet) -> Self { + Self { + instructions: source.instructions().iter().map(|i| i.into()).collect(), + } + } +} + +impl From<&Instruction> for dan_p2p::Instruction { + fn from(source: &Instruction) -> Self { + Self { + asset_id: Vec::from(source.asset_id().as_bytes()), + method: source.method().to_string(), + args: Vec::from(source.args()), + from: Vec::from(source.from_owner().as_bytes()), + signature: vec![], // com_sig_to_bytes(source.signature()), + } + } +} + +impl TryFrom for HotStuffMessage { + type Error = String; + + fn try_from(value: dan_p2p::HotStuffMessage) -> Result { + Ok(Self::new( + ViewId(value.view_number), + HotStuffMessageType::try_from(value.message_type as u8)?, + value.justify.map(|j| j.try_into()).transpose()?, + value.node.map(|n| n.try_into()).transpose()?, + value.partial_sig.map(|p| p.try_into()).transpose()?, + )) + } +} + +impl TryFrom for QuorumCertificate { + type Error = String; + + fn try_from(value: dan_p2p::QuorumCertificate) -> Result { + Ok(Self::new( + HotStuffMessageType::try_from(value.message_type as u8)?, + ViewId(value.view_number), + value + .node + .map(|n| n.try_into()) + .transpose()? + .ok_or_else(|| "node not provided on Quorum Certificate".to_string())?, + value.signature.map(|s| s.try_into()).transpose()?, + )) + } +} + +impl TryFrom for HotStuffTreeNode { + type Error = String; + + fn try_from(value: dan_p2p::HotStuffTreeNode) -> Result { + if value.parent.is_empty() { + return Err("parent not provided".to_string()); + } + Ok(Self::new( + TreeNodeHash(value.parent), + value + .payload + .map(|p| p.try_into()) + .transpose()? + .ok_or_else(|| "payload not provided".to_string())?, + )) + } +} + +impl TryFrom for Signature { + type Error = String; + + fn try_from(_value: dan_p2p::Signature) -> Result { + Ok(Self {}) + } +} + +impl TryFrom for InstructionSet { + type Error = String; + + fn try_from(value: dan_p2p::InstructionSet) -> Result { + let instructions: Vec = value + .instructions + .into_iter() + .map(|i| i.try_into()) + .collect::>()?; + Ok(Self::from_slice(&instructions)) + } +} + +impl TryFrom for Instruction { + type Error = String; + + fn try_from(value: dan_p2p::Instruction) -> Result { + Ok(Self::new( + PublicKey::from_bytes(&value.asset_id) + .map_err(|e| format!("asset_id was not a valid public key: {}", e))?, + value.method, + value.args, + TokenId(value.from), + create_com_sig_from_bytes(&value.signature) + .map_err(|e| format!("Could not convert signature bytes to comsig: {}", e))?, + )) + } +} diff --git a/applications/tari_dan_node/src/p2p/proto/dan_consensus_messages.proto b/applications/tari_dan_node/src/p2p/proto/dan_consensus_messages.proto new file mode 100644 index 0000000000..a32171aec4 --- /dev/null +++ b/applications/tari_dan_node/src/p2p/proto/dan_consensus_messages.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +//import "google/protobuf/wrappers.proto"; +//import "google/protobuf/timestamp.proto"; +//import "transaction.proto"; +//import "types.proto"; + +package tari.dan_p2p; + +enum HotStuffMessageType { + HOT_STUFF_MESSAGE_TYPE_UNKNOWN = 0; + HOT_STUFF_MESSAGE_TYPE_NEW_VIEW = 1; + HOT_STUFF_MESSAGE_TYPE_PREPARE = 2; + HOT_STUFF_MESSAGE_TYPE_PRE_COMMIT = 3; + HOT_STUFF_MESSAGE_TYPE_COMMIT = 4; + HOT_STUFF_MESSAGE_TYPE_GENESIS = 255; + +} + +message HotStuffMessage { + uint64 view_number = 1; + HotStuffMessageType message_type = 2; + QuorumCertificate justify = 3; + HotStuffTreeNode node= 4; + Signature partial_sig = 5; +} + +message QuorumCertificate { + HotStuffMessageType message_type = 1; + HotStuffTreeNode node = 2; + uint64 view_number = 3; + Signature signature = 4; +} + +message HotStuffTreeNode { + bytes parent = 1; + InstructionSet payload = 2; +} + +message Signature{ + +} + +message InstructionSet{ + repeated Instruction instructions = 1; +} +message Instruction { + bytes asset_id = 1; + string method = 2; + repeated bytes args = 3; + bytes from = 4; + bytes signature = 5; +} diff --git a/applications/tari_dan_node/src/types.rs b/applications/tari_dan_node/src/types.rs new file mode 100644 index 0000000000..7a0c9afade --- /dev/null +++ b/applications/tari_dan_node/src/types.rs @@ -0,0 +1,49 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_crypto::{ + commitment::HomomorphicCommitment, + ristretto::{RistrettoPublicKey, RistrettoSecretKey}, + signatures::CommitmentSignature, + tari_utilities::{ByteArray, ByteArrayError}, +}; + +/// Define the explicit Public key implementation for the Tari base layer +pub(crate) type PublicKey = RistrettoPublicKey; + +pub(crate) type ComSig = CommitmentSignature; + +pub fn create_com_sig_from_bytes(bytes: &[u8]) -> Result { + Ok(ComSig::default()) + // Ok(ComSig::new( + // HomomorphicCommitment::from_bytes(&bytes[0..32])?, + // RistrettoSecretKey::from_bytes(&bytes[33..64])?, + // RistrettoSecretKey::from_bytes(&bytes[64..96])?, + // )) +} + +pub fn com_sig_to_bytes(comsig: &ComSig) -> Vec { + let mut v = Vec::from(comsig.public_nonce().as_bytes()); + v.extend_from_slice(comsig.u().as_bytes()); + v.extend_from_slice(comsig.v().as_bytes()); + v +} diff --git a/applications/tari_explorer/app.js b/applications/tari_explorer/app.js index 9007a468f3..f2d3eb0a6d 100644 --- a/applications/tari_explorer/app.js +++ b/applications/tari_explorer/app.js @@ -5,8 +5,9 @@ const cookieParser = require('cookie-parser') const logger = require('morgan') const asciichart = require('asciichart') -var indexRouter = require('./routes/index') -var blocksRouter = require('./routes/blocks') +var indexRouter = require('./routes/index'); +var blocksRouter = require('./routes/blocks'); +var assetsRouter = require('./routes/assets'); var hbs = require('hbs') hbs.registerHelper('hex', function (buffer) { @@ -43,6 +44,10 @@ hbs.registerHelper('chart', function(data, height) { return asciichart.plot(data, {height: height}) }) +hbs.registerHelper('json', function(obj) { + return JSON.stringify(obj); +}) + var app = express() // view engine setup @@ -55,8 +60,9 @@ app.use(express.urlencoded({ extended: false })) app.use(cookieParser()) app.use(express.static(path.join(__dirname, 'public'))) -app.use('/', indexRouter) -app.use('/blocks', blocksRouter) +app.use('/', indexRouter); +app.use('/blocks', blocksRouter); +app.use('/assets', assetsRouter); // catch 404 and forward to error handler app.use(function (req, res, next) { diff --git a/applications/tari_explorer/package-lock.json b/applications/tari_explorer/package-lock.json index 9f7e30e99a..69bd586e57 100644 --- a/applications/tari_explorer/package-lock.json +++ b/applications/tari_explorer/package-lock.json @@ -5,13 +5,11 @@ "requires": true, "dependencies": { "@grpc/grpc-js": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.2.6.tgz", - "integrity": "sha512-wfYwFy7CvVEmBKzeDX1kQQYrv5NBpe8Z+VwXipFvqof3lCXKch7k+4T3grKtptaH5GQ5KP9iKwPr9hMDSynIUw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.4.tgz", + "integrity": "sha512-AxtZcm0mArQhY9z8T3TynCYVEaSKxNCa9mVhVwBCUnsuUEe8Zn94bPYYKVQSLt+hJJ1y0ukr3mUvtWfcATL/IQ==", "requires": { - "@types/node": ">=12.12.47", - "google-auth-library": "^6.1.1", - "semver": "^6.2.0" + "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { @@ -98,9 +96,9 @@ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "14.14.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz", - "integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==" + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz", + "integrity": "sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==" }, "abbrev": { "version": "1.1.1", @@ -108,14 +106,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -125,29 +115,6 @@ "negotiator": "0.6.2" } }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -200,11 +167,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" - }, "asciichart": { "version": "1.5.25", "resolved": "https://registry.npmjs.org/asciichart/-/asciichart-1.5.25.tgz", @@ -217,19 +179,14 @@ "dev": true }, "base-node-grpc-client": { - "version": "git+ssh://git@github.com/mikethetike/base_node_grpc_client.git#29e84e807639e32c0bdcf56654f4626664aeffda", - "from": "base-node-grpc-client@git://github.com/mikethetike/base_node_grpc_client.git#master", + "version": "git://github.com/tari-project/base_node_grpc_client.git#23bb5f46a6a6a21ebd257174758595737e99fad2", + "from": "git://github.com/tari-project/base_node_grpc_client.git#mb-add-nft-data", "requires": { "@grpc/grpc-js": "^1.2.3", "@grpc/proto-loader": "^0.5.5", "grpc-promise": "^1.4.0" } }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -238,11 +195,6 @@ "safe-buffer": "5.1.2" } }, - "bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -301,11 +253,6 @@ "fill-range": "^7.0.1" } }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -537,14 +484,6 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -586,11 +525,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "express": { "version": "4.16.4", "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", @@ -635,16 +569,6 @@ } } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "fast-text-encoding": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", - "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -690,27 +614,6 @@ "dev": true, "optional": true }, - "gaxios": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz", - "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==", - "requires": { - "abort-controller": "^3.0.0", - "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", - "is-stream": "^2.0.0", - "node-fetch": "^2.3.0" - } - }, - "gcp-metadata": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", - "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", - "requires": { - "gaxios": "^4.0.0", - "json-bigint": "^1.0.0" - } - }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -738,30 +641,6 @@ "ini": "1.3.7" } }, - "google-auth-library": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz", - "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==", - "requires": { - "arrify": "^2.0.0", - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^4.0.0", - "gcp-metadata": "^4.2.0", - "gtoken": "^5.0.4", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" - } - }, - "google-p12-pem": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", - "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", - "requires": { - "node-forge": "^0.10.0" - } - }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -792,16 +671,6 @@ "resolved": "https://registry.npmjs.org/grpc-promise/-/grpc-promise-1.4.0.tgz", "integrity": "sha512-4BBXHXb5OjjBh7luylu8vFqL6H6aPn/LeqpQaSBeRzO/Xv95wHW/WkU9TJRqaCTMZ5wq9jTSvlJWp0vRJy1pVA==" }, - "gtoken": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", - "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", - "requires": { - "gaxios": "^4.0.0", - "google-p12-pem": "^3.0.3", - "jws": "^4.0.0" - } - }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -852,30 +721,6 @@ "statuses": ">= 1.4.0 < 2" } }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -991,11 +836,6 @@ "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", "dev": true }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -1008,39 +848,12 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, - "json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "requires": { - "bignumber.js": "^9.0.0" - } - }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", "dev": true }, - "jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "requires": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -1075,14 +888,6 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -1172,16 +977,6 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" - }, "nodemon": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", @@ -1307,9 +1102,9 @@ "dev": true }, "protobufjs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", - "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -1322,15 +1117,8 @@ "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.1", - "@types/node": "^13.7.0", + "@types/node": ">=13.7.0", "long": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "13.13.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.41.tgz", - "integrity": "sha512-qLT9IvHiXJfdrje9VmsLzun7cQ65obsBTmtU3EOnCSLFOoSHx1hpiRHoBnpdbyFqnzqdUUIv81JcEJQCB8un9g==" - } } }, "proxy-addr": { @@ -1449,7 +1237,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "semver-diff": { "version": "3.1.1", @@ -1744,11 +1533,6 @@ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/applications/tari_explorer/package.json b/applications/tari_explorer/package.json index 89a4e02469..ca6992041a 100644 --- a/applications/tari_explorer/package.json +++ b/applications/tari_explorer/package.json @@ -4,11 +4,11 @@ "private": true, "scripts": { "start": "node ./bin/www", - "dev": "nodemon --exec node ./bin/www" + "dev": "nodemon --exec node --trace-warnings ./bin/www" }, "dependencies": { "asciichart": "^1.5.25", - "base-node-grpc-client": "git://github.com/mikethetike/base_node_grpc_client.git#master", + "base-node-grpc-client": "git://github.com/tari-project/base_node_grpc_client.git#mb-add-nft-data", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", diff --git a/applications/tari_explorer/routes/assets.js b/applications/tari_explorer/routes/assets.js new file mode 100644 index 0000000000..e02cad2bc5 --- /dev/null +++ b/applications/tari_explorer/routes/assets.js @@ -0,0 +1,55 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var { createClient } = require('../baseNodeClient') + +var express = require('express') +var router = express.Router() + +/* GET home page. */ +router.get('/:asset_public_key', async function (req, res, next) { + let client = createClient() + let asset_public_key = req.params.asset_public_key + + try { + let tokens = await client.getTokens({ asset_public_key: Buffer.from(asset_public_key, "hex") }) + console.log(tokens) + + if (!tokens || tokens.length === 0) { + res.status(404); + res.render('404', { message: `No tokens for asset found`}); + return; + } + + res.render('assets', { + title: `Asset with pub key: ${asset_public_key}`, + tokens: tokens + }) + + + } catch (error) { + res.status(500) + res.render('error', { error: error }) + } +}) + +module.exports = router diff --git a/applications/tari_explorer/routes/blocks.js b/applications/tari_explorer/routes/blocks.js index 42510818c6..79913211d2 100644 --- a/applications/tari_explorer/routes/blocks.js +++ b/applications/tari_explorer/routes/blocks.js @@ -1,17 +1,58 @@ -var {createClient} = require("../baseNodeClient"); +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -var express = require('express'); -var router = express.Router(); +var { createClient } = require('../baseNodeClient') + +var express = require('express') +var router = express.Router() /* GET home page. */ -router.get('/:height', async function(req, res, next) { - let client = createClient(); - let height = req.params.height; +router.get('/:height', async function (req, res, next) { + let client = createClient() + let height = req.params.height + + try { + let block = await client.getBlocks({ heights: [height] }) - let block = await client.getBlocks({heights:[height]}); + if (!block || block.length === 0) { + res.status(404); + res.render('404', { message: `Block at height ${height} not found`}); + return; + } + console.log(block) + console.log(block[0].block.body.outputs[0]) + res.render('blocks', { + title: `Block at height:${block[0].block.header.height}`, + height: height, + prevHeight: parseInt(height) - 1, + nextHeight: parseInt(height) + 1, + block: block[0].block, + pows: { '0': 'Monero', '2': 'SHA' } + }) - console.log(block[0].block.body.kernels[0]); - res.render('blocks', { title: `Block at height:${block[0].block.header.height}`, height:height, prevHeight: parseInt(height) - 1, nextHeight: parseInt(height) + 1, block: block[0].block , pows: {"0": "Monero", "2": "SHA"}}); -}); + } catch (error) { + res.status(500) + res.render('error', { error: error }) + } +}) -module.exports = router; +module.exports = router diff --git a/applications/tari_explorer/routes/index.js b/applications/tari_explorer/routes/index.js index 2edce785d5..5cf505b516 100644 --- a/applications/tari_explorer/routes/index.js +++ b/applications/tari_explorer/routes/index.js @@ -5,17 +5,21 @@ var router = express.Router() /* GET home page. */ router.get('/', async function (req, res, next) { + try { let client = createClient() let from = parseInt(req.query.from || 0) let limit = parseInt(req.query.limit || '20') let tipInfo = await client.getTipInfo({}) + console.log("Getting headers"); // Algo split let last100Headers = await client.listHeaders({ from_height: 0, num_headers: 101 }) let monero = [0, 0, 0, 0] let sha = [0, 0, 0, 0] + console.log(last100Headers); + for (let i = 0; i < last100Headers.length - 1; i++) { let arr = last100Headers[i].pow.pow_algo === '0' ? monero : sha if (i < 10) { @@ -41,9 +45,7 @@ router.get('/', async function (req, res, next) { sha100: sha[3] } - - - + console.log(algoSplit); // Get one more header than requested so we can work out the difference in MMR_size let headers = await client.listHeaders({ from_height: from, num_headers: limit + 1 }) for (var i = headers.length - 2; i >= 0; i--) { @@ -73,7 +75,6 @@ router.get('/', async function (req, res, next) { } mempool[i].transaction.body.total_fees = sum } - console.log(getBlockTimes(last100Headers, "0")) res.render('index', { title: 'Blocks', tipInfo: tipInfo, @@ -87,20 +88,29 @@ router.get('/', async function (req, res, next) { algoSplit: algoSplit, blockTimes: getBlockTimes(last100Headers), moneroTimes: getBlockTimes(last100Headers, "0"), - shaTimes: getBlockTimes(last100Headers, "2") + shaTimes: getBlockTimes(last100Headers, "1") }) + +} catch (error) { + res.status(500) + res.render('error', { error: error }) +} }) function getBlockTimes(last100Headers, algo) { let blocktimes = [] let i = 0 - if (algo === '0' || algo === '2') { - while (last100Headers[i].pow.pow_algo !== algo) { + if (algo === '0' || algo === '1') { + while (i < last100Headers.length && last100Headers[i].pow.pow_algo !== algo) { i++; blocktimes.push(0) } } + if (i >= last100Headers.length) { + // This happens if there are no blocks for a specific algorithm in last100headers + return blocktimes; + } let lastBlockTime = parseInt(last100Headers[i].timestamp.seconds); i++; while (i< last100Headers.length && blocktimes.length < 60) { diff --git a/applications/tari_explorer/views/404.hbs b/applications/tari_explorer/views/404.hbs new file mode 100644 index 0000000000..08d2e533d4 --- /dev/null +++ b/applications/tari_explorer/views/404.hbs @@ -0,0 +1 @@ +

Not found: {{message}}

diff --git a/applications/tari_explorer/views/assets.hbs b/applications/tari_explorer/views/assets.hbs new file mode 100644 index 0000000000..46e2073af3 --- /dev/null +++ b/applications/tari_explorer/views/assets.hbs @@ -0,0 +1,19 @@ +

{{this.title}}

+ +

Tokens

+ + + + + + + + + {{#each this.tokens}} + + + + + {{/each}} + +
Unique IDOwner commitment
{{hex unique_id}}{{hex owner_commitment}}
diff --git a/applications/tari_explorer/views/blocks.hbs b/applications/tari_explorer/views/blocks.hbs index 04f8cb0436..4007f0259a 100644 --- a/applications/tari_explorer/views/blocks.hbs +++ b/applications/tari_explorer/views/blocks.hbs @@ -40,6 +40,11 @@ Commitment Flags Maturity + Metadata + Asset data + NFT data + Unique Id + Parent PK @@ -49,6 +54,11 @@ {{hex commitment}} {{features.flags}} {{features.maturity}} + {{hex features.metadata}} + {{ json features.asset }} + {{ json features.mint_non_fungible }} + {{ hex unique_id}} + {{ hex parent_public_key }} {{/each}} diff --git a/applications/tari_explorer/views/index.hbs b/applications/tari_explorer/views/index.hbs index e5b3e0d3d1..568228bdee 100644 --- a/applications/tari_explorer/views/index.hbs +++ b/applications/tari_explorer/views/index.hbs @@ -53,24 +53,6 @@ -

Block times (minutes)

-Target time: 2 minutes -

All

-
-{{chart this.blockTimes 15 }}
-
- -

Monero

-Target time: 3.3 minutes -
{{chart this.moneroTimes 15 }}
-
- -

SHA3

-Target time: 5 minutes - -
{{chart this.shaTimes 15 }}
-
-

{{title}}

@@ -134,3 +116,24 @@ Target time: 5 minutes {{/each}}
+ + + +

Block times (minutes)

+Target time: 2 minutes +

All

+
+    {{chart this.blockTimes 15 }}
+
+ +

Monero

+Target time: 3.3 minutes +
{{chart this.moneroTimes 15 }}
+
+ +

SHA3

+Target time: 5 minutes + +
{{chart this.shaTimes 15 }}
+
+

{{title}}

diff --git a/applications/tari_json_rpc_proxy/Cargo.toml b/applications/tari_json_rpc_proxy/Cargo.toml new file mode 100644 index 0000000000..284f38e003 --- /dev/null +++ b/applications/tari_json_rpc_proxy/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tari_json_rpc_proxy" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tari_app_grpc = {path="../tari_app_grpc"} +tari_utilities = "^0.3" + +jsonrpc-http-server = "17.1.0" diff --git a/applications/tari_json_rpc_proxy/src/main.rs b/applications/tari_json_rpc_proxy/src/main.rs new file mode 100644 index 0000000000..3dd12c012f --- /dev/null +++ b/applications/tari_json_rpc_proxy/src/main.rs @@ -0,0 +1,126 @@ +use jsonrpc_http_server::{ + jsonrpc_core::{IoHandler, Params, Value}, + ServerBuilder, +}; + +use tari_app_grpc::tari_rpc as grpc; +use tari_utilities::hex::Hex; + +fn main() { + let mut io = IoHandler::default(); + io.add_method("say_hello", |_params: Params| async { + dbg!("Hello called"); + Ok(Value::String("hello".to_owned())) + }); + + io.add_method("eth_chainId", |_params: Params| async { + dbg!("eth_chainId called"); + Ok(Value::String("0x88".to_owned())) + }); + + io.add_method("eth_blockNumber", |_params: Params| async { + dbg!("eth_blockNumber called"); + Ok(Value::String("0x7777".to_owned())) + }); + + io.add_method("net_version", |_params: Params| async { + dbg!("net_version called"); + Ok(Value::String("Test".to_owned())) + }); + + io.add_method("eth_call", |params: Params| async { + dbg!("eth_call called"); + dbg!(¶ms); + + struct CallParams { + data: String, + to: String, + } + + let call_data = match params { + Params::Array(values) => { + let v = values.first().unwrap(); + CallParams { + data: v["data"].as_str().unwrap().to_string(), + to: v["to"].as_str().unwrap().to_string(), + } + }, + _ => return Ok(Value::String("Unexpected".to_owned())), + }; + + match &call_data.data.as_str()[0..10] { + "0x313ce567" => { + // decimals + Ok(Value::String("0x00".to_owned())) + }, + "0x95d89b41" => + // symbol + { + Ok(Value::String("TXTR2".to_owned())) + }, + "0x70a08231" => + // balance + { + Ok(Value::String("0x17".to_owned())) + }, + _ => Ok(Value::String("don't know".to_owned())), + } + }); + + io.add_method("eth_estimateGas", |_params: Params| async { + dbg!("eth_estimateGas called"); + Ok(Value::String("0x1".to_owned())) + }); + + io.add_method("eth_gasPrice", |_params: Params| async { + dbg!("eth_gasPrice called"); + Ok(Value::String("0x1".to_owned())) + }); + + io.add_method("eth_getTransactionCount", |_params: Params| async { + dbg!("eth_getTransactionCount called"); + Ok(Value::String("0x00".to_owned())) + }); + + io.add_method("eth_sendRawTransaction", |params: Params| async { + dbg!("eth_sendRawTransaction called"); + Ok(Value::String("not yet impl".to_owned())) + }); + + io.add_method("eth_getBalance", |params: Params| async { + dbg!("eth_getBalance called"); + dbg!(params); + Ok(Value::String("0x00".to_owned())) + }); + + io.add_method("eth_accounts", |_params: Params| async { + dbg!("eth_accounts called"); + + let accounts = get_wallet_accounts().await.unwrap(); + Ok(Value::Array( + accounts.into_iter().map(|s| Value::String(s.to_string())).collect(), + )) + }); + + let server = ServerBuilder::new(io) + .threads(3) + .start_http(&"127.0.0.1:3030".parse().unwrap()) + .unwrap(); + + server.wait(); +} + +async fn get_wallet_accounts() -> Result, String> { + let mut wallet_client = grpc::wallet_client::WalletClient::connect(format!("http://{}", "127.0.0.1:18144")) + .await + .unwrap(); + let owned = wallet_client + .get_owned_tokens(grpc::GetOwnedTokensRequest { + asset_public_key: Hex::from_hex("d458e49c9fe023e6706db830c7520ce520ffbbfe22edb26f5c3379d2d4a9b838") + .unwrap(), + }) + .await + .unwrap() + .into_inner(); + Ok(owned.tokens.into_iter().map(|o| o.unique_id.to_hex()).collect()) +} diff --git a/base_layer/core/src/base_node/comms_interface/comms_request.rs b/base_layer/core/src/base_node/comms_interface/comms_request.rs index eef287d8f1..dfef55328f 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_request.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_request.rs @@ -52,6 +52,7 @@ pub enum NodeCommsRequest { GetNewBlockTemplate(GetNewBlockTemplateRequest), GetNewBlock(NewBlockTemplate), FetchKernelByExcessSig(Signature), + FetchTokens { asset_public_key: Vec, unique_ids: Vec>} } #[derive(Debug, Serialize, Deserialize)] @@ -84,6 +85,7 @@ impl Display for NodeCommsRequest { s.get_public_nonce().to_hex(), s.get_signature().to_hex() ), + FetchTokens { .. } => { write!(f, "FetchTokens")} } } } diff --git a/base_layer/core/src/base_node/comms_interface/comms_response.rs b/base_layer/core/src/base_node/comms_interface/comms_response.rs index 8f7ec1b9e5..1846997f07 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_response.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_response.rs @@ -49,6 +49,7 @@ pub enum NodeCommsResponse { TargetDifficulty(Difficulty), FetchHeadersAfterResponse(Vec), MmrNodes(Vec, Vec), + FetchTokensResponse{ outputs: Vec } } impl Display for NodeCommsResponse { @@ -76,6 +77,7 @@ impl Display for NodeCommsResponse { TargetDifficulty(_) => write!(f, "TargetDifficulty"), FetchHeadersAfterResponse(_) => write!(f, "FetchHeadersAfterResponse"), MmrNodes(_, _) => write!(f, "MmrNodes"), + FetchTokensResponse { .. } => write!(f, "FetchTokensResponse") } } } diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index e0d2c1268a..818a811854 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -42,9 +42,9 @@ use std::{ sync::Arc, }; use strum_macros::Display; -use tari_common_types::types::{BlockHash, HashOutput}; +use tari_common_types::types::{BlockHash, HashOutput, PublicKey}; use tari_comms::peer_manager::NodeId; -use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex}; +use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex, ByteArray}; use tokio::sync::Semaphore; const LOG_TARGET: &str = "c::bn::comms_interface::inbound_handler"; @@ -417,6 +417,75 @@ where T: BlockchainBackend + 'static Ok(NodeCommsResponse::TransactionKernels(kernels)) }, + NodeCommsRequest::FetchTokens { + asset_public_key, + unique_ids, + } => { + // TODO: use index to prevent reading all outputs + let tip_header = self.blockchain_db.fetch_tip_header().await?; + + debug!(target: LOG_TARGET, "Starting fetch tokens"); + let mut pos = 0; + let mut tokens = vec![]; + // TODO: fix this janky AF way of doing this + while pos < tip_header.header().output_mmr_size { + unimplemented!("Need to read from db index"); + // let (utxos, deleted) = self + // .blockchain_db + // .fetch_utxos_by_mmr_position( + // pos, + // tip_header.header().output_mmr_size, + // tip_header.hash().clone(), + // ) + // .await?; + + // debug!(target: LOG_TARGET, "Checking {} utxos", utxos.len()); + // let pub_key = PublicKey::from_bytes(&asset_public_key).unwrap(); + // + // for u in utxos { + // match u { + // PrunedOutput::Pruned { + // output_hash, + // witness_hash, + // } => { + // pos += 1; + // debug!( + // target: LOG_TARGET, + // "Checking pruned utxo at {} for NFT:{}", + // pos, + // output_hash.to_hex() + // ); + // continue; + // }, + // PrunedOutput::NotPruned { output } => { + // debug!( + // target: LOG_TARGET, + // "Checking unpruned utxo at {} for NFT:{}", + // pos, + // output.hash().to_hex() + // ); + // if deleted.contains(pos as u32) { + // debug!(target: LOG_TARGET, "Utxo is spent"); + // pos += 1; + // continue; + // } + // if output.parent_public_key.as_ref() == Some(&pub_key) { + // debug!(target: LOG_TARGET, "Utxo has matching pub key"); + // if unique_ids.is_empty() { + // tokens.push(output.clone()); + // } else if let Some(ref unique_id) = output.unique_id { + // if unique_ids.contains(unique_id) { + // tokens.push(output.clone()); + // } + // } + // } + // pos += 1; + // }, + // } + // } + } + Ok(NodeCommsResponse::FetchTokensResponse { outputs: tokens }) + }, } } diff --git a/base_layer/core/src/base_node/comms_interface/local_interface.rs b/base_layer/core/src/base_node/comms_interface/local_interface.rs index 0a270a78e2..2c55790fe4 100644 --- a/base_layer/core/src/base_node/comms_interface/local_interface.rs +++ b/base_layer/core/src/base_node/comms_interface/local_interface.rs @@ -105,6 +105,13 @@ impl LocalNodeCommsInterface { } } + pub async fn get_tokens(&mut self, asset_public_key: Vec, unique_ids: Vec>) -> Result, CommsInterfaceError> { + match self.request_sender.call(NodeCommsRequest::FetchTokens{ asset_public_key, unique_ids}).await?? { + NodeCommsResponse::FetchTokensResponse{outputs} => Ok(outputs) , + _ => Err(CommsInterfaceError::UnexpectedApiResponse) + } + } + /// Request the construction of a new mineable block template from the base node service. pub async fn get_new_block_template( &mut self, diff --git a/base_layer/core/src/base_node/proto/request.rs b/base_layer/core/src/base_node/proto/request.rs index bf766bffc2..003b7c34c2 100644 --- a/base_layer/core/src/base_node/proto/request.rs +++ b/base_layer/core/src/base_node/proto/request.rs @@ -119,6 +119,9 @@ impl From for ProtoNodeCommsRequest { }, GetNewBlock(block_template) => ProtoNodeCommsRequest::GetNewBlock(block_template.into()), FetchKernelByExcessSig(signature) => ProtoNodeCommsRequest::FetchKernelByExcessSig(signature.into()), + FetchTokens { .. } => { + unimplemented!("This should not go over the wire") + }, } } } diff --git a/base_layer/core/src/base_node/proto/response.rs b/base_layer/core/src/base_node/proto/response.rs index ef88be684f..b1eeb93ec0 100644 --- a/base_layer/core/src/base_node/proto/response.rs +++ b/base_layer/core/src/base_node/proto/response.rs @@ -127,6 +127,9 @@ impl From for ProtoNodeCommsResponse { }), TargetDifficulty(difficulty) => ProtoNodeCommsResponse::TargetDifficulty(difficulty.as_u64()), MmrNodes(added, deleted) => ProtoNodeCommsResponse::MmrNodes(ProtoMmrNodes { added, deleted }), + FetchTokensResponse { .. } => { + unimplemented!("This should not go over the wire") + }, } } } diff --git a/base_layer/core/src/base_node/state_machine_service/state_machine.rs b/base_layer/core/src/base_node/state_machine_service/state_machine.rs index ec99ccc989..81145e9749 100644 --- a/base_layer/core/src/base_node/state_machine_service/state_machine.rs +++ b/base_layer/core/src/base_node/state_machine_service/state_machine.rs @@ -184,7 +184,7 @@ impl BaseNodeStateMachine { let mut state = Starting(states::Starting); loop { if let Shutdown(reason) = &state { - debug!( + info!( target: LOG_TARGET, "Base Node state machine is shutting down because {}", reason ); diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index 861b3e8650..b97829d7c5 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -46,71 +46,32 @@ pub fn get_mainnet_genesis_block() -> ChainBlock { unimplemented!() } -pub fn get_stibbons_genesis_block() -> ChainBlock { - // lets get the block - let mut block = get_stibbons_genesis_block_raw(); - // Lets load in the stibbons faucet transactions - let mut utxos = Vec::new(); - let file = include_str!("faucets/stibbons_faucet.json"); - // last 2 lines are used for the kernel creation - let mut kernel: Option = None; - let mut counter = 1; - for line in file.lines() { - if counter < 4001 { - let utxo: TransactionOutput = serde_json::from_str(line).unwrap(); - utxos.push(utxo); - } else { - kernel = Some(serde_json::from_str(line).unwrap()); - } - counter += 1; - } - // fix headers to new mmr roots after adding utxos - block.header.output_mr = from_hex("a939fda2579fb0b6fd906111f61e37c5ea23eccd8b737eb7da517fde71a98078").unwrap(); - block.header.witness_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); - block.header.output_mmr_size += 4000; - block.header.kernel_mr = from_hex("f5e08e66e9c0e5e3818d96a694f4f6eafd689f38cea2e52e771eab2cc7a3941a").unwrap(); - block.header.kernel_mmr_size += 1; - block.body.add_outputs(&mut utxos); - block.body.add_kernels(&mut vec![kernel.unwrap()]); - let accumulated_data = BlockHeaderAccumulatedData { - hash: block.hash(), - total_kernel_offset: block.header.total_kernel_offset.clone(), - achieved_difficulty: 1.into(), - total_accumulated_difficulty: 1, - accumulated_monero_difficulty: 1.into(), - accumulated_sha_difficulty: 1.into(), - target_difficulty: 1.into(), - }; - // NOTE: Panic is impossible, accumulated_data is created from the block - ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap() -} - pub fn get_weatherwax_genesis_block() -> ChainBlock { // lets get the block - let mut block = get_weatherwax_genesis_block_raw(); + let block = get_weatherwax_genesis_block_raw(); // Lets load in the weatherwax faucet transactions - let mut utxos = Vec::new(); - let file = include_str!("faucets/weatherwax_faucet.json"); - // last 2 lines are used for the kernel creation - let mut kernel: Option = None; - let mut counter = 1; - for line in file.lines() { - if counter < 4001 { - let utxo: TransactionOutput = serde_json::from_str(line).unwrap(); - utxos.push(utxo); - } else { - kernel = Some(serde_json::from_str(line).unwrap()); - } - counter += 1; - } + // let mut utxos = Vec::new(); + // let file = include_str!("faucets/weatherwax_faucet.json"); + // // last 2 lines are used for the kernel creation + // let mut kernel: Option = None; + // let mut counter = 1; + // for line in file.lines() { + // if counter < 4001 { + // let utxo: TransactionOutput = serde_json::from_str(line).unwrap(); + // utxos.push(utxo); + // } else { + // kernel = Some(serde_json::from_str(line).unwrap()); + // } + // counter += 1; + // } // fix headers to new mmr roots after adding utxos - block.header.output_mr = from_hex("a939fda2579fb0b6fd906111f61e37c5ea23eccd8b737eb7da517fde71a98078").unwrap(); - block.header.witness_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); - block.header.output_mmr_size += 4000; - block.header.kernel_mr = from_hex("f5e08e66e9c0e5e3818d96a694f4f6eafd689f38cea2e52e771eab2cc7a3941a").unwrap(); - block.header.kernel_mmr_size += 1; - block.body.add_outputs(&mut utxos); - block.body.add_kernels(&mut vec![kernel.unwrap()]); + // block.header.output_mr = from_hex("a939fda2579fb0b6fd906111f61e37c5ea23eccd8b737eb7da517fde71a98078").unwrap(); + // block.header.witness_mr = from_hex("90a557390ce185318375546cb1244ffda3bb62274cce591880e2d012c38b1755").unwrap(); + // block.header.output_mmr_size += 4000; + // block.header.kernel_mr = from_hex("f5e08e66e9c0e5e3818d96a694f4f6eafd689f38cea2e52e771eab2cc7a3941a").unwrap(); + // block.header.kernel_mmr_size += 1; + // block.body.add_outputs(&mut utxos); + // block.body.add_kernels(&mut vec![kernel.unwrap()]); let accumulated_data = BlockHeaderAccumulatedData { hash: block.hash(), total_kernel_offset: block.header.total_kernel_offset.clone(), @@ -123,73 +84,6 @@ pub fn get_weatherwax_genesis_block() -> ChainBlock { ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap() } -#[allow(deprecated)] -pub fn get_stibbons_genesis_block_raw() -> Block { - let sig = Signature::new( - PublicKey::from_hex("f2139d1cdbcfa670bbb60d4d03d9d50b0a522e674b11280e8064f6dc30e84133").unwrap(), - PrivateKey::from_hex("3ff7522d9a744ebf99c7b6664c0e2c8c64d2a7b902a98b78964766f9f7f2b107").unwrap(), - ); - let mut body = AggregateBody::new( - vec![], - vec![TransactionOutput { - features: OutputFeatures { - flags: OutputFlags::COINBASE_OUTPUT, - maturity: 60, - }, - commitment: Commitment::from_hex( - "fadafb12de96d90042dcbf839985aadb7ae88baa3446d5c6a17937ef2b36783e", - ) - .unwrap(), - proof: BulletRangeProof::from_hex("845c947cbf23683f6ff6a56d0aa55fca14a618f7476d4e29348c5cbadf2bb062b8da701a0f058eb69c88492895c3f034db194f6d1b2d29ea83c1a68cbdd19a3f90ae080cfd0315bb20cd05a462c4e06e708b015da1d70c0f87e8c7413b579008e43a6c8dc1edb72b0b67612e897d251ec55798184ff35c80d18262e98034677b73f2dcc7ae25c9119900aadaf04a16068bf57b9e8b9bb694331750dc8acc6102b8961be183419dce2f96c48ced9892e4cdb091dcda0d6a0bb4ed94fc0c63ca065f25ce1e560504d49970bcaac007f33368f15ffa0dd3f56bf799b66fa684fe0fbeb882aee4a6fe05a3ca7c488a6ba22779a42f0f5d875175b8ebc517dd49df20b4f04f027b7d22b7c62cb93727f35c18a0b776d95fac4ff5405d6ed3dbb7613152178cecea4b712aa6e6701804ded71d94cf67de2e86ae401499b39de81b7344185c9eb3bd570ac6121143a690f118d9413abb894729b6b3e057f4771b2c2204285151a56695257992f2b0331f27066270718b37ab472c339d2560c1f6559f3c4ce31ec7f7e2acdbebb1715951d8177283a1ccc2f393ce292956de5db4afde419c0264d5cc4758e6e2c07b730ad43819f3761658d63794cc8071b30f9d7cd622bece4f086b0ca6a04fee888856084543a99848f06334acf48cace58e5ef8c85412017c400b4ec92481ba6d745915aef40531db73d1d84d07d7fce25737629e0fc4ee71e7d505bfd382e362cd1ac03a67c93b8f20cb4285ce240cf1e000d48332ba32e713d6cdf6266449a0a156241f7b1b36753f46f1ecb8b1836625508c5f31bc7ebc1d7cd634272be02cc109bf86983a0591bf00bacea1287233fc12324846398be07d44e8e14bd78cd548415f6de60b5a0c43a84ac29f6a8ac0b1b748dd07a8a4124625e1055b5f5b19da79c319b6e465ca5df0eb70cb4e3dc399891ce90b").unwrap(), - // For genesis block: A default script can never be spent, intentionally - script: TariScript::default(), - // Script offset never checked for coinbase, thus can use default - sender_offset_public_key: Default::default(), - // For genesis block: Metadata signature will never be checked - metadata_signature: Default::default(), - }], - vec![TransactionKernel { - features: KernelFeatures::COINBASE_KERNEL, - fee: MicroTari(0), - lock_height: 0, - excess: Commitment::from_hex( - "f472cc347a1006b7390f9c93b3c62fba334fd99f6c9c1daf9302646cd4781f61", - ) - .unwrap(), - excess_sig: sig, - }], - ); - body.sort(); - Block { - header: BlockHeader { - version: 0, - height: 0, - prev_hash: vec![0; BLOCK_HASH_LENGTH], - timestamp: 1_611_835_200.into(), // 28/01/2021 @ 12:00pm (UTC) - output_mr: from_hex("dcc44f39b65e5e1e526887e7d56f7b85e2ea44bd29bc5bc195e6e015d19e1c06").unwrap(), - witness_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(), - output_mmr_size: 1, - kernel_mr: from_hex("589bc62ac5d9139f921c68b8075c32d8d130024acaf3196d1d6a89df601e2bcf").unwrap(), - input_mr: vec![0; BLOCK_HASH_LENGTH], - kernel_mmr_size: 1, - total_kernel_offset: PrivateKey::from_hex( - "0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - total_script_offset: PrivateKey::from_hex( - "0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - nonce: 0, - pow: ProofOfWork { - pow_algo: PowAlgorithm::Sha3, - pow_data: vec![], - }, - }, - body, - } -} - #[allow(deprecated)] pub fn get_weatherwax_genesis_block_raw() -> Block { let sig = Signature::new( @@ -199,10 +93,7 @@ pub fn get_weatherwax_genesis_block_raw() -> Block { let mut body = AggregateBody::new( vec![], vec![TransactionOutput { - features: OutputFeatures { - flags: OutputFlags::COINBASE_OUTPUT, - maturity: 60, - }, + features: OutputFeatures::create_coinbase(60), commitment: Commitment::from_hex( "fadafb12de96d90042dcbf839985aadb7ae88baa3446d5c6a17937ef2b36783e", ) @@ -214,6 +105,8 @@ pub fn get_weatherwax_genesis_block_raw() -> Block { sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked metadata_signature: Default::default(), + unique_id: None, + parent_public_key: None }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, @@ -309,10 +202,7 @@ pub fn get_ridcully_genesis_block_raw() -> Block { let mut body = AggregateBody::new( vec![], vec![TransactionOutput { - features: OutputFeatures { - flags: OutputFlags::COINBASE_OUTPUT, - maturity: 60, - }, + features: OutputFeatures::create_coinbase(60), commitment: Commitment::from_hex( "fadafb12de96d90042dcbf839985aadb7ae88baa3446d5c6a17937ef2b36783e", ) @@ -323,7 +213,9 @@ pub fn get_ridcully_genesis_block_raw() -> Block { // Script offset never checked for coinbase, thus can use default sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked - metadata_signature: Default::default() + metadata_signature: Default::default(), + unique_id: None, + parent_public_key: None }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, @@ -395,6 +287,7 @@ pub fn get_igor_genesis_block_raw() -> Block { features: OutputFeatures { flags: OutputFlags::COINBASE_OUTPUT, maturity: 60, + .. Default::default() }, commitment: Commitment::from_hex( "fadafb12de96d90042dcbf839985aadb7ae88baa3446d5c6a17937ef2b36783e", @@ -407,6 +300,8 @@ pub fn get_igor_genesis_block_raw() -> Block { sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked metadata_signature: Default::default(), + unique_id: None, + parent_public_key: None }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 85e5027479..4c607a5119 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -221,7 +221,7 @@ impl ConsensusConstants { // seting sha3/monero to 40/60 split algos.insert(PowAlgorithm::Sha3, PowAlgorithmConstants { max_target_time: 1800, - min_difficulty: 60_000_000.into(), + min_difficulty: 60_00.into(), max_difficulty: u64::MAX.into(), target_time: 300, }); @@ -266,7 +266,7 @@ impl ConsensusConstants { // }); algos.insert(PowAlgorithm::Sha3, PowAlgorithmConstants { max_target_time: 1800, - min_difficulty: 60_000_000.into(), + min_difficulty: 60_00.into(), max_difficulty: 60_000_000.into(), target_time: 300, }); @@ -328,10 +328,10 @@ impl ConsensusConstants { let mut algos = HashMap::new(); // seting sha3/monero to 40/60 split algos.insert(PowAlgorithm::Sha3, PowAlgorithmConstants { - max_target_time: 1800, - min_difficulty: 60_000_000.into(), + max_target_time: 180, + min_difficulty: 60_00.into(), max_difficulty: u64::MAX.into(), - target_time: 300, + target_time: 30, }); algos.insert(PowAlgorithm::Monero, PowAlgorithmConstants { max_target_time: 1200, @@ -394,13 +394,13 @@ impl ConsensusConstants { let mut algos = HashMap::new(); algos.insert(PowAlgorithm::Sha3, PowAlgorithmConstants { max_target_time: 1800, - min_difficulty: 60_000_000.into(), + min_difficulty: 40_000.into(), max_difficulty: u64::MAX.into(), target_time: 300, }); algos.insert(PowAlgorithm::Monero, PowAlgorithmConstants { max_target_time: 800, - min_difficulty: 60_000_000.into(), + min_difficulty: 70_000_000.into(), max_difficulty: u64::MAX.into(), target_time: 200, }); diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs index 5ca1610e03..984a88a430 100644 --- a/base_layer/core/src/consensus/consensus_manager.rs +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -26,7 +26,6 @@ use crate::{ get_igor_genesis_block, get_mainnet_genesis_block, get_ridcully_genesis_block, - get_stibbons_genesis_block, get_weatherwax_genesis_block, }, Block, @@ -76,7 +75,7 @@ impl ConsensusManager { match self.inner.network.as_network() { Network::MainNet => get_mainnet_genesis_block(), Network::Ridcully => get_ridcully_genesis_block(), - Network::Stibbons => get_stibbons_genesis_block(), + Network::Stibbons => unimplemented!(), Network::Weatherwax => get_weatherwax_genesis_block(), Network::LocalNet => self .inner diff --git a/base_layer/core/src/proto/transaction.proto b/base_layer/core/src/proto/transaction.proto index 08a1e9775d..aa31f92cc1 100644 --- a/base_layer/core/src/proto/transaction.proto +++ b/base_layer/core/src/proto/transaction.proto @@ -60,6 +60,10 @@ message TransactionOutput { bytes sender_offset_public_key = 5; // UTXO signature with the script offset private key, k_O ComSignature metadata_signature = 6; + // A unique id field that can be used to ensure there is only one of a specific NFT or token in the Unspent set at a time + bytes unique_id = 7; + + bytes parent_public_key = 8; } // Options for UTXO's @@ -69,6 +73,20 @@ message OutputFeatures { // The maturity of the specific UTXO. This is the min lock height at which an UTXO can be spend. Coinbase UTXO // require a min maturity of the Coinbase_lock_height, this should be checked on receiving new blocks. uint64 maturity = 2; + + bytes metadata = 3; + + AssetOutputFeatures asset = 16; + MintNonFungibleFeatures mint_non_fungible = 17; +} + +message AssetOutputFeatures { + bytes public_key = 1; +} + +message MintNonFungibleFeatures { + bytes asset_public_key = 1; + Commitment asset_owner_commitment = 2; } // The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index d500157360..ba26443dc0 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -29,7 +29,9 @@ use crate::{ aggregated_body::AggregateBody, tari_amount::MicroTari, transaction::{ + AssetOutputFeatures, KernelFeatures, + MintNonFungibleFeatures, OutputFeatures, OutputFlags, Transaction, @@ -167,6 +169,17 @@ impl TryFrom for TransactionOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; + let unique_id = if output.unique_id.is_empty() { + None + } else { + Some(output.unique_id.clone()) + }; + + let parent_public_key = if output.parent_public_key.is_empty() { + None } + else { + Some(PublicKey::from_bytes(output.parent_public_key.as_bytes()).map_err(| err | format!("{:?}", err))?) }; + Ok(Self { features, commitment, @@ -174,6 +187,8 @@ impl TryFrom for TransactionOutput { script, sender_offset_public_key, metadata_signature, + unique_id, + parent_public_key }) } } @@ -187,6 +202,8 @@ impl From for proto::types::TransactionOutput { script: output.script.as_bytes(), sender_offset_public_key: output.sender_offset_public_key.as_bytes().to_vec(), metadata_signature: Some(output.metadata_signature.into()), + unique_id: output.unique_id.unwrap_or_default(), + parent_public_key: output.parent_public_key.map(|pp| pp.as_bytes().to_vec()).unwrap_or_default() } } } @@ -201,6 +218,15 @@ impl TryFrom for OutputFeatures { flags: OutputFlags::from_bits(features.flags as u8) .ok_or_else(|| "Invalid or unrecognised output flags".to_string())?, maturity: features.maturity, + metadata: features.metadata, + asset: match features.asset { + Some(a) => Some(a.try_into()?), + None => None, + }, + mint_non_fungible: match features.mint_non_fungible { + Some(m) => Some(m.try_into()?), + None => None, + }, }) } } @@ -210,6 +236,54 @@ impl From for proto::types::OutputFeatures { Self { flags: features.flags.bits() as u32, maturity: features.maturity, + metadata: features.metadata, + asset: features.asset.map(|a| a.into()), + mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), + } + } +} + +impl TryFrom for AssetOutputFeatures { + type Error = String; + + fn try_from(features: proto::types::AssetOutputFeatures) -> Result { + let public_key = PublicKey::from_bytes(features.public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; + + Ok(Self { public_key }) + } +} + +impl From for proto::types::AssetOutputFeatures { + fn from(features: AssetOutputFeatures) -> Self { + Self { + public_key: features.public_key.as_bytes().to_vec(), + } + } +} + +impl TryFrom for MintNonFungibleFeatures { + type Error = String; + + fn try_from(value: proto::types::MintNonFungibleFeatures) -> Result { + let asset_public_key = + PublicKey::from_bytes(value.asset_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; + + let asset_owner_commitment = value + .asset_owner_commitment + + .map(|c| Commitment::from_bytes(&c.data)) .ok_or_else(|| "asset_owner_commitment is missing".to_string())?.map_err(|err| err.to_string())?; + Ok(Self { + asset_public_key, + asset_owner_commitment, + }) + } +} + +impl From for proto::types::MintNonFungibleFeatures { + fn from(value: MintNonFungibleFeatures) -> Self { + Self { + asset_public_key: value.asset_public_key.as_bytes().to_vec(), + asset_owner_commitment: Some(value.asset_owner_commitment.into()), } } } diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index 5091844e6a..7e2612ed53 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -208,14 +208,17 @@ impl CoinbaseBuilder { script_private_key, sender_offset_public_key, metadata_sig, + None, + None ); + // TODO: Verify bullet proof? let output = if let Some(rewind_data) = self.rewind_data.as_ref() { unblinded_output - .as_rewindable_transaction_output(&self.factories, rewind_data) + .as_rewindable_transaction_output(&self.factories, rewind_data, false) .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))? } else { unblinded_output - .as_transaction_output(&self.factories) + .as_transaction_output(&self.factories, false) .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))? }; let kernel = KernelBuilder::new() diff --git a/base_layer/core/src/transactions/helpers.rs b/base_layer/core/src/transactions/helpers.rs index 54f90cebb8..c3e5907adc 100644 --- a/base_layer/core/src/transactions/helpers.rs +++ b/base_layer/core/src/transactions/helpers.rs @@ -147,6 +147,8 @@ impl TestParams { self.script_private_key.clone(), self.sender_offset_public_key.clone(), metadata_signature, + None, + None, ) } @@ -506,6 +508,8 @@ pub fn spend_utxos(schema: TransactionSchema) -> (Transaction, Vec, + pub asset: Option, + pub mint_non_fungible: Option } impl OutputFeatures { @@ -117,6 +132,7 @@ impl OutputFeatures { OutputFeatures { flags: OutputFlags::COINBASE_OUTPUT, maturity: maturity_height, + ..Default::default() } } @@ -124,7 +140,39 @@ impl OutputFeatures { pub fn with_maturity(maturity: u64) -> OutputFeatures { OutputFeatures { maturity, - ..OutputFeatures::default() + ..Default::default() + } + } + + pub fn custom(flags: OutputFlags, metadata: Vec) -> OutputFeatures { + Self { + flags, + metadata, + ..Default::default() + } + } + + pub fn for_asset_registration(metadata: Vec, public_key: PublicKey) -> OutputFeatures { + Self{ + flags: OutputFlags::ASSET_REGISTRATION, + maturity: 0, + metadata, + asset: Some(AssetOutputFeatures{ + public_key + }), + ..Default::default() + } + } + + pub fn for_minting(metadata: Vec, asset_public_key: PublicKey, asset_owner_commitment: Commitment)-> OutputFeatures { + Self { + flags: OutputFlags::MINT_NON_FUNGIBLE, + metadata, + mint_non_fungible: Some(MintNonFungibleFeatures{ + asset_public_key, + asset_owner_commitment + }), + .. Default::default() } } } @@ -134,6 +182,10 @@ impl Default for OutputFeatures { OutputFeatures { flags: OutputFlags::empty(), maturity: 0, + metadata: vec![], + asset: None, + mint_non_fungible: None + } } } @@ -165,6 +217,10 @@ bitflags! { pub struct OutputFlags: u8 { /// Output is a coinbase output, must not be spent until maturity const COINBASE_OUTPUT = 0b0000_0001; + const NON_FUNGIBLE = 0b0000_1000; + // TODO: separate these flags + const ASSET_REGISTRATION = 0b0000_1010; // Registration and also non-fungible + const MINT_NON_FUNGIBLE = 0b0000_1100; // Mint and non-fungible } } @@ -205,6 +261,112 @@ pub enum TransactionError { } //----------------------------------------- UnblindedOutput ----------------------------------------------------// +#[derive(Debug, Clone)] +pub struct UnblindedOutputBuilder { + value: MicroTari, + spending_key: BlindingFactor, + features: OutputFeatures, + script: Option, + input_data: Option, + script_private_key: Option, + sender_offset_public_key: Option, + metadata_signature: Option, + metadata_signed_by_receiver: bool, + metadata_signed_by_sender: bool, + unique_id: Option>, + parent_public_key: Option +} + + +impl UnblindedOutputBuilder { + + pub fn new(value: MicroTari, spending_key: BlindingFactor) -> Self { + Self{ + value, spending_key, + features: OutputFeatures::default(), + script: None, + input_data: None, + script_private_key: None, + sender_offset_public_key: None, + metadata_signature: None, + metadata_signed_by_receiver: false, + metadata_signed_by_sender: false, + unique_id: None, + parent_public_key: None + } + } + + pub fn sign_as_receiver(&mut self, sender_offset_public_key: PublicKey, public_nonce_commitment: PublicKey) -> Result<(), TransactionError> { + self.sender_offset_public_key = Some(sender_offset_public_key.clone()); + + let metadata_partial = TransactionOutput::create_partial_metadata_signature(&self.value, &self.spending_key, + self.script.as_ref().ok_or_else(||TransactionError::ValidationError("script must be set".to_string()))?, + &self.features, + &sender_offset_public_key, + &public_nonce_commitment + )?; + self.metadata_signature = Some(metadata_partial); + self.metadata_signed_by_receiver = true; + Ok(()) + } + + pub fn sign_as_sender(&mut self, sender_offset_private_key: &PrivateKey) -> Result<(), TransactionError> { + let metadata_sig = TransactionOutput::create_final_metadata_signature(&self.value,&self.spending_key, self.script.as_ref().ok_or_else(||TransactionError::ValidationError("script must be set".to_string()))?, + &self.features, + &sender_offset_private_key + )?; + self.metadata_signature = Some(metadata_sig); + self.metadata_signed_by_sender = true; + Ok(()) + } + + pub fn try_build(self) -> Result { + if !self.metadata_signed_by_receiver { + return Err(TransactionError::ValidationError("Cannot build output because it has not been signed by the receiver".to_string())); + } + if !self.metadata_signed_by_sender { + return Err(TransactionError::ValidationError("Cannot build output because it has not been signed by the sender".to_string())); + } + let ub = UnblindedOutput{ + value: self.value, + spending_key: self.spending_key, + features:self.features, + script:self.script.ok_or_else(||TransactionError::ValidationError("script must be set".to_string()))?, + input_data: self.input_data.ok_or_else(||TransactionError::ValidationError("input_data must be set".to_string()))?, + script_private_key: self.script_private_key.ok_or_else(||TransactionError::ValidationError("script_private_key must be set".to_string()))?, + sender_offset_public_key: self.sender_offset_public_key.ok_or_else(||TransactionError::ValidationError("sender_offset_public_key must be set".to_string()))?, + metadata_signature: self.metadata_signature.ok_or_else(||TransactionError::ValidationError("metadata_signature must be set".to_string()))?, + unique_id: self.unique_id, + parent_public_key: self.parent_public_key + }; + Ok(ub) + } + pub fn with_features(mut self, features: OutputFeatures) -> Self { + self.features = features; + self + } + pub fn with_script(mut self, script: TariScript) -> Self { + self.script =Some( script); + self + } + pub fn with_input_data(mut self, input_data: ExecutionStack) -> Self { + self.input_data =Some( input_data); + self + } + pub fn with_script_private_key(mut self, script_private_key: PrivateKey) -> Self { + self.script_private_key =Some( script_private_key); + self + } + pub fn with_unique_id(mut self, unique_id: Option>)-> Self { + self.unique_id = unique_id; + self + } + pub fn with_parent_public_key(mut self, parent_public_key: Option)-> Self { + self.parent_public_key = parent_public_key; + self + } +} + /// An unblinded output is one where the value and spending key (blinding factor) are known. This can be used to /// build both inputs and outputs (every input comes from an output) @@ -219,6 +381,8 @@ pub struct UnblindedOutput { pub script_private_key: PrivateKey, pub sender_offset_public_key: PublicKey, pub metadata_signature: ComSignature, + pub unique_id: Option>, + pub parent_public_key: Option } impl UnblindedOutput { @@ -233,6 +397,8 @@ impl UnblindedOutput { script_private_key: PrivateKey, sender_offset_public_key: PublicKey, metadata_signature: ComSignature, + unique_id: Option>, + parent_public_key: Option ) -> UnblindedOutput { UnblindedOutput { value, @@ -243,6 +409,8 @@ impl UnblindedOutput { script_private_key, sender_offset_public_key, metadata_signature, + unique_id, + parent_public_key } } @@ -280,7 +448,7 @@ impl UnblindedOutput { }) } - pub fn as_transaction_output(&self, factories: &CryptoFactories) -> Result { + pub fn as_transaction_output(&self, factories: &CryptoFactories, verify_proof: bool) -> Result { let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); let output = TransactionOutput { features: self.features.clone(), @@ -294,9 +462,11 @@ impl UnblindedOutput { script: self.script.clone(), sender_offset_public_key: self.sender_offset_public_key.clone(), metadata_signature: self.metadata_signature.clone(), + unique_id: self.unique_id.clone(), + parent_public_key: self.parent_public_key.clone() }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. - if !output.verify_range_proof(&factories.range_proof)? { + if verify_proof && !output.verify_range_proof(&factories.range_proof)? { return Err(TransactionError::ValidationError( "Range proof could not be verified".into(), )); @@ -308,6 +478,7 @@ impl UnblindedOutput { &self, factories: &CryptoFactories, rewind_data: &RewindData, + verify_proof: bool ) -> Result { let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); @@ -329,9 +500,11 @@ impl UnblindedOutput { script: self.script.clone(), sender_offset_public_key: self.sender_offset_public_key.clone(), metadata_signature: self.metadata_signature.clone(), + unique_id: self.unique_id.clone(), + parent_public_key: self.parent_public_key.clone() }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. - if !output.verify_range_proof(&factories.range_proof)? { + if verify_proof && !output.verify_range_proof(&factories.range_proof)? { return Err(TransactionError::ValidationError( "Range proof could not be verified".into(), )); @@ -558,6 +731,10 @@ pub struct TransactionOutput { pub sender_offset_public_key: PublicKey, /// UTXO signature with the script offset private key, k_O pub metadata_signature: ComSignature, + /// Unique id. There can only be one UTXO at a time in the unspent set with this id + pub unique_id: Option>, + /// Public key if this has a parent (e.g. tokens or sub assets) + pub parent_public_key: Option, } /// An output for a transaction, includes a range proof and Tari script metadata @@ -570,6 +747,8 @@ impl TransactionOutput { script: TariScript, sender_offset_public_key: PublicKey, metadata_signature: ComSignature, + unique_id: Option>, + parent_public_key: Option ) -> TransactionOutput { TransactionOutput { features, @@ -578,6 +757,8 @@ impl TransactionOutput { script, sender_offset_public_key, metadata_signature, + unique_id, + parent_public_key } } @@ -800,18 +981,17 @@ impl Hashable for TransactionOutput { } } -impl Default for TransactionOutput { - fn default() -> Self { - TransactionOutput::new( - OutputFeatures::default(), - CommitmentFactory::default().zero(), - RangeProof::default(), - TariScript::default(), - PublicKey::default(), - ComSignature::default(), - ) - } -} +// impl Default for TransactionOutput { +// fn default() -> Self { +// TransactionOutput::new( +// OutputFeatures::default(), +// CommitmentFactory::default().zero(), +// RangeProof::default(), +// TariScript::default(), +// PublicKey::default(), +// ) +// } +// } impl Display for TransactionOutput { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { diff --git a/base_layer/core/src/transactions/transaction_protocol/mod.rs b/base_layer/core/src/transactions/transaction_protocol/mod.rs index 5e140c4388..462a2fdd08 100644 --- a/base_layer/core/src/transactions/transaction_protocol/mod.rs +++ b/base_layer/core/src/transactions/transaction_protocol/mod.rs @@ -84,7 +84,9 @@ pub mod proto; pub mod recipient; pub mod sender; pub mod single_receiver; -pub mod transaction_initializer; +pub mod sender_transaction_protocol_builder; +mod tx_id; +pub use tx_id::*; use crate::transactions::{tari_amount::*, transaction::TransactionError}; use digest::Digest; diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs index c149874ef3..faa98533ff 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs @@ -45,7 +45,7 @@ impl TryFrom for RecipientSignedMessage { .map_err(|err| format!("{}", err))?; Ok(Self { - tx_id: message.tx_id, + tx_id: message.tx_id.into(), output, public_spend_key, partial_signature, @@ -56,7 +56,7 @@ impl TryFrom for RecipientSignedMessage { impl From for proto::RecipientSignedMessage { fn from(message: RecipientSignedMessage) -> Self { Self { - tx_id: message.tx_id, + tx_id: message.tx_id.into(), output: Some(message.output.into()), public_spend_key: message.public_spend_key.to_vec(), partial_signature: Some(message.partial_signature.into()), diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto index 76ae4ca715..eaa4625bd7 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto @@ -27,6 +27,8 @@ message SingleRoundSenderData { bytes public_commitment_nonce = 9; // Output features tari.types.OutputFeatures features = 10; + // Unique id for NFTs + bytes unique_id = 11; } message TransactionSenderMessage { diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs index 14c0f7ee2c..75083be7b4 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs @@ -106,8 +106,9 @@ impl TryFrom for SingleRoundSenderData { .map(TryInto::try_into) .ok_or_else(|| "Transaction output features not provided".to_string())??; + let unique_id = if data.unique_id.is_empty() { None} else {Some(data.unique_id.clone())}; Ok(Self { - tx_id: data.tx_id, + tx_id: data.tx_id.into(), amount: data.amount.into(), public_excess, public_nonce, @@ -117,6 +118,7 @@ impl TryFrom for SingleRoundSenderData { script: TariScript::from_bytes(&data.script).map_err(|err| err.to_string())?, sender_offset_public_key, public_commitment_nonce, + unique_id }) } } @@ -124,7 +126,7 @@ impl TryFrom for SingleRoundSenderData { impl From for proto::SingleRoundSenderData { fn from(sender_data: SingleRoundSenderData) -> Self { Self { - tx_id: sender_data.tx_id, + tx_id: sender_data.tx_id.into(), // The amount, in µT, being sent to the recipient amount: sender_data.amount.into(), // The offset public excess for this transaction @@ -136,6 +138,7 @@ impl From for proto::SingleRoundSenderData { script: sender_data.script.as_bytes(), sender_offset_public_key: sender_data.sender_offset_public_key.to_vec(), public_commitment_nonce: sender_data.public_commitment_nonce.to_vec(), + unique_id: sender_data.unique_id.unwrap_or_default() } } } diff --git a/base_layer/core/src/transactions/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs index 2518f8f1de..890749f77b 100644 --- a/base_layer/core/src/transactions/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -35,6 +35,7 @@ use crate::transactions::{ }, }; use tari_common_types::types::{MessageHash, PrivateKey, PublicKey, Signature}; +use crate::transactions::transaction_protocol::TxId; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[allow(clippy::large_enum_variant)] @@ -80,7 +81,7 @@ pub(super) struct MultiRecipientInfo { /// This is the message containing the public data that the Receiver will send back to the Sender #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct RecipientSignedMessage { - pub tx_id: u64, + pub tx_id: TxId, pub output: TransactionOutput, pub public_spend_key: PublicKey, pub partial_signature: Signature, diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index c91097d32a..294d846af0 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -50,12 +50,13 @@ use crate::transactions::{ transaction_protocol::{ build_challenge, recipient::{RecipientInfo, RecipientSignedMessage}, - transaction_initializer::SenderTransactionInitializer, + sender_transaction_protocol_builder::SenderTransactionProtocolBuilder, TransactionMetadata, TransactionProtocolError as TPE, }, }; use tari_common_types::types::{BlindingFactor, ComSignature, PrivateKey, PublicKey, RangeProofService, Signature}; +use crate::transactions::transaction_protocol::TxId; //---------------------------------------- Local Data types ----------------------------------------------------// @@ -68,7 +69,7 @@ pub(super) struct RawTransactionInfo { pub num_recipients: usize, // The sum of self-created outputs plus change pub amount_to_self: MicroTari, - pub tx_id: u64, + pub tx_id: TxId, pub amounts: Vec, pub recipient_scripts: Vec, pub recipient_output_features: Vec, @@ -97,12 +98,13 @@ pub(super) struct RawTransactionInfo { pub recipient_info: RecipientInfo, pub signatures: Vec, pub message: String, + pub unique_id: Option> } #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct SingleRoundSenderData { /// The transaction id for the recipient - pub tx_id: u64, + pub tx_id: TxId, /// The amount, in µT, being sent to the recipient pub amount: MicroTari, /// The offset public excess for this transaction @@ -121,6 +123,8 @@ pub struct SingleRoundSenderData { pub sender_offset_public_key: PublicKey, /// The sender's portion of the public commitment nonce pub public_commitment_nonce: PublicKey, + /// Unique id on the blockchain, if present + pub unique_id: Option> } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -153,8 +157,8 @@ pub struct SenderTransactionProtocol { impl SenderTransactionProtocol { /// Begin constructing a new transaction. All the up-front data is collected via the `SenderTransactionInitializer` /// builder function - pub fn builder(num_recipients: usize) -> SenderTransactionInitializer { - SenderTransactionInitializer::new(num_recipients) + pub fn builder(num_recipients: usize) -> SenderTransactionProtocolBuilder { + SenderTransactionProtocolBuilder::new(num_recipients) } /// Convenience method to check whether we're receiving recipient data @@ -208,7 +212,7 @@ impl SenderTransactionProtocol { } /// Method to check if the provided tx_id matches this transaction - pub fn check_tx_id(&self, tx_id: u64) -> bool { + pub fn check_tx_id(&self, tx_id: TxId) -> bool { match &self.state { SenderState::Finalizing(info) | SenderState::SingleRoundMessageReady(info) | @@ -217,7 +221,7 @@ impl SenderTransactionProtocol { } } - pub fn get_tx_id(&self) -> Result { + pub fn get_tx_id(&self) -> Result { match &self.state { SenderState::Finalizing(info) | SenderState::SingleRoundMessageReady(info) | @@ -370,6 +374,7 @@ impl SenderTransactionProtocol { script: recipient_script, sender_offset_public_key: PublicKey::from_secret_key(recipient_script_offset_secret_key), public_commitment_nonce: PublicKey::from_secret_key(&private_commitment_nonce), + unique_id: info.unique_id.clone() }) }, _ => Err(TPE::InvalidStateError), @@ -618,14 +623,14 @@ impl fmt::Display for SenderTransactionProtocol { } } -pub fn calculate_tx_id(pub_nonce: &PublicKey, index: usize) -> u64 { +pub fn calculate_tx_id(pub_nonce: &PublicKey, index: usize) -> TxId { let hash = D::new() .chain(pub_nonce.as_bytes()) .chain(index.to_le_bytes()) .finalize(); let mut bytes: [u8; 8] = [0u8; 8]; bytes.copy_from_slice(&hash[..8]); - u64::from_le_bytes(bytes) + u64::from_le_bytes(bytes).into() } //---------------------------------------- Sender State ----------------------------------------------------// diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs similarity index 97% rename from base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs rename to base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs index b27a1e528f..c180817222 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs @@ -54,6 +54,7 @@ use crate::transactions::{ sender::{calculate_tx_id, RawTransactionInfo, SenderState, SenderTransactionProtocol}, RewindData, TransactionMetadata, + TxId, }, }; use tari_common_types::types::{BlindingFactor, PrivateKey, PublicKey}; @@ -69,7 +70,7 @@ pub const LOG_TARGET: &str = "c::tx::tx_protocol::tx_initializer"; /// which returns an instance of this builder. Once all the sender's information has been added via the builder /// methods, you can call `build()` which will return a #[derive(Debug, Clone)] -pub struct SenderTransactionInitializer { +pub struct SenderTransactionProtocolBuilder { num_recipients: usize, amounts: FixedSet, lock_height: Option, @@ -93,11 +94,12 @@ pub struct SenderTransactionInitializer { recipient_scripts: FixedSet, recipient_sender_offset_private_keys: FixedSet, private_commitment_nonces: FixedSet, - tx_id: Option, + unique_id: Option>, + tx_id: Option, } pub struct BuildError { - pub builder: SenderTransactionInitializer, + pub builder: SenderTransactionProtocolBuilder, pub message: String, } @@ -107,7 +109,7 @@ impl Debug for BuildError { } } -impl SenderTransactionInitializer { +impl SenderTransactionProtocolBuilder { pub fn new(num_recipients: usize) -> Self { Self { num_recipients, @@ -133,6 +135,7 @@ impl SenderTransactionInitializer { recipient_scripts: FixedSet::new(num_recipients), recipient_sender_offset_private_keys: FixedSet::new(num_recipients), private_commitment_nonces: FixedSet::new(num_recipients), + unique_id: None, tx_id: None, } } @@ -290,7 +293,7 @@ impl SenderTransactionInitializer { let change_amount = v.checked_sub(extra_fee); let change_sender_offset_private_key = PrivateKey::random(&mut OsRng); self.change_sender_offset_private_key = Some(change_sender_offset_private_key.clone()); - + // TODO: Add unique id if needed match change_amount { // You can't win. Just add the change to the fee (which is less than the cost of adding another // output and go without a change output @@ -330,6 +333,8 @@ impl SenderTransactionInitializer { .clone(), PublicKey::from_secret_key(&change_sender_offset_private_key), metadata_signature, + None, + None, ); Ok((fee_with_change, v, Some(change_unblinded_output))) }, @@ -339,7 +344,7 @@ impl SenderTransactionInitializer { } /// Specify the tx_id of this transaction, if not provided it will be calculated on build - pub fn with_tx_id(&mut self, tx_id: u64) -> &mut Self { + pub fn with_tx_id(&mut self, tx_id: TxId) -> &mut Self { self.tx_id = Some(tx_id); self } @@ -361,6 +366,11 @@ impl SenderTransactionInitializer { self.amounts.clone().into_vec().iter().sum() } + pub fn with_unique_id(mut self, unique_id: Vec) -> Self { + self.unique_id = Some(unique_id); + self + } + /// Construct a `SenderTransactionProtocol` instance in and appropriate state. The data stored /// in the struct is _moved_ into the new struct. If any data is missing, the `self` instance is returned in the /// error (so that you can continue building) along with a string listing the missing fields. @@ -415,15 +425,19 @@ impl SenderTransactionInitializer { if total_fee < MINIMUM_TRANSACTION_FEE { return self.build_err("Fee is less than the minimum"); } + // Create transaction outputs + let mut outputs = match self .sender_custom_outputs .iter() .map(|o| { if let Some(rewind_data) = self.rewind_data.as_ref() { - o.as_rewindable_transaction_output(factories, rewind_data) + // TODO: Should proof be verified? + o.as_rewindable_transaction_output(factories, rewind_data, false) } else { - o.as_transaction_output(factories) + // TODO: Should proof be verified + o.as_transaction_output(factories, false) } }) .collect::, _>>() @@ -444,14 +458,16 @@ impl SenderTransactionInitializer { // If rewind data is present we produce a rewindable output, else a standard output let change_output = if let Some(rewind_data) = self.rewind_data.as_ref() { - match change_unblinded_output.as_rewindable_transaction_output(factories, rewind_data) { + // TODO: Should proof be verified? + match change_unblinded_output.as_rewindable_transaction_output(factories, rewind_data, false) { Ok(o) => o, Err(e) => { return self.build_err(e.to_string().as_str()); }, } } else { - match change_unblinded_output.as_transaction_output(factories) { + // TODO: Should proof be verified? + match change_unblinded_output.as_transaction_output(factories, false) { Ok(o) => o, Err(e) => { return self.build_err(e.to_string().as_str()); @@ -560,6 +576,7 @@ impl SenderTransactionInitializer { recipient_info, signatures: Vec::new(), message: self.message.unwrap_or_else(|| "".to_string()), + unique_id: self.unique_id, }; let state = SenderState::Initializing(Box::new(sender_info)); @@ -592,7 +609,7 @@ mod test { transaction::{OutputFeatures, MAX_TRANSACTION_INPUTS}, transaction_protocol::{ sender::SenderState, - transaction_initializer::SenderTransactionInitializer, + sender_transaction_protocol_builder::SenderTransactionInitializer, TransactionProtocolError, }, }, diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 7f6060fed6..609e91e66c 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -128,6 +128,8 @@ impl SingleReceiverTransactionProtocol { sender_info.script.clone(), sender_info.sender_offset_public_key.clone(), partial_metadata_signature, + None, + None ); Ok(output) } diff --git a/base_layer/core/src/transactions/transaction_protocol/tx_id.rs b/base_layer/core/src/transactions/transaction_protocol/tx_id.rs new file mode 100644 index 0000000000..90eb1c603c --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/tx_id.rs @@ -0,0 +1,77 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt; +use std::fmt::Formatter; +use serde::{Serialize, Deserialize}; +use std::hash::{Hash, Hasher}; +use rand::rngs::OsRng; +use rand::RngCore; + + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)] +pub struct TxId(u64); + +impl TxId { + pub fn new_random() -> Self { + TxId(OsRng.next_u64()) + } + + pub fn as_u64(self) -> u64 { + self.0 + } +} + +impl Hash for TxId { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl PartialEq for TxId { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + + +impl Eq for TxId { + +} + +impl From for TxId { + fn from(s: u64) -> Self { + Self(s) + } +} + +impl From for u64 { + fn from(s: TxId) ->Self { + s.0 + } +} + +impl fmt::Display for TxId { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/base_layer/p2p/src/proto/message_type.proto b/base_layer/p2p/src/proto/message_type.proto index e1d0339f7f..d5fcfd8f93 100644 --- a/base_layer/p2p/src/proto/message_type.proto +++ b/base_layer/p2p/src/proto/message_type.proto @@ -24,9 +24,10 @@ enum TariMessageType { TariMessageTypeTransactionFinalized = 73; TariMessageTypeTransactionCancelled = 74; // -- DAN Messages -- - + TariMessageTypeDanConsensusMessage = 101; // -- Extended -- TariMessageTypeText = 225; TariMessageTypeTextAck = 226; + } diff --git a/base_layer/wallet/README.md b/base_layer/wallet/README.md index 8448d4d4b0..f7f477a733 100644 --- a/base_layer/wallet/README.md +++ b/base_layer/wallet/README.md @@ -11,3 +11,7 @@ See README.md in wallet_ffi crate ## Setup (Windows) See README.md in wallet_ffi crate + + +## Running migrations: +`diesel migration run --database-url test.sqlite3` diff --git a/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/down.sql b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql new file mode 100644 index 0000000000..2e84723709 --- /dev/null +++ b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql @@ -0,0 +1,9 @@ +alter table outbound_transactions add unique_id blob; +alter table completed_transactions add unique_id blob; +alter table outputs add unique_id blob; +alter table outputs add metadata blob; +alter table outputs add features_asset_public_key blob; +alter table outputs add features_mint_asset_public_key blob; +alter table outputs add features_mint_asset_owner_commitment blob; +alter table outputs add parent_public_key blob; +alter table inbound_transactions add unique_id blob; diff --git a/base_layer/wallet/src/assets/asset.rs b/base_layer/wallet/src/assets/asset.rs new file mode 100644 index 0000000000..e9f604503b --- /dev/null +++ b/base_layer/wallet/src/assets/asset.rs @@ -0,0 +1,63 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_common_types::types::{Commitment, PublicKey}; + +#[derive(Clone)] +pub struct Asset { + name: String, + registration_output_status: String, + public_key: PublicKey, + owner_commitment: Commitment, +} + +impl Asset { + pub fn new( + name: String, + registration_output_status: String, + public_key: PublicKey, + owner_commitment: Commitment, + ) -> Self { + Self { + name, + registration_output_status, + public_key, + owner_commitment, + } + } + + pub fn name(&self) -> &str { + self.name.as_str() + } + + pub fn registration_output_status(&self) -> &str { + self.registration_output_status.as_str() + } + + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + pub fn owner_commitment(&self) -> &Commitment { + &self.owner_commitment + } +} diff --git a/base_layer/wallet/src/assets/asset_manager.rs b/base_layer/wallet/src/assets/asset_manager.rs new file mode 100644 index 0000000000..1cfbd02878 --- /dev/null +++ b/base_layer/wallet/src/assets/asset_manager.rs @@ -0,0 +1,204 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + assets::Asset, + error::WalletError, + output_manager_service::storage::database::{OutputManagerBackend, OutputManagerDatabase}, +}; +use tari_core::transactions::transaction::{OutputFeatures, OutputFlags, Transaction}; + +use crate::{ + output_manager_service::{handle::OutputManagerHandle, storage::models::DbUnblindedOutput}, + types::PersistentKeyManager, +}; +use log::*; +use tari_common_types::types::{Commitment, PublicKey}; +use tari_core::transactions::transaction_protocol::TxId; +use tari_crypto::tari_utilities::ByteArray; + +const LOG_TARGET: &str = "wallet::assets::asset_manager"; + +pub(crate) struct AssetManager { + output_database: OutputManagerDatabase, + output_manager: OutputManagerHandle, + assets_key_manager: TPersistentKeyManager, // transaction_service: TransactionServiceHandle +} +impl + AssetManager +{ + pub fn new(backend: T, output_manager: OutputManagerHandle, assets_key_manager: TPersistentKeyManager) -> Self { + Self { + output_database: OutputManagerDatabase::new(backend), + output_manager, + assets_key_manager, + } + } + + pub async fn list_owned(&self) -> Result, WalletError> { + let outputs = self + .output_database + .fetch_with_features(OutputFlags::ASSET_REGISTRATION) + .await + .map_err(|err| WalletError::OutputManagerError(err.into()))?; + + debug!( + target: LOG_TARGET, + "Found {} owned outputs that contain assets", + outputs.len() + ); + let assets: Vec = outputs + .into_iter() + .map(|unblinded_output| convert_to_asset(unblinded_output)) + .collect::>()?; + Ok(assets) + } + + pub async fn get_owned_asset_by_pub_key(&self, public_key: PublicKey) -> Result { + let output = self + .output_database + .fetch_by_features_asset_public_key(public_key) + .map_err(|err| WalletError::OutputManagerError(err.into()))?; + Ok(convert_to_asset(output)?) + } + + pub async fn create_registration_transaction(&mut self, name: String) -> Result<(TxId, Transaction), WalletError> { + let serializer = V1AssetMetadataSerializer {}; + + let metadata = AssetMetadata { name }; + let mut metadata_bin = vec![1u8]; + metadata_bin.extend(serializer.serialize(&metadata).into_iter()); + + let public_key = self.assets_key_manager.create_and_store_new()?; + let public_key_bytes = public_key.to_vec(); + let output = self + .output_manager + .create_output_with_features( + 0.into(), + OutputFeatures::for_asset_registration(metadata_bin, public_key), + Some(public_key_bytes), + None, + ) + .await?; + debug!(target: LOG_TARGET, "Created output: {:?}", output); + let (tx_id, transaction) = self + .output_manager + .create_send_to_self_with_output(0.into(), vec![output], 100.into()) + .await?; + Ok((tx_id, transaction)) + } + + pub async fn create_minting_transaction( + &mut self, + asset_public_key: PublicKey, + asset_owner_commitment: Commitment, + unique_ids: Vec>, + ) -> Result<(TxId, Transaction), WalletError> { + let mut outputs = Vec::with_capacity(unique_ids.len()); + // TODO: generate proof of ownership + for id in unique_ids { + let output = self + .output_manager + .create_output_with_features( + 0.into(), + OutputFeatures::for_minting(vec![], asset_public_key.clone(), asset_owner_commitment.clone()), + Some(id), + Some(asset_public_key.clone()), + ) + .await?; + outputs.push(output); + } + + let (tx_id, transaction) = self + .output_manager + .create_send_to_self_with_output(0.into(), outputs, 100.into()) + .await?; + Ok((tx_id, transaction)) + } +} + +fn convert_to_asset(unblinded_output: DbUnblindedOutput) -> Result { + if unblinded_output.unblinded_output.features.metadata.is_empty() { + // TODO: sort out unwraps + return Ok(Asset::new( + "".to_string(), + unblinded_output.status.to_string(), + unblinded_output + .unblinded_output + .features + .asset + .as_ref() + .map(|a| a.public_key.clone()) + .unwrap(), + unblinded_output.commitment, + )); + } + let version = unblinded_output.unblinded_output.features.metadata[0]; + + let deserializer = get_deserializer(version); + + let metadata = deserializer.deserialize(&unblinded_output.unblinded_output.features.metadata[1..]); + Ok(Asset::new( + metadata.name, + unblinded_output.status.to_string(), + unblinded_output + .unblinded_output + .features + .asset + .as_ref() + .map(|a| a.public_key.clone()) + .unwrap(), + unblinded_output.commitment, + )) +} + +fn get_deserializer(_version: u8) -> impl AssetMetadataDeserializer { + V1AssetMetadataSerializer {} +} + +pub trait AssetMetadataDeserializer { + fn deserialize(&self, metadata: &[u8]) -> AssetMetadata; +} +pub trait AssetMetadataSerializer { + fn serialize(&self, model: &AssetMetadata) -> Vec; +} + +pub struct V1AssetMetadataSerializer {} + +// TODO: Replace with proto serializer +impl AssetMetadataDeserializer for V1AssetMetadataSerializer { + fn deserialize(&self, metadata: &[u8]) -> AssetMetadata { + AssetMetadata { + name: String::from_utf8(Vec::from(metadata)).unwrap(), + } + } +} + +impl AssetMetadataSerializer for V1AssetMetadataSerializer { + fn serialize(&self, model: &AssetMetadata) -> Vec { + model.name.clone().into_bytes() + } +} + +pub struct AssetMetadata { + name: String, +} diff --git a/base_layer/wallet/src/assets/asset_manager_handle.rs b/base_layer/wallet/src/assets/asset_manager_handle.rs new file mode 100644 index 0000000000..fd2b755739 --- /dev/null +++ b/base_layer/wallet/src/assets/asset_manager_handle.rs @@ -0,0 +1,107 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + assets::{ + infrastructure::{AssetManagerRequest, AssetManagerResponse}, + Asset, + }, + error::WalletError, +}; +use tari_service_framework::{reply_channel::SenderService, Service}; + +use tari_common_types::types::{Commitment, PublicKey}; +use tari_core::transactions::{transaction::Transaction, transaction_protocol::TxId}; + +#[derive(Clone)] +pub struct AssetManagerHandle { + handle: SenderService>, +} + +impl AssetManagerHandle { + pub fn new(sender: SenderService>) -> Self { + Self { handle: sender } + } + + pub async fn list_owned_assets(&mut self) -> Result, WalletError> { + match self.handle.call(AssetManagerRequest::ListOwned {}).await?? { + AssetManagerResponse::ListOwned { assets } => Ok(assets), + _ => Err(WalletError::UnexpectedApiResponse { + method: "list_owned_assets".to_string(), + api: "AssetManagerService".to_string(), + }), + } + } + + pub async fn get_owned_asset_by_pub_key(&mut self, public_key: &PublicKey) -> Result { + match self + .handle + .call(AssetManagerRequest::GetOwnedAsset { + public_key: public_key.clone(), + }) + .await?? + { + AssetManagerResponse::GetOwnedAsset { asset } => Ok(*asset), + _ => Err(WalletError::UnexpectedApiResponse { + method: "get_owned_asset_by_pub_key".to_string(), + api: "AssetManagerService".to_string(), + }), + } + } + + pub async fn create_registration_transaction(&mut self, name: String) -> Result<(TxId, Transaction), WalletError> { + match self + .handle + .call(AssetManagerRequest::CreateRegistrationTransaction { name }) + .await?? + { + AssetManagerResponse::CreateRegistrationTransaction { transaction, tx_id } => Ok((tx_id, *transaction)), + _ => Err(WalletError::UnexpectedApiResponse { + method: "create_registration_transaction".to_string(), + api: "AssetManagerService".to_string(), + }), + } + } + + pub async fn create_minting_transaction( + &mut self, + asset_public_key: &PublicKey, + asset_owner_commitment: &Commitment, + unique_ids: Vec>, + ) -> Result<(TxId, Transaction), WalletError> { + match self + .handle + .call(AssetManagerRequest::CreateMintingTransaction { + asset_public_key: Box::new(asset_public_key.clone()), + asset_owner_commitment: Box::new(asset_owner_commitment.clone()), + unique_ids, + }) + .await?? + { + AssetManagerResponse::CreateMintingTransaction { transaction, tx_id } => Ok((tx_id, *transaction)), + _ => Err(WalletError::UnexpectedApiResponse { + method: "create_minting_transaction".to_string(), + api: "AssetManagerService".to_string(), + }), + } + } +} diff --git a/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs b/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs new file mode 100644 index 0000000000..28e19de2d7 --- /dev/null +++ b/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs @@ -0,0 +1,110 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + assets::{ + infrastructure::{AssetManagerRequest, AssetManagerResponse}, + AssetManager, + }, + error::WalletError, + output_manager_service::storage::{ + database::{OutputManagerBackend}, + }, +}; +use tari_service_framework::{ + reply_channel::{Receiver, }, +}; +use futures::{pin_mut, StreamExt}; +use tari_shutdown::ShutdownSignal; +use log::*; +use crate::output_manager_service::handle::OutputManagerHandle; +use crate::types::MockPersistentKeyManager; + +const LOG_TARGET: &str = "wallet::assets::infrastructure::asset_manager_service"; + +pub struct AssetManagerService { + manager: AssetManager, +} + +impl AssetManagerService { + pub fn new(backend: T, output_manager: OutputManagerHandle) -> Self { + Self { + manager: AssetManager::::new(backend, output_manager, MockPersistentKeyManager::new()), + } + } + + pub async fn start( + mut self, + mut shutdown_signal: ShutdownSignal, + request_stream: Receiver>, + ) -> Result<(), WalletError> { + let request_stream = request_stream.fuse(); + pin_mut!(request_stream); + + info!(target: LOG_TARGET, "Asset Manager Service started"); + loop { + futures::select! { + request_context = request_stream.select_next_some() => { + trace!(target: LOG_TARGET, "Handling Service API Request"); + let (request, reply_tx) = request_context.split(); + let response = self.handle_request(request).await.map_err(|e| { + warn!(target: LOG_TARGET, "Error handling request: {:?}", e); + e + }); + let _ = reply_tx.send(response).map_err(|e| { + warn!(target: LOG_TARGET, "Failed to send reply"); + e + }); + }, + _ = shutdown_signal => { + info!(target: LOG_TARGET, "Asset manager service shutting down because it received the shutdown signal"); + break; + } + complete => { + info!(target: LOG_TARGET, "Asset manager service shutting down"); + break; + } + } + } + Ok(()) + } + + pub async fn handle_request(&mut self, request: AssetManagerRequest) -> Result { + match request { + AssetManagerRequest::ListOwned { .. } => Ok(AssetManagerResponse::ListOwned { + assets: self.manager.list_owned().await?, + }), + AssetManagerRequest::CreateRegistrationTransaction {name} => { + let (tx_id, transaction) =self.manager.create_registration_transaction(name).await?; + Ok(AssetManagerResponse::CreateRegistrationTransaction {transaction: Box::new(transaction), tx_id}) + } + AssetManagerRequest::GetOwnedAsset { public_key } => { + let asset = self.manager.get_owned_asset_by_pub_key(public_key).await?; + Ok(AssetManagerResponse::GetOwnedAsset { asset: Box::new(asset)}) + }, + AssetManagerRequest::CreateMintingTransaction { asset_public_key, asset_owner_commitment, unique_ids } => { + let (tx_id, transaction) =self.manager.create_minting_transaction(*asset_public_key, *asset_owner_commitment, unique_ids).await?; + Ok(AssetManagerResponse::CreateMintingTransaction {transaction: Box::new(transaction), tx_id}) + } + } + } +} diff --git a/base_layer/wallet/src/assets/infrastructure/initializer.rs b/base_layer/wallet/src/assets/infrastructure/initializer.rs new file mode 100644 index 0000000000..12f9425cee --- /dev/null +++ b/base_layer/wallet/src/assets/infrastructure/initializer.rs @@ -0,0 +1,94 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::output_manager_service::storage::database::OutputManagerBackend; + + +use crate::assets::AssetManagerHandle; +use crate::assets::infrastructure::AssetManagerService; +use log::*; + +use futures::{future, Future}; + + + +use tari_service_framework::{ + reply_channel, + ServiceInitializationError, + ServiceInitializer, + ServiceInitializerContext, +}; + +use crate::output_manager_service::handle::OutputManagerHandle; +use tari_service_framework::{ + async_trait, +}; + +const LOG_TARGET: &str = "wallet::assets::infrastructure::initializer"; + +pub struct AssetManagerServiceInitializer + where T: OutputManagerBackend +{ + backend: Option +} + +impl AssetManagerServiceInitializer + where T: OutputManagerBackend + 'static +{ + pub fn new(backend: T + + ) -> Self { + Self { + backend: Some(backend) + } + } +} + +#[async_trait] +impl ServiceInitializer for AssetManagerServiceInitializer + where T: OutputManagerBackend + 'static +{ + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { + + let (sender, receiver) = reply_channel::unbounded(); + + let handle = AssetManagerHandle::new(sender); + context.register_handle(handle); + + let backend = self.backend.take().expect("this expect pattern is dumb"); + + context.spawn_when_ready(move |handles| async move { + + let output_manager = handles.expect_handle::(); + // let transaction_service = handles.expect_handle::(); + let service = AssetManagerService::new(backend, output_manager); + + let running = service.start(handles.get_shutdown_signal(), receiver); + + futures::pin_mut!(running); + future::select(running, handles.get_shutdown_signal()).await; + info!(target: LOG_TARGET, "Asset Manager Service shutdown"); + }); + Ok(()) + } +} + diff --git a/base_layer/wallet/src/assets/infrastructure/mod.rs b/base_layer/wallet/src/assets/infrastructure/mod.rs new file mode 100644 index 0000000000..16fe057e7b --- /dev/null +++ b/base_layer/wallet/src/assets/infrastructure/mod.rs @@ -0,0 +1,52 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod asset_manager_service; +use crate::assets::Asset; +pub use asset_manager_service::AssetManagerService; + +use tari_common_types::types::{Commitment, PublicKey}; +use tari_core::transactions::{transaction::Transaction, transaction_protocol::TxId}; + +pub mod initializer; + +pub enum AssetManagerRequest { + ListOwned {}, + GetOwnedAsset { + public_key: PublicKey, + }, + CreateRegistrationTransaction { + name: String, + }, + CreateMintingTransaction { + asset_public_key: Box, + asset_owner_commitment: Box, + unique_ids: Vec>, + }, +} + +pub enum AssetManagerResponse { + ListOwned { assets: Vec }, + GetOwnedAsset { asset: Box }, + CreateRegistrationTransaction { transaction: Box, tx_id: TxId }, + CreateMintingTransaction { transaction: Box, tx_id: TxId }, +} diff --git a/base_layer/wallet/src/assets/mod.rs b/base_layer/wallet/src/assets/mod.rs new file mode 100644 index 0000000000..3b6fa8b6eb --- /dev/null +++ b/base_layer/wallet/src/assets/mod.rs @@ -0,0 +1,31 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod asset_manager; +pub(crate) use asset_manager::AssetManager; + +mod asset; +pub use asset::Asset; + +mod asset_manager_handle; +pub use asset_manager_handle::AssetManagerHandle; +pub(crate) mod infrastructure; diff --git a/base_layer/wallet/src/base_node_service/config.rs b/base_layer/wallet/src/base_node_service/config.rs index e7468a809e..85ad381c3e 100644 --- a/base_layer/wallet/src/base_node_service/config.rs +++ b/base_layer/wallet/src/base_node_service/config.rs @@ -36,7 +36,7 @@ pub struct BaseNodeServiceConfig { impl Default for BaseNodeServiceConfig { fn default() -> Self { Self { - base_node_monitor_refresh_interval: Duration::from_secs(5), + base_node_monitor_refresh_interval: Duration::from_secs(30), base_node_rpc_pool_size: 10, request_max_age: Duration::from_secs(60), event_channel_size: 250, diff --git a/base_layer/wallet/src/base_node_service/handle.rs b/base_layer/wallet/src/base_node_service/handle.rs index f495479778..404673398f 100644 --- a/base_layer/wallet/src/base_node_service/handle.rs +++ b/base_layer/wallet/src/base_node_service/handle.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::{error::BaseNodeServiceError, service::BaseNodeState}; -use std::{sync::Arc, time::Duration}; +use std::{fmt, fmt::Formatter, sync::Arc, time::Duration}; use tari_common_types::chain_metadata::ChainMetadata; use tari_comms::peer_manager::Peer; use tari_service_framework::reply_channel::SenderService; @@ -52,6 +52,19 @@ pub enum BaseNodeEvent { BaseNodePeerSet(Box), } +impl fmt::Display for BaseNodeEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + BaseNodeEvent::BaseNodeStateChanged(state) => { + write!(f, "BaseNodeStateChanged: Synced:{:?}", state.is_synced) + }, + BaseNodeEvent::BaseNodePeerSet(peer) => { + write!(f, "BaseNodePeerSet:{}", peer) + }, + } + } +} + /// The Base Node Service Handle is a struct that contains the interfaces used to communicate with a running /// Base Node #[derive(Clone)] diff --git a/base_layer/wallet/src/error.rs b/base_layer/wallet/src/error.rs index bfd57766c4..ff111e877a 100644 --- a/base_layer/wallet/src/error.rs +++ b/base_layer/wallet/src/error.rs @@ -42,6 +42,7 @@ use tari_crypto::tari_utilities::{hex::HexError, ByteArrayError}; use tari_p2p::{initialization::CommsInitializationError, services::liveness::error::LivenessError}; use tari_service_framework::ServiceInitializationError; use thiserror::Error; +use tari_service_framework::reply_channel::TransportChannelError; #[derive(Debug, Error)] pub enum WalletError { @@ -83,6 +84,12 @@ pub enum WalletError { ByteArrayError(#[from] tari_crypto::tari_utilities::ByteArrayError), #[error("Utxo Scanner Error: {0}")] UtxoScannerError(#[from] UtxoScannerError), + + #[error("Transport channel error: `{0}`")] + TransportChannelError(#[from] TransportChannelError), + + #[error("Unexpected API Response while calling method `{method}` on `{api}`")] + UnexpectedApiResponse{ method: String, api: String}, } #[derive(Debug, Error)] diff --git a/base_layer/wallet/src/lib.rs b/base_layer/wallet/src/lib.rs index 22bce8bfdb..012b481d84 100644 --- a/base_layer/wallet/src/lib.rs +++ b/base_layer/wallet/src/lib.rs @@ -7,8 +7,11 @@ #![deny(unknown_lints)] #![recursion_limit = "2048"] +#![allow(clippy::too_many_arguments)] + #[macro_use] mod macros; +pub mod assets; pub mod base_node_service; pub mod connectivity_service; pub mod contacts_service; @@ -16,10 +19,13 @@ pub mod error; pub mod output_manager_service; pub mod storage; pub mod test_utils; +pub mod tokens; pub mod transaction_service; pub mod types; pub mod util; pub mod wallet; +mod operation_id; +pub use operation_id::OperationId; #[macro_use] extern crate diesel; diff --git a/base_layer/wallet/src/operation_id.rs b/base_layer/wallet/src/operation_id.rs new file mode 100644 index 0000000000..6a91989062 --- /dev/null +++ b/base_layer/wallet/src/operation_id.rs @@ -0,0 +1,77 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt; +use std::fmt::Formatter; +use serde::{Serialize, Deserialize}; +use std::hash::{Hash, Hasher}; +use rand::rngs::OsRng; +use rand::RngCore; + + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)] +pub struct OperationId(u64); + +impl OperationId { + pub fn new_random() -> Self { + OperationId(OsRng.next_u64()) + } + + pub fn as_u64(self) -> u64 { + self.0 + } +} + +impl Hash for OperationId { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl PartialEq for OperationId { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + + +impl Eq for OperationId { + +} + +impl From for OperationId { + fn from(s: u64) -> Self { + Self(s) + } +} + +impl From for u64 { + fn from(s: OperationId) ->Self { + s.0 + } +} + +impl fmt::Display for OperationId { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/base_layer/wallet/src/output_manager_service/error.rs b/base_layer/wallet/src/output_manager_service/error.rs index 1dfd3e969e..e2a466d409 100644 --- a/base_layer/wallet/src/output_manager_service/error.rs +++ b/base_layer/wallet/src/output_manager_service/error.rs @@ -133,6 +133,8 @@ pub enum OutputManagerStorageError { ConversionError, #[error("Output has already been spent")] OutputAlreadySpent, + #[error("Output is already encumbered")] + OutputAlreadyEncumbered, #[error("Key Manager not initialized")] KeyManagerNotInitialized, #[error("Out of range error: `{0}`")] diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index 54b082c900..2e53d4e77c 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -26,18 +26,24 @@ use crate::{ service::Balance, storage::{database::PendingTransactionOutputs, models::KnownOneSidedPaymentScript}, tasks::TxoValidationType, - TxId, }, types::ValidationRetryStrategy, }; use aes_gcm::Aes256Gcm; -use std::{collections::HashMap, fmt, sync::Arc, time::Duration}; +use std::{collections::HashMap, fmt, fmt::Formatter, sync::Arc, time::Duration}; use tari_common_types::types::PublicKey; use tari_comms::types::CommsPublicKey; use tari_core::transactions::{ tari_amount::MicroTari, - transaction::{Transaction, TransactionInput, TransactionOutput, UnblindedOutput}, - transaction_protocol::sender::TransactionSenderMessage, + transaction::{ + OutputFeatures, + Transaction, + TransactionInput, + TransactionOutput, + UnblindedOutput, + UnblindedOutputBuilder, + }, + transaction_protocol::{sender::TransactionSenderMessage, TxId}, ReceiverTransactionProtocol, SenderTransactionProtocol, }; @@ -53,12 +59,32 @@ pub enum OutputManagerRequest { AddOutputWithTxId((TxId, Box)), UpdateOutputMetadataSignature(Box), GetRecipientTransaction(TransactionSenderMessage), - GetCoinbaseTransaction((u64, MicroTari, MicroTari, u64)), - ConfirmPendingTransaction(u64), - ConfirmTransaction((u64, Vec, Vec)), - PrepareToSendTransaction((TxId, MicroTari, MicroTari, Option, String, TariScript)), - CreatePayToSelfTransaction((TxId, MicroTari, MicroTari, Option, String)), - CancelTransaction(u64), + GetCoinbaseTransaction((TxId, MicroTari, MicroTari, u64)), + ConfirmPendingTransaction(TxId), + ConfirmTransaction((TxId, Vec, Vec)), + PrepareToSendTransaction { + tx_id: TxId, + amount: MicroTari, + unique_id: Option>, + fee_per_gram: MicroTari, + lock_height: Option, + message: String, + script: TariScript, + }, + CreatePayToSelfTransaction { + tx_id: TxId, + amount: MicroTari, + unique_id: Option>, + fee_per_gram: MicroTari, + lock_height: Option, + message: String, + }, + CreatePayToSelfWithOutputs { + amount: MicroTari, + outputs: Vec, + fee_per_gram: MicroTari, + }, + CancelTransaction(TxId), TimeoutTransactions(Duration), GetPendingTransactions, GetSpentOutputs, @@ -75,6 +101,13 @@ pub enum OutputManagerRequest { ScanForRecoverableOutputs(Vec), ScanOutputs(Vec), AddKnownOneSidedPaymentScript(KnownOneSidedPaymentScript), + CreateOutputWithFeatures { + value: MicroTari, + features: Box, + unique_id: Option>, + parent_public_key: Box>, + }, + ReinstateCancelledInboundTx(TxId), } @@ -95,8 +128,8 @@ impl fmt::Display for OutputManagerRequest { GetRecipientTransaction(_) => write!(f, "GetRecipientTransaction"), ConfirmTransaction(v) => write!(f, "ConfirmTransaction ({})", v.0), ConfirmPendingTransaction(v) => write!(f, "ConfirmPendingTransaction ({})", v), - PrepareToSendTransaction((_, _, _, _, msg, _)) => write!(f, "PrepareToSendTransaction ({})", msg), - CreatePayToSelfTransaction((_, _, _, _, msg)) => write!(f, "CreatePayToSelfTransaction ({})", msg), + PrepareToSendTransaction { message, .. } => write!(f, "PrepareToSendTransaction ({})", message), + CreatePayToSelfTransaction { message, .. } => write!(f, "CreatePayToSelfTransaction ({})", message), CancelTransaction(v) => write!(f, "CancelTransaction ({})", v), TimeoutTransactions(d) => write!(f, "TimeoutTransactions ({}s)", d.as_secs()), GetPendingTransactions => write!(f, "GetPendingTransactions"), @@ -115,6 +148,20 @@ impl fmt::Display for OutputManagerRequest { ScanForRecoverableOutputs(_) => write!(f, "ScanForRecoverableOutputs"), ScanOutputs(_) => write!(f, "ScanOutputs"), AddKnownOneSidedPaymentScript(_) => write!(f, "AddKnownOneSidedPaymentScript"), + CreateOutputWithFeatures { + value, + features, + unique_id, + parent_public_key, + } => write!( + f, + "CreateOutputWithFeatures({}, {}, {:?}, {:?})", + value, + features.to_string(), + unique_id, + parent_public_key + ), + CreatePayToSelfWithOutputs { .. } => write!(f, "CreatePayToSelfWithOutputs"), ReinstateCancelledInboundTx(_) => write!(f, "ReinstateCancelledInboundTx"), } } @@ -135,14 +182,14 @@ pub enum OutputManagerResponse { TransactionToSend(SenderTransactionProtocol), TransactionCancelled, TransactionsTimedOut, - PendingTransactions(HashMap), + PendingTransactions(HashMap), SpentOutputs(Vec), UnspentOutputs(Vec), InvalidOutputs(Vec), SeedWords(Vec), BaseNodePublicKeySet, UtxoValidationStarted(u64), - Transaction((u64, Transaction, MicroTari, MicroTari)), + Transaction((TxId, Transaction, MicroTari, MicroTari)), EncryptionApplied, EncryptionRemoved, PublicRewindKeys(Box), @@ -150,6 +197,8 @@ pub enum OutputManagerResponse { RewoundOutputs(Vec), ScanOutputs(Vec), AddKnownOneSidedPaymentScript, + CreateOutputWithFeatures { output: Box }, + CreatePayToSelfWithOutputs { transaction: Box, tx_id: TxId }, ReinstatedCancelledInboundTx, } @@ -167,6 +216,31 @@ pub enum OutputManagerEvent { Error(String), } +impl fmt::Display for OutputManagerEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + OutputManagerEvent::TxoValidationTimedOut(tx, validation_type) => { + write!(f, "TxoValidationTimedOut for {}: {}", tx, validation_type) + }, + OutputManagerEvent::TxoValidationSuccess(tx, validation_type) => { + write!(f, "TxoValidationSuccess for {}: {}", tx, validation_type) + }, + OutputManagerEvent::TxoValidationFailure(tx, validation_type) => { + write!(f, "TxoValidationFailure for {}: {}", tx, validation_type) + }, + OutputManagerEvent::TxoValidationAborted(tx, validation_type) => { + write!(f, "TxoValidationAborted for {}: {}", tx, validation_type) + }, + OutputManagerEvent::TxoValidationDelayed(tx, validation_type) => { + write!(f, "TxoValidationDelayed for {}: {}", tx, validation_type) + }, + OutputManagerEvent::Error(error) => { + write!(f, "Error {}", error) + }, + } + } +} + #[derive(Debug, Clone)] pub struct PublicRewindKeys { pub rewind_public_key: PublicKey, @@ -220,6 +294,28 @@ impl OutputManagerHandle { } } + pub async fn create_output_with_features( + &mut self, + value: MicroTari, + features: OutputFeatures, + unique_id: Option>, + parent_public_key: Option, + ) -> Result { + match self + .handle + .call(OutputManagerRequest::CreateOutputWithFeatures { + value, + features: Box::new(features), + unique_id, + parent_public_key: Box::new(parent_public_key), + }) + .await?? + { + OutputManagerResponse::CreateOutputWithFeatures { output } => Ok(*output), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + pub async fn update_output_metadata_signature( &mut self, output: TransactionOutput, @@ -281,21 +377,23 @@ impl OutputManagerHandle { &mut self, tx_id: TxId, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, lock_height: Option, message: String, - recipient_script: TariScript, + script: TariScript, ) -> Result { match self .handle - .call(OutputManagerRequest::PrepareToSendTransaction(( + .call(OutputManagerRequest::PrepareToSendTransaction { tx_id, amount, + unique_id, fee_per_gram, lock_height, message, - recipient_script, - ))) + script, + }) .await?? { OutputManagerResponse::TransactionToSend(stp) => Ok(stp), @@ -327,7 +425,7 @@ impl OutputManagerHandle { } } - pub async fn confirm_pending_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { + pub async fn confirm_pending_transaction(&mut self, tx_id: TxId) -> Result<(), OutputManagerError> { match self .handle .call(OutputManagerRequest::ConfirmPendingTransaction(tx_id)) @@ -340,7 +438,7 @@ impl OutputManagerHandle { pub async fn confirm_transaction( &mut self, - tx_id: u64, + tx_id: TxId, spent_outputs: Vec, received_outputs: Vec, ) -> Result<(), OutputManagerError> { @@ -358,7 +456,7 @@ impl OutputManagerHandle { } } - pub async fn cancel_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { + pub async fn cancel_transaction(&mut self, tx_id: TxId) -> Result<(), OutputManagerError> { match self .handle .call(OutputManagerRequest::CancelTransaction(tx_id)) @@ -382,7 +480,7 @@ impl OutputManagerHandle { pub async fn get_pending_transactions( &mut self, - ) -> Result, OutputManagerError> { + ) -> Result, OutputManagerError> { match self.handle.call(OutputManagerRequest::GetPendingTransactions).await?? { OutputManagerResponse::PendingTransactions(p) => Ok(p), _ => Err(OutputManagerError::UnexpectedApiResponse), @@ -459,7 +557,7 @@ impl OutputManagerHandle { split_count: usize, fee_per_gram: MicroTari, lock_height: Option, - ) -> Result<(u64, Transaction, MicroTari, MicroTari), OutputManagerError> { + ) -> Result<(TxId, Transaction, MicroTari, MicroTari), OutputManagerError> { match self .handle .call(OutputManagerRequest::CreateCoinSplit(( @@ -528,23 +626,45 @@ impl OutputManagerHandle { } } + pub async fn create_send_to_self_with_output( + &mut self, + amount: MicroTari, + outputs: Vec, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::CreatePayToSelfWithOutputs { + amount, + outputs, + fee_per_gram, + }) + .await?? + { + OutputManagerResponse::CreatePayToSelfWithOutputs { transaction, tx_id } => Ok((tx_id, *transaction)), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + pub async fn create_pay_to_self_transaction( &mut self, tx_id: TxId, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, lock_height: Option, message: String, ) -> Result<(MicroTari, Transaction), OutputManagerError> { match self .handle - .call(OutputManagerRequest::CreatePayToSelfTransaction(( + .call(OutputManagerRequest::CreatePayToSelfTransaction { tx_id, amount, fee_per_gram, lock_height, message, - ))) + unique_id, + }) .await?? { OutputManagerResponse::PayToSelfTransaction(outputs) => Ok(outputs), diff --git a/base_layer/wallet/src/output_manager_service/mod.rs b/base_layer/wallet/src/output_manager_service/mod.rs index 80f02f2445..570e85e585 100644 --- a/base_layer/wallet/src/output_manager_service/mod.rs +++ b/base_layer/wallet/src/output_manager_service/mod.rs @@ -63,8 +63,6 @@ mod tasks; const LOG_TARGET: &str = "wallet::output_manager_service::initializer"; -pub type TxId = u64; - pub struct OutputManagerServiceInitializer where T: OutputManagerBackend { diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index 1da885d7f1..fb47648786 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -86,11 +86,13 @@ where TBackend: OutputManagerBackend + 'static output.script, output.sender_offset_public_key, output.metadata_signature, + output.unique_id, + output.parent_public_key ) }) }) .map( - |(output, features, script, sender_offset_public_key, metadata_signature)| { + |(output, features, script, sender_offset_public_key, metadata_signature, unique_id, parent_public_key)| { UnblindedOutput::new( output.committed_value, output.blinding_factor.clone(), @@ -100,6 +102,8 @@ where TBackend: OutputManagerBackend + 'static output.blinding_factor, sender_offset_public_key, metadata_signature, + unique_id, + parent_public_key ) }, ) diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index bb1a98f505..2c82c7c69d 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -85,11 +85,11 @@ use crate::{ }, tasks::{TxoValidationTask, TxoValidationType}, MasterKeyManager, - TxId, }, transaction_service::handle::TransactionServiceHandle, types::{HashDigest, ValidationRetryStrategy}, }; +use tari_core::transactions::{transaction::UnblindedOutputBuilder, transaction_protocol::TxId}; const LOG_TARGET: &str = "wallet::output_manager_service"; const LOG_TARGET_STRESS: &str = "stress_test::output_manager_service"; @@ -229,22 +229,29 @@ where TBackend: OutputManagerBackend + 'static .get_coinbase_transaction(tx_id, reward, fees, block_height) .await .map(OutputManagerResponse::CoinbaseTransaction), - OutputManagerRequest::PrepareToSendTransaction(( + OutputManagerRequest::PrepareToSendTransaction { tx_id, amount, + unique_id, fee_per_gram, lock_height, message, - recipient_script, - )) => self - .prepare_transaction_to_send(tx_id, amount, fee_per_gram, lock_height, message, recipient_script) + script, + } => self + .prepare_transaction_to_send(tx_id, amount, unique_id, fee_per_gram, lock_height, message, script) .await .map(OutputManagerResponse::TransactionToSend), - OutputManagerRequest::CreatePayToSelfTransaction((tx_id, amount, fee_per_gram, lock_height, message)) => { - self.create_pay_to_self_transaction(tx_id, amount, fee_per_gram, lock_height, message) - .await - .map(OutputManagerResponse::PayToSelfTransaction) - }, + OutputManagerRequest::CreatePayToSelfTransaction { + tx_id, + amount, + unique_id, + fee_per_gram, + lock_height, + message, + } => self + .create_pay_to_self_transaction(tx_id, amount, unique_id, fee_per_gram, lock_height, message) + .await + .map(OutputManagerResponse::PayToSelfTransaction), OutputManagerRequest::FeeEstimate((amount, fee_per_gram, num_kernels, num_outputs)) => self .fee_estimate(amount, fee_per_gram, num_kernels, num_outputs) .await @@ -351,6 +358,32 @@ where TBackend: OutputManagerBackend + 'static .reinstate_cancelled_inbound_transaction(tx_id) .await .map(|_| OutputManagerResponse::ReinstatedCancelledInboundTx), + OutputManagerRequest::CreateOutputWithFeatures { + value, + features, + unique_id, + parent_public_key, + } => { + let unblinded_output = self + .create_output_with_features(value, *features, unique_id, *parent_public_key) + .await?; + Ok(OutputManagerResponse::CreateOutputWithFeatures { + output: Box::new(unblinded_output), + }) + }, + OutputManagerRequest::CreatePayToSelfWithOutputs { + amount: _, + outputs, + fee_per_gram, + } => { + let (tx_id, transaction) = self + .create_pay_to_self_containing_outputs(outputs, fee_per_gram) + .await?; + Ok(OutputManagerResponse::CreatePayToSelfWithOutputs { + transaction: Box::new(transaction), + tx_id, + }) + }, } } @@ -418,6 +451,30 @@ where TBackend: OutputManagerBackend + 'static Ok(()) } + async fn create_output_with_features( + &self, + value: MicroTari, + features: OutputFeatures, + unique_id: Option>, + parent_public_key: Option, + ) -> Result { + let (spending_key, script_private_key) = self + .resources + .master_key_manager + .get_next_spend_and_script_key() + .await?; + let input_data = inputs!(PublicKey::from_secret_key(&script_private_key)); + let script = script!(Nop); + + Ok(UnblindedOutputBuilder::new(value, spending_key.clone()) + .with_features(features) + .with_script(script.clone()) + .with_input_data(input_data) + .with_script_private_key(script_private_key) + .with_unique_id(unique_id) + .with_parent_public_key(parent_public_key)) + } + async fn get_balance(&self, current_chain_tip: Option) -> Result { let balance = self.resources.db.get_balance(current_chain_tip).await?; trace!(target: LOG_TARGET, "Balance: {:?}", balance); @@ -464,6 +521,9 @@ where TBackend: OutputManagerBackend + 'static &single_round_sender_data.sender_offset_public_key.clone(), &single_round_sender_data.public_commitment_nonce.clone(), )?, + single_round_sender_data.unique_id.clone(), + // TODO: put data in + None, ), &self.resources.factories, )?; @@ -493,7 +553,7 @@ where TBackend: OutputManagerBackend + 'static /// detects the output on the blockchain pub async fn confirm_received_transaction_output( &mut self, - tx_id: u64, + tx_id: TxId, received_output: &TransactionOutput, ) -> Result<(), OutputManagerError> { let pending_transaction = self.resources.db.fetch_pending_transaction_outputs(tx_id).await?; @@ -546,9 +606,9 @@ where TBackend: OutputManagerBackend + 'static num_kernels, num_outputs ); - + // TODO: Perhaps include unique id here let (utxos, _, _) = self - .select_utxos(amount, fee_per_gram, num_outputs as usize, None) + .select_utxos(amount, fee_per_gram, num_outputs as usize, None, None) .await?; debug!(target: LOG_TARGET, "{} utxos selected.", utxos.len()); @@ -564,6 +624,7 @@ where TBackend: OutputManagerBackend + 'static &mut self, tx_id: TxId, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, lock_height: Option, message: String, @@ -573,7 +634,9 @@ where TBackend: OutputManagerBackend + 'static target: LOG_TARGET, "Preparing to send transaction. Amount: {}. Fee per gram: {}. ", amount, fee_per_gram, ); - let (outputs, _, total) = self.select_utxos(amount, fee_per_gram, 1, None).await?; + let (outputs, _, total) = self + .select_utxos(amount, fee_per_gram, 1, None, unique_id.as_ref()) + .await?; let offset = PrivateKey::random(&mut OsRng); let nonce = PrivateKey::random(&mut OsRng); @@ -596,6 +659,9 @@ where TBackend: OutputManagerBackend + 'static .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_tx_id(tx_id); + if let Some(ref unique_id) = unique_id { + builder = builder.with_unique_id(unique_id.clone()); + } for uo in outputs.iter() { builder.with_input( uo.unblinded_output @@ -725,15 +791,120 @@ where TBackend: OutputManagerBackend + 'static Ok(tx) } + async fn create_pay_to_self_containing_outputs( + &mut self, + outputs: Vec, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction), OutputManagerError> { + let (inputs, _, total) = self + .select_utxos(0.into(), fee_per_gram, outputs.len(), None, None) + .await?; + let offset = PrivateKey::random(&mut OsRng); + let nonce = PrivateKey::random(&mut OsRng); + + // Create builder with no recipients (other than ourselves) + let mut builder = SenderTransactionProtocol::builder(0); + builder + .with_lock_height(0) + .with_fee_per_gram(fee_per_gram) + .with_offset(offset.clone()) + .with_private_nonce(nonce.clone()) + .with_prevent_fee_gt_amount(false); + + for uo in &inputs { + builder.with_input( + uo.unblinded_output + .as_transaction_input(&self.resources.factories.commitment)?, + uo.unblinded_output.clone(), + ); + } + let mut db_outputs = vec![]; + for mut unblinded_output in outputs { + let sender_offset_private_key = PrivateKey::random(&mut OsRng); + let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); + + let public_offset_commitment_private_key = PrivateKey::random(&mut OsRng); + let public_offset_commitment_pub_key = PublicKey::from_secret_key(&public_offset_commitment_private_key); + + unblinded_output.sign_as_receiver(sender_offset_public_key, public_offset_commitment_pub_key)?; + unblinded_output.sign_as_sender(&sender_offset_private_key)?; + + let ub = unblinded_output.try_build()?; + builder + .with_output(ub.clone(), sender_offset_private_key.clone()) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + db_outputs.push(DbUnblindedOutput::from_unblinded_output(ub, &self.resources.factories)?) + } + + let mut change_keys = None; + + let fee = Fee::calculate(fee_per_gram, 1, inputs.len(), 1); + let change_value = total.saturating_sub(fee); + if change_value > 0.into() { + let (spending_key, script_private_key) = self + .resources + .master_key_manager + .get_next_spend_and_script_key() + .await?; + change_keys = Some((spending_key.clone(), script_private_key.clone())); + builder.with_change_secret(spending_key); + builder.with_rewindable_outputs(self.resources.master_key_manager.rewind_data().clone()); + builder.with_change_script( + script!(Nop), + inputs!(PublicKey::from_secret_key(&script_private_key)), + script_private_key, + ); + } + + let mut stp = builder + .build::(&self.resources.factories) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + if let Some((spending_key, script_private_key)) = change_keys { + let change_script_offset_public_key = stp.get_change_sender_offset_public_key()?.ok_or_else(|| { + OutputManagerError::BuildError( + "There should be a change script offset public key available".to_string(), + ) + })?; + + let sender_offset_private_key = PrivateKey::random(&mut OsRng); + let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); + + let public_offset_commitment_private_key = PrivateKey::random(&mut OsRng); + let public_offset_commitment_pub_key = PublicKey::from_secret_key(&public_offset_commitment_private_key); + + let mut output_builder = UnblindedOutputBuilder::new(stp.get_change_amount()?, spending_key) + .with_script(script!(Nop)) + .with_input_data(inputs!(PublicKey::from_secret_key(&script_private_key))) + .with_script_private_key(script_private_key); + + output_builder.sign_as_receiver(sender_offset_public_key, public_offset_commitment_pub_key)?; + output_builder.sign_as_sender(&sender_offset_private_key)?; + + let change_output = + DbUnblindedOutput::from_unblinded_output(output_builder.try_build()?, &self.resources.factories)?; + + db_outputs.push(change_output); + } + let tx_id = stp.get_tx_id()?; + + self.resources.db.encumber_outputs(tx_id, inputs, db_outputs).await?; + stp.finalize(KernelFeatures::empty(), &self.resources.factories)?; + + Ok((tx_id, stp.take_transaction()?)) + } + async fn create_pay_to_self_transaction( &mut self, tx_id: TxId, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, lock_height: Option, message: String, ) -> Result<(MicroTari, Transaction), OutputManagerError> { - let (inputs, _, total) = self.select_utxos(amount, fee_per_gram, 1, None).await?; + let (inputs, _, total) = self + .select_utxos(amount, fee_per_gram, 1, None, unique_id.as_ref()) + .await?; let offset = PrivateKey::random(&mut OsRng); let nonce = PrivateKey::random(&mut OsRng); @@ -750,6 +921,10 @@ where TBackend: OutputManagerBackend + 'static .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_tx_id(tx_id); + if let Some(ref unique_id) = unique_id { + builder = builder.with_unique_id(unique_id.clone()); + } + for uo in &inputs { builder.with_input( uo.unblinded_output @@ -782,6 +957,8 @@ where TBackend: OutputManagerBackend + 'static script_private_key, PublicKey::from_secret_key(&sender_offset_private_key), metadata_signature, + unique_id.clone(), + None, ), &self.resources.factories, )?; @@ -841,7 +1018,7 @@ where TBackend: OutputManagerBackend + 'static /// Confirm that a transaction has finished being negotiated between parties so the short-term encumberance can be /// made official - async fn confirm_encumberance(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { + async fn confirm_encumberance(&mut self, tx_id: TxId) -> Result<(), OutputManagerError> { self.resources.db.confirm_encumbered_outputs(tx_id).await?; Ok(()) @@ -852,7 +1029,7 @@ where TBackend: OutputManagerBackend + 'static /// be called by the Transaction Service which monitors the base chain. async fn confirm_transaction( &mut self, - tx_id: u64, + tx_id: TxId, inputs: &[TransactionInput], outputs: &[TransactionOutput], ) -> Result<(), OutputManagerError> { @@ -898,7 +1075,7 @@ where TBackend: OutputManagerBackend + 'static } /// Cancel a pending transaction and place the encumbered outputs back into the unspent pool - pub async fn cancel_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { + pub async fn cancel_transaction(&mut self, tx_id: TxId) -> Result<(), OutputManagerError> { debug!( target: LOG_TARGET, "Cancelling pending transaction outputs for TxId: {}", tx_id @@ -940,7 +1117,12 @@ where TBackend: OutputManagerBackend + 'static fee_per_gram: MicroTari, output_count: usize, strategy: Option, + unique_id: Option<&Vec>, ) -> Result<(Vec, bool, MicroTari), OutputManagerError> { + if unique_id.is_some() { + // Select UTXO that has this ID + todo!(); + } debug!( target: LOG_TARGET, "select_utxos amount: {}, fee_per_gram: {}, output_count: {}, strategy: {:?}", @@ -954,7 +1136,7 @@ where TBackend: OutputManagerBackend + 'static let mut fee_without_change = MicroTari::from(0); let mut fee_with_change = MicroTari::from(0); - let uo = self.resources.db.fetch_sorted_unspent_outputs().await?; + let uo = self.resources.db.fetch_spendable_outputs()?; // Attempt to get the chain tip height let chain_metadata = self.base_node_service.get_chain_metadata().await?; @@ -1086,7 +1268,7 @@ where TBackend: OutputManagerBackend + 'static pub async fn fetch_pending_transaction_outputs( &self, - ) -> Result, OutputManagerError> { + ) -> Result, OutputManagerError> { Ok(self.resources.db.fetch_all_pending_transaction_outputs().await?) } @@ -1094,9 +1276,8 @@ where TBackend: OutputManagerBackend + 'static Ok(self.resources.db.fetch_spent_outputs().await?) } - /// Sorted from lowest value to highest pub async fn fetch_unspent_outputs(&self) -> Result, OutputManagerError> { - Ok(self.resources.db.fetch_sorted_unspent_outputs().await?) + Ok(self.resources.db.fetch_all_unspent_outputs().await?) } pub async fn fetch_invalid_outputs(&self) -> Result, OutputManagerError> { @@ -1109,7 +1290,7 @@ where TBackend: OutputManagerBackend + 'static split_count: usize, fee_per_gram: MicroTari, lock_height: Option, - ) -> Result<(u64, Transaction, MicroTari, MicroTari), OutputManagerError> { + ) -> Result<(TxId, Transaction, MicroTari, MicroTari), OutputManagerError> { trace!( target: LOG_TARGET, "Select UTXOs and estimate coin split transaction fee." @@ -1122,6 +1303,7 @@ where TBackend: OutputManagerBackend + 'static fee_per_gram, output_count, Some(UTXOSelectionStrategy::Largest), + None, ) .await?; let input_count = inputs.len(); @@ -1191,6 +1373,8 @@ where TBackend: OutputManagerBackend + 'static script_private_key, sender_offset_public_key, metadata_signature, + None, + None, ), &self.resources.factories, )?; @@ -1273,6 +1457,8 @@ where TBackend: OutputManagerBackend + 'static known_one_sided_payment_scripts[i].private_key.clone(), output.sender_offset_public_key, output.metadata_signature, + output.unique_id, + output.parent_public_key, ); let db_output = DbUnblindedOutput::from_unblinded_output(rewound_output.clone(), &self.resources.factories)?; @@ -1302,7 +1488,6 @@ where TBackend: OutputManagerBackend + 'static } } } - Ok(rewound_outputs) } } diff --git a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs new file mode 100644 index 0000000000..e5ccc35165 --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs @@ -0,0 +1,87 @@ +use crate::output_manager_service::{ + error::OutputManagerStorageError, + storage::{ + database::{DbKey, DbValue, WriteOperation}, + models::DbUnblindedOutput, + }, +}; +use aes_gcm::Aes256Gcm; +use std::time::Duration; +use tari_common_types::types::{Commitment, PublicKey}; +use tari_core::transactions::{ + transaction::{OutputFlags, TransactionOutput}, + transaction_protocol::TxId, +}; + +/// This trait defines the required behaviour that a storage backend must provide for the Output Manager service. +/// Data is passed to and from the backend via the [DbKey], [DbValue], and [DbValueKey] enums. If new data types are +/// required to be supported by the backends then these enums can be updated to reflect this requirement and the trait +/// will remain the same +pub trait OutputManagerBackend: Send + Sync + Clone { + /// Retrieve the record associated with the provided DbKey + fn fetch(&self, key: &DbKey) -> Result, OutputManagerStorageError>; + + /// Fetch outputs that can be spent + fn fetch_spendable_outputs(&self) -> Result, OutputManagerStorageError>; + + /// Fetch outputs with specific features + fn fetch_with_features(&self, features: OutputFlags) -> Result, OutputManagerStorageError>; + + fn fetch_by_features_asset_public_key( + &self, + public_key: PublicKey, + ) -> Result; + + /// Modify the state the of the backend with a write operation + fn write(&self, op: WriteOperation) -> Result, OutputManagerStorageError>; + /// This method is called when a pending transaction is to be confirmed. It must move the `outputs_to_be_spent` and + /// `outputs_to_be_received` from a `PendingTransactionOutputs` record into the `unspent_outputs` and + /// `spent_outputs` collections. + fn confirm_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; + /// This method encumbers the specified outputs into a `PendingTransactionOutputs` record. This is a short term + /// encumberance in case the app is closed or crashes before transaction neogtiation is complete. These will be + /// cleared on startup of the service. + fn short_term_encumber_outputs( + &self, + tx_id: TxId, + outputs_to_send: &[DbUnblindedOutput], + outputs_to_receive: &[DbUnblindedOutput], + ) -> Result<(), OutputManagerStorageError>; + /// This method confirms that a transaction negotiation is complete and outputs can be fully encumbered. This + /// reserves these outputs until the transaction is confirmed or cancelled + fn confirm_encumbered_outputs(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; + /// Clear all pending transaction encumberances marked as short term. These are the result of an unfinished + /// transaction negotiation + fn clear_short_term_encumberances(&self) -> Result<(), OutputManagerStorageError>; + /// This method must take all the `outputs_to_be_spent` from the specified transaction and move them back into the + /// `UnspentOutputs` pool. The `outputs_to_be_received`'` will be marked as cancelled inbound outputs in case they + /// need to be recovered. + fn cancel_pending_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; + /// This method must run through all the `PendingTransactionOutputs` and test if any have existed for longer that + /// the specified duration. If they have they should be cancelled. + fn timeout_pending_transactions(&self, period: Duration) -> Result<(), OutputManagerStorageError>; + /// This method will increment the currently stored key index for the key manager config. Increment this after each + /// key is generated + fn increment_key_index(&self) -> Result<(), OutputManagerStorageError>; + /// This method will set the currently stored key index for the key manager + fn set_key_index(&self, index: u64) -> Result<(), OutputManagerStorageError>; + /// If an unspent output is detected as invalid (i.e. not available on the blockchain) then it should be moved to + /// the invalid outputs collection. The function will return the last recorded TxId associated with this output. + fn invalidate_unspent_output(&self, output: &DbUnblindedOutput) -> Result, OutputManagerStorageError>; + /// If an invalid output is found to be valid this function will turn it back into an unspent output + fn revalidate_unspent_output(&self, spending_key: &Commitment) -> Result<(), OutputManagerStorageError>; + /// Check to see if there exist any pending transaction with a blockheight equal that provided and cancel those + /// pending transaction outputs. + fn cancel_pending_transaction_at_block_height(&self, block_height: u64) -> Result<(), OutputManagerStorageError>; + /// Apply encryption to the backend. + fn apply_encryption(&self, cipher: Aes256Gcm) -> Result<(), OutputManagerStorageError>; + /// Remove encryption from the backend. + fn remove_encryption(&self) -> Result<(), OutputManagerStorageError>; + /// Update a Spent output to be Unspent + fn update_spent_output_to_unspent( + &self, + commitment: &Commitment, + ) -> Result; + + fn update_output_metadata_signature(&self, output: &TransactionOutput) -> Result<(), OutputManagerStorageError>; +} diff --git a/base_layer/wallet/src/output_manager_service/storage/database.rs b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs similarity index 85% rename from base_layer/wallet/src/output_manager_service/storage/database.rs rename to base_layer/wallet/src/output_manager_service/storage/database/mod.rs index 7344550c63..30bdcdb768 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs @@ -23,8 +23,7 @@ use crate::output_manager_service::{ error::OutputManagerStorageError, service::Balance, - storage::models::{DbUnblindedOutput, KnownOneSidedPaymentScript, OutputStatus}, - TxId, + storage::models::{DbUnblindedOutput, KnownOneSidedPaymentScript}, }; use aes_gcm::Aes256Gcm; use chrono::{NaiveDateTime, Utc}; @@ -35,76 +34,20 @@ use std::{ sync::Arc, time::Duration, }; -use tari_common_types::types::{BlindingFactor, Commitment, PrivateKey}; +use tari_common_types::types::{BlindingFactor, Commitment, PrivateKey, PublicKey}; use tari_core::transactions::{tari_amount::MicroTari, transaction::TransactionOutput}; const LOG_TARGET: &str = "wallet::output_manager_service::database"; -/// This trait defines the required behaviour that a storage backend must provide for the Output Manager service. -/// Data is passed to and from the backend via the [DbKey], [DbValue], and [DbValueKey] enums. If new data types are -/// required to be supported by the backends then these enums can be updated to reflect this requirement and the trait -/// will remain the same -pub trait OutputManagerBackend: Send + Sync + Clone { - /// Retrieve the record associated with the provided DbKey - fn fetch(&self, key: &DbKey) -> Result, OutputManagerStorageError>; - /// Modify the state the of the backend with a write operation - fn write(&self, op: WriteOperation) -> Result, OutputManagerStorageError>; - /// This method is called when a pending transaction is to be confirmed. It must move the `outputs_to_be_spent` and - /// `outputs_to_be_received` from a `PendingTransactionOutputs` record into the `unspent_outputs` and - /// `spent_outputs` collections. - fn confirm_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; - /// This method encumbers the specified outputs into a `PendingTransactionOutputs` record. This is a short term - /// encumberance in case the app is closed or crashes before transaction neogtiation is complete. These will be - /// cleared on startup of the service. - fn short_term_encumber_outputs( - &self, - tx_id: TxId, - outputs_to_send: &[DbUnblindedOutput], - outputs_to_receive: &[DbUnblindedOutput], - ) -> Result<(), OutputManagerStorageError>; - /// This method confirms that a transaction negotiation is complete and outputs can be fully encumbered. This - /// reserves these outputs until the transaction is confirmed or cancelled - fn confirm_encumbered_outputs(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; - /// Clear all pending transaction encumberances marked as short term. These are the result of an unfinished - /// transaction negotiation - fn clear_short_term_encumberances(&self) -> Result<(), OutputManagerStorageError>; - /// This method must take all the `outputs_to_be_spent` from the specified transaction and move them back into the - /// `UnspentOutputs` pool. The `outputs_to_be_received`'` will be marked as cancelled inbound outputs in case they - /// need to be recovered. - fn cancel_pending_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; - /// This method must run through all the `PendingTransactionOutputs` and test if any have existed for longer that - /// the specified duration. If they have they should be cancelled. - fn timeout_pending_transactions(&self, period: Duration) -> Result<(), OutputManagerStorageError>; - /// This method will increment the currently stored key index for the key manager config. Increment this after each - /// key is generated - fn increment_key_index(&self) -> Result<(), OutputManagerStorageError>; - /// This method will set the currently stored key index for the key manager - fn set_key_index(&self, index: u64) -> Result<(), OutputManagerStorageError>; - /// If an unspent output is detected as invalid (i.e. not available on the blockchain) then it should be moved to - /// the invalid outputs collection. The function will return the last recorded TxId associated with this output. - fn invalidate_unspent_output(&self, output: &DbUnblindedOutput) -> Result, OutputManagerStorageError>; - /// This method will update an output's metadata signature, akin to 'finalize output' - fn update_output_metadata_signature(&self, output: &TransactionOutput) -> Result<(), OutputManagerStorageError>; - /// If an invalid output is found to be valid this function will turn it back into an unspent output - fn revalidate_unspent_output(&self, spending_key: &Commitment) -> Result<(), OutputManagerStorageError>; - /// Check to see if there exist any pending transaction with a blockheight equal that provided and cancel those - /// pending transaction outputs. - fn cancel_pending_transaction_at_block_height(&self, block_height: u64) -> Result<(), OutputManagerStorageError>; - /// Apply encryption to the backend. - fn apply_encryption(&self, cipher: Aes256Gcm) -> Result<(), OutputManagerStorageError>; - /// Remove encryption from the backend. - fn remove_encryption(&self) -> Result<(), OutputManagerStorageError>; - /// Update a Spent output to be Unspent - fn update_spent_output_to_unspent( - &self, - commitment: &Commitment, - ) -> Result; -} +mod backend; +use crate::output_manager_service::storage::OutputStatus; +pub use backend::OutputManagerBackend; +use tari_core::transactions::{transaction::OutputFlags, transaction_protocol::TxId}; /// Holds the outputs that have been selected for a given pending transaction waiting for confirmation #[derive(Debug, Clone, PartialEq)] pub struct PendingTransactionOutputs { - pub tx_id: u64, + pub tx_id: TxId, pub outputs_to_be_spent: Vec, pub outputs_to_be_received: Vec, pub timestamp: NaiveDateTime, @@ -467,24 +410,17 @@ where T: OutputManagerBackend + 'static .and_then(|inner_result| inner_result) } - /// Retrieves UTXOs sorted by value from smallest to largest. - pub async fn fetch_sorted_unspent_outputs(&self) -> Result, OutputManagerStorageError> { - let db_clone = self.db.clone(); - - let mut uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::UnspentOutputs) { - Ok(None) => log_error( - DbKey::UnspentOutputs, - OutputManagerStorageError::UnexpectedResult("Could not retrieve unspent outputs".to_string()), - ), - Ok(Some(DbValue::UnspentOutputs(uo))) => Ok(uo), - Ok(Some(other)) => unexpected_result(DbKey::UnspentOutputs, other), - Err(e) => log_error(DbKey::UnspentOutputs, e), - }) - .await - .map_err(|err| OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))??; + pub async fn fetch_all_unspent_outputs(&self) -> Result, OutputManagerStorageError> { + let result = match self.db.fetch(&DbKey::UnspentOutputs)? { + Some(DbValue::UnspentOutputs(outputs)) => outputs, + Some(other) => return unexpected_result(DbKey::UnspentOutputs, other), + None => vec![], + }; + Ok(result) + } - uo.sort(); - Ok(uo) + pub fn fetch_spendable_outputs(&self) -> Result, OutputManagerStorageError> { + self.db.fetch_spendable_outputs() } pub async fn fetch_spent_outputs(&self) -> Result, OutputManagerStorageError> { @@ -506,7 +442,7 @@ where T: OutputManagerBackend + 'static pub async fn fetch_all_pending_transaction_outputs( &self, - ) -> Result, OutputManagerStorageError> { + ) -> Result, OutputManagerStorageError> { let db_clone = self.db.clone(); let uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::AllPendingTransactionOutputs) { @@ -542,6 +478,21 @@ where T: OutputManagerBackend + 'static Ok(uo) } + pub async fn fetch_with_features( + &self, + feature: OutputFlags, + ) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + db_clone.fetch_with_features(feature) + } + + pub fn fetch_by_features_asset_public_key( + &self, + public_key: PublicKey, + ) -> Result { + self.db.fetch_by_features_asset_public_key(public_key) + } + pub async fn get_spent_outputs(&self) -> Result, OutputManagerStorageError> { let db_clone = self.db.clone(); diff --git a/base_layer/wallet/src/output_manager_service/storage/mod.rs b/base_layer/wallet/src/output_manager_service/storage/mod.rs index ee67147cff..64149adebd 100644 --- a/base_layer/wallet/src/output_manager_service/storage/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/mod.rs @@ -23,3 +23,5 @@ pub mod database; pub mod models; pub mod sqlite_db; +mod output_status; +pub use output_status::*; diff --git a/base_layer/wallet/src/output_manager_service/storage/models.rs b/base_layer/wallet/src/output_manager_service/storage/models.rs index e0f00a0569..4e87c09fa5 100644 --- a/base_layer/wallet/src/output_manager_service/storage/models.rs +++ b/base_layer/wallet/src/output_manager_service/storage/models.rs @@ -30,13 +30,14 @@ use tari_core::{ transactions::{transaction::UnblindedOutput, transaction_protocol::RewindData, CryptoFactories}, }; -use crate::output_manager_service::error::OutputManagerStorageError; +use crate::output_manager_service::{error::OutputManagerStorageError, storage::OutputStatus}; #[derive(Debug, Clone)] pub struct DbUnblindedOutput { pub commitment: Commitment, pub unblinded_output: UnblindedOutput, pub hash: HashOutput, + pub status: OutputStatus, } impl DbUnblindedOutput { @@ -44,11 +45,12 @@ impl DbUnblindedOutput { output: UnblindedOutput, factory: &CryptoFactories, ) -> Result { - let tx_out = output.as_transaction_output(factory)?; + let tx_out = output.as_transaction_output(factory, false)?; Ok(DbUnblindedOutput { hash: tx_out.hash(), commitment: tx_out.commitment, unblinded_output: output, + status: OutputStatus::NotStored, }) } @@ -57,11 +59,12 @@ impl DbUnblindedOutput { factory: &CryptoFactories, rewind_data: &RewindData, ) -> Result { - let tx_out = output.as_rewindable_transaction_output(factory, rewind_data)?; + let tx_out = output.as_rewindable_transaction_output(factory, rewind_data, false)?; Ok(DbUnblindedOutput { hash: tx_out.hash(), commitment: tx_out.commitment, unblinded_output: output, + status: OutputStatus::NotStored, }) } } @@ -105,14 +108,3 @@ impl PartialEq for KnownOneSidedPaymentScript { self.script_hash == other.script_hash } } - -/// The status of a given output -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum OutputStatus { - Unspent, - Spent, - EncumberedToBeReceived, - EncumberedToBeSpent, - Invalid, - CancelledInbound, -} diff --git a/base_layer/wallet/src/output_manager_service/storage/output_status.rs b/base_layer/wallet/src/output_manager_service/storage/output_status.rs new file mode 100644 index 0000000000..de3b97d694 --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/output_status.rs @@ -0,0 +1,87 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::output_manager_service::error::OutputManagerStorageError; +use core::{ + convert::TryFrom, + result::{ + Result, + Result::{Err, Ok}, + }, +}; +use std::{fmt, fmt::Formatter}; + +/// The status of a given output +#[derive(Debug, Clone, PartialEq, Copy)] +pub enum OutputStatus { + Unspent, + Spent, + EncumberedToBeReceived, + EncumberedToBeSpent, + Invalid, + CancelledInbound, + NotStored, +} + +impl TryFrom for OutputStatus { + type Error = OutputManagerStorageError; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(OutputStatus::Unspent), + 1 => Ok(OutputStatus::Spent), + 2 => Ok(OutputStatus::EncumberedToBeReceived), + 3 => Ok(OutputStatus::EncumberedToBeSpent), + 4 => Ok(OutputStatus::Invalid), + 5 => Ok(OutputStatus::CancelledInbound), + 6 => Ok(OutputStatus::NotStored), + _ => Err(OutputManagerStorageError::ConversionError), + } + } +} + +impl fmt::Display for OutputStatus { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + OutputStatus::Unspent => { + write!(f, "Unspent") + }, + OutputStatus::Spent => { + write!(f, "Spent") + }, + OutputStatus::EncumberedToBeReceived => { + write!(f, "EncumberedToBeReceived") + }, + OutputStatus::EncumberedToBeSpent => { + write!(f, "EncumberedToBeSpent") + }, + OutputStatus::Invalid => { + write!(f, "Invalid") + }, + OutputStatus::CancelledInbound => { + write!(f, "CancelledInbound") + }, + OutputStatus::NotStored => { + write!(f, "NotStored") + }, + } + } +} diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs similarity index 82% rename from base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs rename to base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 052bad580b..4fdbf7f250 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -46,7 +46,14 @@ use tari_core::{ tari_utilities::hash::Hashable, transactions::{ tari_amount::MicroTari, - transaction::{OutputFeatures, OutputFlags, TransactionOutput, UnblindedOutput}, + transaction::{ + AssetOutputFeatures, + MintNonFungibleFeatures, + OutputFeatures, + OutputFlags, + TransactionOutput, + UnblindedOutput, + }, CryptoFactories, }, }; @@ -64,17 +71,29 @@ use crate::{ PendingTransactionOutputs, WriteOperation, }, - models::{DbUnblindedOutput, KnownOneSidedPaymentScript, OutputStatus}, + models::{DbUnblindedOutput, KnownOneSidedPaymentScript}, + sqlite_db::{new_output_sql::NewOutputSql, output_sql::OutputSql}, + OutputStatus, }, - TxId, }, - schema::{key_manager_states, known_one_sided_payment_scripts, outputs, pending_transaction_outputs}, + schema::{ + key_manager_states, + known_one_sided_payment_scripts, + outputs, + outputs::columns, + pending_transaction_outputs, + }, storage::sqlite_utilities::WalletDbConnection, util::encryption::{decrypt_bytes_integral_nonce, encrypt_bytes_integral_nonce, Encryptable}, }; +use std::convert::TryInto; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::output_manager_service::database::sqlite_db"; +mod new_output_sql; +mod output_sql; + /// A Sqlite backend for the Output Manager Service. The Backend is accessed via a connection pool to the Sqlite file. #[derive(Clone)] pub struct OutputManagerSqliteDatabase { @@ -164,7 +183,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { } Some(DbValue::PendingTransactionOutputs(Box::new( pending_transaction_outputs_from_sql_outputs( - p.tx_id as u64, + (p.tx_id as u64).into(), &p.timestamp, outputs, p.coinbase_block_height.map(|h| h as u64), @@ -234,16 +253,16 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { let pending_sql_txs = PendingTransactionOutputSql::index(&(*conn))?; let mut pending_txs = HashMap::new(); for p_tx in pending_sql_txs { - let mut outputs = OutputSql::find_by_tx_id_and_encumbered(p_tx.tx_id as u64, &(*conn))?; + let mut outputs = OutputSql::find_by_tx_id_and_encumbered((p_tx.tx_id as u64).into(), &(*conn))?; for o in outputs.iter_mut() { self.decrypt_if_necessary(o)?; } pending_txs.insert( - p_tx.tx_id as u64, + (p_tx.tx_id as u64).into(), pending_transaction_outputs_from_sql_outputs( - p_tx.tx_id as u64, + (p_tx.tx_id as u64).into(), &p_tx.timestamp, outputs, p_tx.coinbase_block_height.map(|h| h as u64), @@ -291,6 +310,32 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { Ok(result) } + fn fetch_with_features(&self, flags: OutputFlags) -> Result, OutputManagerStorageError> { + let conn = self.database_connection.acquire_lock(); + let mut outputs = OutputSql::index_by_feature_flags(flags, &conn)?; + for o in outputs.iter_mut() { + self.decrypt_if_necessary(o)?; + } + + outputs + .iter() + .map(|o| DbUnblindedOutput::try_from(o.clone())) + .collect::, _>>() + } + + fn fetch_by_features_asset_public_key( + &self, + public_key: PublicKey, + ) -> Result { + let conn = self.database_connection.acquire_lock(); + let mut o: OutputSql = outputs::table + .filter(columns::features_asset_public_key.eq(public_key.to_vec())) + .filter(outputs::status.eq(OutputStatus::Unspent as i32)) + .first(&*conn)?; + self.decrypt_if_necessary(&mut o)?; + o.try_into() + } + #[allow(clippy::cognitive_complexity)] fn write(&self, op: WriteOperation) -> Result, OutputManagerStorageError> { let conn = self.database_connection.acquire_lock(); @@ -301,7 +346,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, &(*conn)).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let mut new_output = NewOutputSql::new(*o, OutputStatus::Spent, None)?; + let mut new_output = NewOutputSql::new(*o, OutputStatus::Spent, None); self.encrypt_if_necessary(&mut new_output)?; @@ -311,7 +356,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, &(*conn)).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let mut new_output = NewOutputSql::new(*o, OutputStatus::Unspent, None)?; + let mut new_output = NewOutputSql::new(*o, OutputStatus::Unspent, None); self.encrypt_if_necessary(&mut new_output)?; new_output.commit(&(*conn))? }, @@ -319,7 +364,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, &(*conn)).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let mut new_output = NewOutputSql::new(*o, OutputStatus::Unspent, Some(tx_id))?; + let mut new_output = NewOutputSql::new(*o, OutputStatus::Unspent, Some(tx_id)); self.encrypt_if_necessary(&mut new_output)?; new_output.commit(&(*conn))? }, @@ -329,19 +374,19 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { } PendingTransactionOutputSql::new( - p.tx_id, + p.tx_id.as_u64() as i64, true, p.timestamp, p.coinbase_block_height.map(|h| h as i64), ) .commit(&(*conn))?; for o in p.outputs_to_be_spent { - let mut new_output = NewOutputSql::new(o, OutputStatus::EncumberedToBeSpent, Some(p.tx_id))?; + let mut new_output = NewOutputSql::new(o, OutputStatus::EncumberedToBeSpent, Some(p.tx_id)); self.encrypt_if_necessary(&mut new_output)?; new_output.commit(&(*conn))?; } for o in p.outputs_to_be_received { - let mut new_output = NewOutputSql::new(o, OutputStatus::EncumberedToBeReceived, Some(p.tx_id))?; + let mut new_output = NewOutputSql::new(o, OutputStatus::EncumberedToBeReceived, Some(p.tx_id)); self.encrypt_if_necessary(&mut new_output)?; new_output.commit(&(*conn))?; } @@ -412,7 +457,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { }, DbKey::PendingTransactionOutputs(tx_id) => match PendingTransactionOutputSql::find(tx_id, &(*conn)) { Ok(p) => { - let mut outputs = OutputSql::find_by_tx_id_and_encumbered(p.tx_id as u64, &(*conn))?; + let mut outputs = OutputSql::find_by_tx_id_and_encumbered((p.tx_id as u64).into(), &(*conn))?; for o in outputs.iter_mut() { self.decrypt_if_necessary(o)?; @@ -421,7 +466,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { p.delete(&(*conn))?; return Ok(Some(DbValue::PendingTransactionOutputs(Box::new( pending_transaction_outputs_from_sql_outputs( - p.tx_id as u64, + (p.tx_id as u64).into(), &p.timestamp, outputs, p.coinbase_block_height.map(|h| h as u64), @@ -449,7 +494,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { Ok(None) } - fn confirm_transaction(&self, tx_id: u64) -> Result<(), OutputManagerStorageError> { + fn confirm_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { let conn = self.database_connection.acquire_lock(); match PendingTransactionOutputSql::find(tx_id, &(*conn)) { @@ -499,7 +544,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { fn short_term_encumber_outputs( &self, - tx_id: u64, + tx_id: TxId, outputs_to_send: &[DbUnblindedOutput], outputs_to_receive: &[DbUnblindedOutput], ) -> Result<(), OutputManagerStorageError> { @@ -511,27 +556,27 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { if output.status == (OutputStatus::Spent as i32) { return Err(OutputManagerStorageError::OutputAlreadySpent); } + if output.status == (OutputStatus::EncumberedToBeSpent as i32) { + return Err(OutputManagerStorageError::OutputAlreadyEncumbered); + } outputs_to_be_spent.push(output); } - PendingTransactionOutputSql::new(tx_id, true, Utc::now().naive_utc(), None).commit(&(*conn))?; + PendingTransactionOutputSql::new(tx_id.as_u64() as i64, true, Utc::now().naive_utc(), None).commit(&(*conn))?; for o in outputs_to_be_spent { o.update( UpdateOutput { status: Some(OutputStatus::EncumberedToBeSpent), - tx_id: Some(tx_id), - spending_key: None, - script_private_key: None, - metadata_signature_nonce: None, - metadata_signature_u_key: None, + tx_id: Some(tx_id.into()), + ..Default::default() }, &(*conn), )?; } for co in outputs_to_receive { - let mut new_output = NewOutputSql::new(co.clone(), OutputStatus::EncumberedToBeReceived, Some(tx_id))?; + let mut new_output = NewOutputSql::new(co.clone(), OutputStatus::EncumberedToBeReceived, Some(tx_id)); self.encrypt_if_necessary(&mut new_output)?; new_output.commit(&(*conn))?; } @@ -566,13 +611,13 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { drop(conn); for pto in pending_transaction_outputs.iter() { - self.cancel_pending_transaction(pto.tx_id as u64)?; + self.cancel_pending_transaction((pto.tx_id as u64).into())?; } Ok(()) } - fn cancel_pending_transaction(&self, tx_id: u64) -> Result<(), OutputManagerStorageError> { + fn cancel_pending_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { let conn = self.database_connection.acquire_lock(); match PendingTransactionOutputSql::find(tx_id, &(*conn)) { @@ -584,11 +629,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { o.update( UpdateOutput { status: Some(OutputStatus::CancelledInbound), - tx_id: None, - spending_key: None, - script_private_key: None, - metadata_signature_nonce: None, - metadata_signature_u_key: None, + ..Default::default() }, &(*conn), )?; @@ -596,11 +637,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { o.update( UpdateOutput { status: Some(OutputStatus::Unspent), - tx_id: None, - spending_key: None, - script_private_key: None, - metadata_signature_nonce: None, - metadata_signature_u_key: None, + ..Default::default() }, &(*conn), )?; @@ -619,7 +656,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { }; }, } - Ok(()) } @@ -632,7 +668,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { )?; drop(conn); for ptx in older_pending_txs { - self.cancel_pending_transaction(ptx.tx_id as u64)?; + self.cancel_pending_transaction((ptx.tx_id as u64).into())?; } Ok(()) } @@ -656,15 +692,11 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { fn invalidate_unspent_output(&self, output: &DbUnblindedOutput) -> Result, OutputManagerStorageError> { let conn = self.database_connection.acquire_lock(); let output = OutputSql::find_by_commitment_and_cancelled(&output.commitment.to_vec(), false, &conn)?; - let tx_id = output.tx_id.map(|id| id as u64); + let tx_id = output.tx_id.map(|id| (id as u64).into()); output.update( UpdateOutput { status: Some(OutputStatus::Invalid), - tx_id: None, - spending_key: None, - script_private_key: None, - metadata_signature_nonce: None, - metadata_signature_u_key: None, + ..Default::default() }, &(*conn), )?; @@ -677,12 +709,9 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { let db_output = OutputSql::find_by_commitment_and_cancelled(&output.commitment.to_vec(), false, &conn)?; db_output.update( UpdateOutput { - status: None, - tx_id: None, - spending_key: None, - script_private_key: None, metadata_signature_nonce: Some(output.metadata_signature.public_nonce().to_vec()), metadata_signature_u_key: Some(output.metadata_signature.u().to_vec()), + ..Default::default() }, &(*conn), )?; @@ -700,11 +729,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { output.update( UpdateOutput { status: Some(OutputStatus::Unspent), - tx_id: None, - spending_key: None, - script_private_key: None, - metadata_signature_nonce: None, - metadata_signature_u_key: None, + ..Default::default() }, &(*conn), )?; @@ -725,11 +750,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { let mut o = output.update( UpdateOutput { status: Some(OutputStatus::Unspent), - tx_id: None, - spending_key: None, - script_private_key: None, - metadata_signature_nonce: None, - metadata_signature_u_key: None, + ..Default::default() }, &(*conn), )?; @@ -745,7 +766,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { pending_txs = PendingTransactionOutputSql::index_block_height(block_height as i64, &conn)?; } for p in pending_txs { - self.cancel_pending_transaction(p.tx_id as u64)?; + self.cancel_pending_transaction((p.tx_id as u64).into())?; } Ok(()) } @@ -846,6 +867,27 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { let _ = (*current_cipher).take(); Ok(()) } + + fn fetch_spendable_outputs(&self) -> Result, OutputManagerStorageError> { + let conn = self.database_connection.acquire_lock(); + // Only select spendable normal utxos and coinbases + let mut outputs: Vec = outputs::table + .filter( + outputs::flags + .eq(OutputFlags::empty().bits() as i32) + .or(outputs::flags.eq(OutputFlags::COINBASE_OUTPUT.bits() as i32)), + ) + .filter(columns::status.eq(OutputStatus::Unspent as i32)) + .load(&*conn)?; + + for o in outputs.iter_mut() { + self.decrypt_if_necessary(o)?; + } + outputs + .iter() + .map(|o| DbUnblindedOutput::try_from(o.clone())) + .collect::, _>>() + } } /// A utility function to construct a PendingTransactionOutputs structure for a TxId, set of Outputs and a Timestamp @@ -874,274 +916,34 @@ fn pending_transaction_outputs_from_sql_outputs( }) } -impl TryFrom for OutputStatus { - type Error = OutputManagerStorageError; - - fn try_from(value: i32) -> Result { - match value { - 0 => Ok(OutputStatus::Unspent), - 1 => Ok(OutputStatus::Spent), - 2 => Ok(OutputStatus::EncumberedToBeReceived), - 3 => Ok(OutputStatus::EncumberedToBeSpent), - 4 => Ok(OutputStatus::Invalid), - 5 => Ok(OutputStatus::CancelledInbound), - _ => Err(OutputManagerStorageError::ConversionError), - } - } -} - -/// This struct represents an Output in the Sql database. A distinct struct is required to define the Sql friendly -/// equivalent datatypes for the members. -#[derive(Clone, Debug, Insertable, PartialEq)] -#[table_name = "outputs"] -struct NewOutputSql { - commitment: Option>, - spending_key: Vec, - value: i64, - flags: i32, - maturity: i64, - status: i32, - tx_id: Option, - hash: Option>, - script: Vec, - input_data: Vec, - script_private_key: Vec, - sender_offset_public_key: Vec, - metadata_signature_nonce: Vec, - metadata_signature_u_key: Vec, - metadata_signature_v_key: Vec, -} - -impl NewOutputSql { - pub fn new( - output: DbUnblindedOutput, - status: OutputStatus, - tx_id: Option, - ) -> Result { - Ok(Self { - commitment: Some(output.commitment.to_vec()), - spending_key: output.unblinded_output.spending_key.to_vec(), - value: (u64::from(output.unblinded_output.value)) as i64, - flags: output.unblinded_output.features.flags.bits() as i32, - maturity: output.unblinded_output.features.maturity as i64, - status: status as i32, - tx_id: tx_id.map(|i| i as i64), - hash: Some(output.hash), - script: output.unblinded_output.script.as_bytes(), - input_data: output.unblinded_output.input_data.as_bytes(), - script_private_key: output.unblinded_output.script_private_key.to_vec(), - sender_offset_public_key: output.unblinded_output.sender_offset_public_key.to_vec(), - metadata_signature_nonce: output.unblinded_output.metadata_signature.public_nonce().to_vec(), - metadata_signature_u_key: output.unblinded_output.metadata_signature.u().to_vec(), - metadata_signature_v_key: output.unblinded_output.metadata_signature.v().to_vec(), - }) - } - - /// Write this struct to the database - pub fn commit(&self, conn: &SqliteConnection) -> Result<(), OutputManagerStorageError> { - diesel::insert_into(outputs::table).values(self.clone()).execute(conn)?; - Ok(()) - } -} - -impl Encryptable for NewOutputSql { - fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = encrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = encrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; - Ok(()) - } - - fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = decrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = decrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; - Ok(()) - } -} - -#[derive(Clone, Debug, Queryable, Identifiable, PartialEq)] -#[table_name = "outputs"] -struct OutputSql { - id: i32, - commitment: Option>, - spending_key: Vec, - value: i64, - flags: i32, - maturity: i64, - status: i32, - tx_id: Option, - hash: Option>, - script: Vec, - input_data: Vec, - script_private_key: Vec, - sender_offset_public_key: Vec, - metadata_signature_nonce: Vec, - metadata_signature_u_key: Vec, - metadata_signature_v_key: Vec, -} - -impl OutputSql { - /// Return all outputs - pub fn index(conn: &SqliteConnection) -> Result, OutputManagerStorageError> { - Ok(outputs::table.load::(conn)?) - } - - /// Return all outputs with a given status - pub fn index_status( - status: OutputStatus, - conn: &SqliteConnection, - ) -> Result, OutputManagerStorageError> { - Ok(outputs::table.filter(outputs::status.eq(status as i32)).load(conn)?) - } - - /// Return all unspent outputs that have a maturity above the provided chain tip - pub fn index_time_locked(tip: u64, conn: &SqliteConnection) -> Result, OutputManagerStorageError> { - Ok(outputs::table - .filter(outputs::status.eq(OutputStatus::Unspent as i32)) - .filter(outputs::maturity.gt(tip as i64)) - .load(conn)?) - } - - /// Find a particular Output, if it exists - pub fn find(spending_key: &[u8], conn: &SqliteConnection) -> Result { - Ok(outputs::table - .filter(outputs::spending_key.eq(spending_key)) - .first::(conn)?) - } - - pub fn find_by_commitment( - commitment: &[u8], - conn: &SqliteConnection, - ) -> Result { - Ok(outputs::table - .filter(outputs::commitment.eq(commitment)) - .first::(conn)?) - } - - pub fn find_by_commitment_and_cancelled( - commitment: &[u8], - cancelled: bool, - conn: &SqliteConnection, - ) -> Result { - let cancelled_flag = OutputStatus::CancelledInbound as i32; - - let mut request = outputs::table.filter(outputs::commitment.eq(commitment)).into_boxed(); - if cancelled { - request = request.filter(outputs::status.eq(cancelled_flag)) - } else { - request = request.filter(outputs::status.ne(cancelled_flag)) - }; - - Ok(request.first::(conn)?) - } - - pub fn find_by_tx_id_and_status( - tx_id: TxId, - status: OutputStatus, - conn: &SqliteConnection, - ) -> Result, OutputManagerStorageError> { - Ok(outputs::table - .filter(outputs::tx_id.eq(Some(tx_id as i64))) - .filter(outputs::status.eq(status as i32)) - .load(conn)?) - } - - /// Find outputs via tx_id that are encumbered. Any outputs that are encumbered cannot be marked as spent. - pub fn find_by_tx_id_and_encumbered( - tx_id: TxId, - conn: &SqliteConnection, - ) -> Result, OutputManagerStorageError> { - Ok(outputs::table - .filter(outputs::tx_id.eq(Some(tx_id as i64))) - .filter( - outputs::status - .eq(OutputStatus::EncumberedToBeReceived as i32) - .or(outputs::status.eq(OutputStatus::EncumberedToBeSpent as i32)), - ) - .load(conn)?) - } - - /// Find a particular Output, if it exists and is in the specified Spent state - pub fn find_status( - spending_key: &[u8], - status: OutputStatus, - conn: &SqliteConnection, - ) -> Result { - Ok(outputs::table - .filter(outputs::status.eq(status as i32)) - .filter(outputs::spending_key.eq(spending_key)) - .first::(conn)?) - } - - pub fn delete(&self, conn: &SqliteConnection) -> Result<(), OutputManagerStorageError> { - let num_deleted = - diesel::delete(outputs::table.filter(outputs::spending_key.eq(&self.spending_key))).execute(conn)?; - - if num_deleted == 0 { - return Err(OutputManagerStorageError::ValuesNotFound); - } - - Ok(()) - } - - pub fn update( - &self, - updated_output: UpdateOutput, - conn: &SqliteConnection, - ) -> Result { - let num_updated = diesel::update(outputs::table.filter(outputs::id.eq(&self.id))) - .set(UpdateOutputSql::from(updated_output)) - .execute(conn)?; - - if num_updated == 0 { - return Err(OutputManagerStorageError::UnexpectedResult( - "Database update error".to_string(), - )); - } - - OutputSql::find(&self.spending_key, conn) - } - - /// This function is used to update an existing record to set fields to null - pub fn update_null( - &self, - updated_null: NullOutputSql, - conn: &SqliteConnection, - ) -> Result { - let num_updated = diesel::update(outputs::table.filter(outputs::spending_key.eq(&self.spending_key))) - .set(updated_null) - .execute(conn)?; - - if num_updated == 0 { - return Err(OutputManagerStorageError::UnexpectedResult( - "Database update error".to_string(), - )); - } - - OutputSql::find(&self.spending_key, conn) - } - - /// Update the changed fields of this record after encryption/decryption is performed - pub fn update_encryption(&self, conn: &SqliteConnection) -> Result<(), OutputManagerStorageError> { - let _ = self.update( - UpdateOutput { - status: None, - tx_id: None, - spending_key: Some(self.spending_key.clone()), - script_private_key: Some(self.script_private_key.clone()), - metadata_signature_nonce: None, - metadata_signature_u_key: None, - }, - conn, - )?; - Ok(()) - } -} - /// Conversion from an DbUnblindedOutput to the Sql datatype form impl TryFrom for DbUnblindedOutput { type Error = OutputManagerStorageError; fn try_from(o: OutputSql) -> Result { + let asset_features = match o.features_asset_public_key { + Some(ref public_key) => Some(AssetOutputFeatures { + public_key: PublicKey::from_bytes(public_key)?, + }), + None => None, + }; + let mint_non_fungible = match o.features_mint_asset_public_key { + Some(ref public_key) => Some(MintNonFungibleFeatures { + asset_public_key: PublicKey::from_bytes(public_key)?, + asset_owner_commitment: o + .features_mint_asset_owner_commitment + .map(|ao| Commitment::from_bytes(&ao)) + .unwrap()?, + }), + None => None, + }; + let features = OutputFeatures { + flags: OutputFlags::from_bits(o.flags as u8).ok_or(OutputManagerStorageError::ConversionError)?, + maturity: o.maturity as u64, + metadata: o.metadata.unwrap_or_default(), + asset: asset_features, + mint_non_fungible, + }; let unblinded_output = UnblindedOutput::new( MicroTari::from(o.value as u64), PrivateKey::from_vec(&o.spending_key).map_err(|_| { @@ -1151,10 +953,7 @@ impl TryFrom for DbUnblindedOutput { ); OutputManagerStorageError::ConversionError })?, - OutputFeatures { - flags: OutputFlags::from_bits(o.flags as u8).ok_or(OutputManagerStorageError::ConversionError)?, - maturity: o.maturity as u64, - }, + features, TariScript::from_bytes(o.script.as_slice())?, ExecutionStack::from_bytes(o.input_data.as_slice())?, PrivateKey::from_vec(&o.script_private_key).map_err(|_| { @@ -1194,12 +993,14 @@ impl TryFrom for DbUnblindedOutput { OutputManagerStorageError::ConversionError })?, ), + o.unique_id.clone(), + o.parent_public_key.map(|p| PublicKey::from_bytes(&p)).transpose()?, ); let hash = match o.hash { None => { let factories = CryptoFactories::default(); - unblinded_output.as_transaction_output(&factories)?.hash() + unblinded_output.as_transaction_output(&factories, false)?.hash() }, Some(v) => v, }; @@ -1217,56 +1018,37 @@ impl TryFrom for DbUnblindedOutput { commitment, unblinded_output, hash, + status: o.status.try_into()?, }) } } -impl Encryptable for OutputSql { - fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = encrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = encrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; - Ok(()) - } - - fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = decrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = decrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; - Ok(()) - } -} - -impl From for NewOutputSql { - fn from(o: OutputSql) -> Self { - Self { - commitment: o.commitment, - spending_key: o.spending_key, - value: o.value, - flags: o.flags, - maturity: o.maturity, - status: o.status, - tx_id: o.tx_id, - hash: o.hash, - script: o.script, - input_data: o.input_data, - script_private_key: o.script_private_key, - sender_offset_public_key: o.sender_offset_public_key, - metadata_signature_nonce: o.metadata_signature_nonce, - metadata_signature_u_key: o.metadata_signature_u_key, - metadata_signature_v_key: o.metadata_signature_v_key, - } - } -} - -impl PartialEq for OutputSql { - fn eq(&self, other: &NewOutputSql) -> bool { - &NewOutputSql::from(self.clone()) == other - } -} +// impl From for NewOutputSql { +// fn from(o: OutputSql) -> Self { +// Self { +// commitment: o.commitment, +// spending_key: o.spending_key, +// value: o.value, +// flags: o.flags, +// maturity: o.maturity, +// status: o.status, +// tx_id: o.tx_id, +// hash: o.hash, +// script: o.script, +// input_data: o.input_data, +// height: o.height, +// script_private_key: o.script_private_key, +// script_offset_public_key: o.script_offset_public_key, +// metadata: o.metadata +// } +// } +// } /// These are the fields that can be updated for an Output +#[derive(Default)] pub struct UpdateOutput { status: Option, - tx_id: Option, + tx_id: Option, spending_key: Option>, script_private_key: Option>, metadata_signature_nonce: Option>, @@ -1317,9 +1099,9 @@ struct PendingTransactionOutputSql { coinbase_block_height: Option, } impl PendingTransactionOutputSql { - pub fn new(tx_id: TxId, short_term: bool, timestamp: NaiveDateTime, coinbase_block_height: Option) -> Self { + pub fn new(tx_id: i64, short_term: bool, timestamp: NaiveDateTime, coinbase_block_height: Option) -> Self { Self { - tx_id: tx_id as i64, + tx_id, short_term: short_term as i32, timestamp, coinbase_block_height, @@ -1338,7 +1120,7 @@ impl PendingTransactionOutputSql { conn: &SqliteConnection, ) -> Result { Ok(pending_transaction_outputs::table - .filter(pending_transaction_outputs::tx_id.eq(tx_id as i64)) + .filter(pending_transaction_outputs::tx_id.eq(tx_id.as_u64() as i64)) .first::(conn)?) } @@ -1383,7 +1165,7 @@ impl PendingTransactionOutputSql { return Err(OutputManagerStorageError::ValuesNotFound); } - let outputs = OutputSql::find_by_tx_id_and_encumbered(self.tx_id as u64, &(*conn))?; + let outputs = OutputSql::find_by_tx_id_and_encumbered((self.tx_id as u64).into(), &(*conn))?; for o in outputs { o.delete(&(*conn))?; } @@ -1408,7 +1190,7 @@ impl PendingTransactionOutputSql { )); } - PendingTransactionOutputSql::find(self.tx_id as u64, conn) + PendingTransactionOutputSql::find((self.tx_id as u64).into(), conn) } } @@ -1745,7 +1527,6 @@ mod test { models::DbUnblindedOutput, sqlite_db::{ KeyManagerStateSql, - NewOutputSql, OutputManagerSqliteDatabase, OutputSql, OutputStatus, diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs new file mode 100644 index 0000000000..33e319083c --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -0,0 +1,106 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use aes_gcm::{Aes256Gcm, Error as AeadError}; +use diesel::{RunQueryDsl, SqliteConnection}; + +use tari_core::crypto::tari_utilities::ByteArray; +use tari_core::transactions::transaction_protocol::TxId; + +use crate::output_manager_service::error::OutputManagerStorageError; +use crate::output_manager_service::storage::models::DbUnblindedOutput; +use crate::output_manager_service::storage::OutputStatus; +use crate::schema::outputs; +use crate::util::encryption::{decrypt_bytes_integral_nonce, encrypt_bytes_integral_nonce, Encryptable}; + +/// This struct represents an Output in the Sql database. A distinct struct is required to define the Sql friendly +/// equivalent datatypes for the members. +#[derive(Clone, Debug, Insertable, PartialEq)] +#[table_name = "outputs"] +pub struct NewOutputSql { + commitment: Option>, + spending_key: Vec, + value: i64, + flags: i32, + maturity: i64, + status: i32, + tx_id: Option, + hash: Option>, + script: Vec, + input_data: Vec, + script_private_key: Vec, + metadata: Option>, + features_asset_public_key: Option>, + unique_id: Option>, + parent_public_key: Option>, + sender_offset_public_key: Vec, + metadata_signature_nonce: Vec, + metadata_signature_u_key: Vec, + metadata_signature_v_key: Vec, +} + +impl NewOutputSql { + pub fn new(output: DbUnblindedOutput, status: OutputStatus, tx_id: Option) -> Self { + Self { + commitment: Some(output.commitment.to_vec()), + spending_key: output.unblinded_output.spending_key.to_vec(), + value: (u64::from(output.unblinded_output.value)) as i64, + flags: output.unblinded_output.features.flags.bits() as i32, + maturity: output.unblinded_output.features.maturity as i64, + status: status as i32, + tx_id: tx_id.map(|i| i.as_u64() as i64), + hash: Some(output.hash), + script: output.unblinded_output.script.as_bytes(), + input_data: output.unblinded_output.input_data.as_bytes(), + script_private_key: output.unblinded_output.script_private_key.to_vec(), + metadata: Some(output.unblinded_output.features.metadata), + features_asset_public_key: output.unblinded_output.features.asset.map(|a| a.public_key.to_vec()), + unique_id: output.unblinded_output.unique_id, + parent_public_key: output.unblinded_output.parent_public_key.map(|a| a.to_vec()), + sender_offset_public_key: output.unblinded_output.sender_offset_public_key.to_vec(), + metadata_signature_nonce: output.unblinded_output.metadata_signature.public_nonce().to_vec(), + metadata_signature_u_key: output.unblinded_output.metadata_signature.u().to_vec(), + metadata_signature_v_key: output.unblinded_output.metadata_signature.v().to_vec(), + } + } + + /// Write this struct to the database + pub fn commit(&self, conn: &SqliteConnection) -> Result<(), OutputManagerStorageError> { + diesel::insert_into(outputs::table).values(self.clone()).execute(conn)?; + Ok(()) + } +} + +impl Encryptable for NewOutputSql { + fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { + self.spending_key = encrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; + self.script_private_key = encrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + Ok(()) + } + + fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { + self.spending_key = decrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; + self.script_private_key = decrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + Ok(()) + } +} + diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs new file mode 100644 index 0000000000..8cd46ed5cc --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -0,0 +1,236 @@ +use crate::{ + output_manager_service::{ + error::OutputManagerStorageError, + storage::{ + sqlite_db::{AeadError, NullOutputSql, UpdateOutput, UpdateOutputSql}, + OutputStatus, + }, + }, + schema::{outputs, outputs::columns}, + util::encryption::{decrypt_bytes_integral_nonce, encrypt_bytes_integral_nonce, Encryptable}, +}; +use aes_gcm::Aes256Gcm; +use diesel::{prelude::*, SqliteConnection}; +use tari_core::transactions::{transaction::OutputFlags, transaction_protocol::TxId}; + +#[derive(Clone, Debug, Queryable, QueryableByName, Identifiable, PartialEq)] +#[table_name = "outputs"] +pub struct OutputSql { + pub id: i32, + pub commitment: Option>, + pub spending_key: Vec, + pub value: i64, + pub flags: i32, + pub maturity: i64, + pub status: i32, + pub tx_id: Option, + pub hash: Option>, + pub script: Vec, + pub input_data: Vec, + pub script_private_key: Vec, + pub sender_offset_public_key: Vec, + pub metadata_signature_nonce: Vec, + pub metadata_signature_u_key: Vec, + pub metadata_signature_v_key: Vec, + pub unique_id: Option>, + pub metadata: Option>, + pub features_asset_public_key: Option>, + pub features_mint_asset_public_key: Option>, + pub features_mint_asset_owner_commitment: Option>, + pub parent_public_key: Option>, +} + +impl OutputSql { + /// Return all outputs + pub fn index(conn: &SqliteConnection) -> Result, OutputManagerStorageError> { + Ok(outputs::table.load::(conn)?) + } + + /// Return all outputs with a given status + pub fn index_status( + status: OutputStatus, + conn: &SqliteConnection, + ) -> Result, OutputManagerStorageError> { + Ok(outputs::table.filter(columns::status.eq(status as i32)).load(conn)?) + } + + /// Return all unspent outputs that have a maturity above the provided chain tip + pub fn index_time_locked(tip: u64, conn: &SqliteConnection) -> Result, OutputManagerStorageError> { + Ok(outputs::table + .filter(columns::status.eq(OutputStatus::Unspent as i32)) + .filter(columns::maturity.gt(tip as i64)) + .load(conn)?) + } + + pub fn index_by_feature_flags( + flags: OutputFlags, + conn: &SqliteConnection, + ) -> Result, OutputManagerStorageError> { + let res = diesel::sql_query("SELECT * FROM outputs where flags & $1 = $1 ORDER BY id;") + .bind::(flags.bits() as i32) + .load(conn)?; + Ok(res) + } + + /// Find a particular Output, if it exists + pub fn find(spending_key: &[u8], conn: &SqliteConnection) -> Result { + Ok(outputs::table + .filter(columns::spending_key.eq(spending_key)) + .first::(conn)?) + } + + pub fn find_by_commitment( + commitment: &[u8], + conn: &SqliteConnection, + ) -> Result { + let cancelled = OutputStatus::CancelledInbound as i32; + Ok(outputs::table + .filter(columns::status.ne(cancelled)) + .filter(columns::commitment.eq(commitment)) + .first::(conn)?) + } + + pub fn find_by_commitment_and_cancelled( + commitment: &[u8], + cancelled: bool, + conn: &SqliteConnection, + ) -> Result { + let cancelled_flag = OutputStatus::CancelledInbound as i32; + + let mut request = outputs::table.filter(outputs::commitment.eq(commitment)).into_boxed(); + if cancelled { + request = request.filter(outputs::status.eq(cancelled_flag)) + } else { + request = request.filter(outputs::status.ne(cancelled_flag)) + }; + + Ok(request.first::(conn)?) + } + + pub fn find_by_tx_id_and_status( + tx_id: TxId, + status: OutputStatus, + conn: &SqliteConnection, + ) -> Result, OutputManagerStorageError> { + Ok(outputs::table + .filter(outputs::tx_id.eq(Some(tx_id.as_u64() as i64))) + .filter(outputs::status.eq(status as i32)) + .load(conn)?) + } + + /// Find outputs via tx_id + pub fn find_by_tx_id(tx_id: TxId, conn: &SqliteConnection) -> Result, OutputManagerStorageError> { + Ok(outputs::table + .filter(columns::tx_id.eq(Some(tx_id.as_u64() as i64))) + .load(conn)?) + } + + /// Find outputs via tx_id that are encumbered. Any outputs that are encumbered cannot be marked as spent. + pub fn find_by_tx_id_and_encumbered( + tx_id: TxId, + conn: &SqliteConnection, + ) -> Result, OutputManagerStorageError> { + Ok(outputs::table + .filter(columns::tx_id.eq(Some(tx_id.as_u64() as i64))) + .filter( + columns::status + .eq(OutputStatus::EncumberedToBeReceived as i32) + .or(columns::status.eq(OutputStatus::EncumberedToBeSpent as i32)), + ) + .load(conn)?) + } + + /// Find a particular Output, if it exists and is in the specified Spent state + pub fn find_status( + spending_key: &[u8], + status: OutputStatus, + conn: &SqliteConnection, + ) -> Result { + Ok(outputs::table + .filter(columns::status.eq(status as i32)) + .filter(columns::spending_key.eq(spending_key)) + .first::(conn)?) + } + + pub fn delete(&self, conn: &SqliteConnection) -> Result<(), OutputManagerStorageError> { + let num_deleted = + diesel::delete(outputs::table.filter(columns::spending_key.eq(&self.spending_key))).execute(conn)?; + + if num_deleted == 0 { + return Err(OutputManagerStorageError::ValuesNotFound); + } + + Ok(()) + } + + // TODO: This method needs to be checked for concurrency + pub fn update( + &self, + updated_output: UpdateOutput, + conn: &SqliteConnection, + ) -> Result { + let num_updated = diesel::update(outputs::table.filter(columns::id.eq(&self.id))) + .set(UpdateOutputSql::from(updated_output)) + .execute(conn)?; + + if num_updated == 0 { + return Err(OutputManagerStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + + OutputSql::find(&self.spending_key, conn) + } + + /// This function is used to update an existing record to set fields to null + pub fn update_null( + &self, + updated_null: NullOutputSql, + conn: &SqliteConnection, + ) -> Result { + let num_updated = diesel::update(outputs::table.filter(columns::spending_key.eq(&self.spending_key))) + .set(updated_null) + .execute(conn)?; + + if num_updated == 0 { + return Err(OutputManagerStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + + OutputSql::find(&self.spending_key, conn) + } + + /// Update the changed fields of this record after encryption/decryption is performed + pub fn update_encryption(&self, conn: &SqliteConnection) -> Result<(), OutputManagerStorageError> { + let _ = self.update( + UpdateOutput { + spending_key: Some(self.spending_key.clone()), + script_private_key: Some(self.script_private_key.clone()), + ..Default::default() + }, + conn, + )?; + Ok(()) + } +} + +impl Encryptable for OutputSql { + fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { + self.spending_key = encrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; + self.script_private_key = encrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + Ok(()) + } + + fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { + self.spending_key = decrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; + self.script_private_key = decrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + Ok(()) + } +} + +// impl PartialEq for OutputSql { +// fn eq(&self, other: &NewOutputSql) -> bool { +// &NewOutputSql::from(self.clone()) == other +// } +// } diff --git a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs index e08059e16b..3ec966e4c6 100644 --- a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs +++ b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs @@ -417,10 +417,10 @@ where TBackend: OutputManagerBackend + 'static let mut returned_outputs = Vec::new(); for output_proto in batch_response.outputs.iter() { - let output = TransactionOutput::try_from(output_proto.clone()).map_err(|_| { + let output = TransactionOutput::try_from(output_proto.clone()).map_err(|err| { OutputManagerProtocolError::new( self.id, - OutputManagerError::ConversionError("Could not convert protobuf TransactionOutput".to_string()), + OutputManagerError::ConversionError(format!("Could not convert protobuf TransactionOutput:{}", err)), ) })?; returned_outputs.push(output); diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index 6b6c61512a..3fad2749ff 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -24,6 +24,7 @@ table! { valid -> Integer, confirmations -> Nullable, mined_height -> Nullable, + unique_id -> Nullable, } } @@ -46,6 +47,7 @@ table! { direct_send_success -> Integer, send_count -> Integer, last_send_timestamp -> Nullable, + unique_id -> Nullable, } } @@ -81,6 +83,7 @@ table! { direct_send_success -> Integer, send_count -> Integer, last_send_timestamp -> Nullable, + unique_id -> Nullable, } } @@ -102,6 +105,12 @@ table! { metadata_signature_nonce -> Binary, metadata_signature_u_key -> Binary, metadata_signature_v_key -> Binary, + unique_id -> Nullable, + metadata -> Nullable, + features_asset_public_key -> Nullable, + features_mint_asset_public_key -> Nullable, + features_mint_asset_owner_commitment -> Nullable, + parent_public_key -> Nullable, } } diff --git a/base_layer/wallet/src/tokens/infrastructure/initializer.rs b/base_layer/wallet/src/tokens/infrastructure/initializer.rs new file mode 100644 index 0000000000..26fb07e471 --- /dev/null +++ b/base_layer/wallet/src/tokens/infrastructure/initializer.rs @@ -0,0 +1,95 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::output_manager_service::storage::database::OutputManagerBackend; + + +use crate::tokens::TokenManagerHandle; +use crate::tokens::infrastructure::token_manager_service::TokenManagerService; +use log::*; + +use futures::{future, Future}; + + + +use tari_service_framework::{ + reply_channel, + ServiceInitializationError, + ServiceInitializer, + ServiceInitializerContext, +}; + +use tari_service_framework::{ + async_trait, +}; +use crate::output_manager_service::handle::OutputManagerHandle; + + +const LOG_TARGET: &str = "wallet::assets::infrastructure::initializer"; + +pub struct TokenManagerServiceInitializer + where T: OutputManagerBackend +{ + backend: Option +} + +impl TokenManagerServiceInitializer + where T: OutputManagerBackend + 'static +{ + pub fn new(backend: T + + ) -> Self { + Self { + backend: Some(backend) + } + } +} + +#[async_trait] +impl ServiceInitializer for TokenManagerServiceInitializer + where T: OutputManagerBackend + 'static +{ + async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { + + let (sender, receiver) = reply_channel::unbounded(); + + let handle = TokenManagerHandle::new(sender); + context.register_handle(handle); + + let backend = self.backend.take().expect("this expect pattern is dumb"); + + context.spawn_when_ready(move |handles| async move { + + let output_manager = handles.expect_handle::(); + // let transaction_service = handles.expect_handle::(); + let service = TokenManagerService::new(backend, output_manager); + + let running = service.start(handles.get_shutdown_signal(), receiver); + + futures::pin_mut!(running); + future::select(running, handles.get_shutdown_signal()).await; + info!(target: LOG_TARGET, "Token Manager Service shutdown"); + }); + Ok(()) + } +} + diff --git a/base_layer/wallet/src/tokens/infrastructure/mod.rs b/base_layer/wallet/src/tokens/infrastructure/mod.rs new file mode 100644 index 0000000000..ab7f92da17 --- /dev/null +++ b/base_layer/wallet/src/tokens/infrastructure/mod.rs @@ -0,0 +1,34 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::tokens::Token; + +pub mod initializer; +pub mod token_manager_service; + +pub enum TokenManagerRequest { + ListOwned{}, +} + +pub enum TokenManagerResponse { + ListOwned{ tokens : Vec}, +} diff --git a/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs b/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs new file mode 100644 index 0000000000..c0c65725e6 --- /dev/null +++ b/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs @@ -0,0 +1,99 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + tokens::{ + infrastructure::{TokenManagerRequest, TokenManagerResponse}, + TokenManager, + }, + error::WalletError, + output_manager_service::storage::{ + database::{OutputManagerBackend}, + }, +}; +use tari_service_framework::{ + reply_channel::{Receiver, }, +}; +use futures::{pin_mut, StreamExt}; +use tari_shutdown::ShutdownSignal; +use log::*; +use crate::output_manager_service::handle::OutputManagerHandle; +use crate::types::MockPersistentKeyManager; + +const LOG_TARGET: &str = "wallet::assets::infrastructure::asset_manager_service"; + +pub struct TokenManagerService { + manager: TokenManager, +} + +impl TokenManagerService { + pub fn new(backend: T, output_manager: OutputManagerHandle) -> Self { + Self { + manager: TokenManager::::new(backend, output_manager), + } + } + + pub async fn start( + mut self, + mut shutdown_signal: ShutdownSignal, + request_stream: Receiver>, + ) -> Result<(), WalletError> { + let request_stream = request_stream.fuse(); + pin_mut!(request_stream); + + info!(target: LOG_TARGET, "Token Manager Service started"); + loop { + futures::select! { + request_context = request_stream.select_next_some() => { + trace!(target: LOG_TARGET, "Handling Service API Request"); + let (request, reply_tx) = request_context.split(); + let response = self.handle_request(request).await.map_err(|e| { + warn!(target: LOG_TARGET, "Error handling request: {:?}", e); + e + }); + let _ = reply_tx.send(response).map_err(|e| { + warn!(target: LOG_TARGET, "Failed to send reply"); + e + }); + }, + _ = shutdown_signal => { + info!(target: LOG_TARGET, "Token manager service shutting down because it received the shutdown signal"); + break; + } + complete => { + info!(target: LOG_TARGET, "Token manager service shutting down"); + break; + } + } + } + Ok(()) + } + + pub async fn handle_request(&mut self, request: TokenManagerRequest) -> Result { + match request { + TokenManagerRequest::ListOwned { .. } => Ok(TokenManagerResponse::ListOwned { + tokens: self.manager.list_owned().await?, + }), + + } + } +} diff --git a/base_layer/wallet/src/tokens/mod.rs b/base_layer/wallet/src/tokens/mod.rs new file mode 100644 index 0000000000..d9567817d4 --- /dev/null +++ b/base_layer/wallet/src/tokens/mod.rs @@ -0,0 +1,30 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + mod token; +mod token_manager; +pub(crate) mod infrastructure; +mod token_manager_handle; + +pub(crate) use token_manager::TokenManager; +pub use token_manager_handle::TokenManagerHandle; +pub use token::Token; diff --git a/base_layer/wallet/src/tokens/token.rs b/base_layer/wallet/src/tokens/token.rs new file mode 100644 index 0000000000..aebb0c216d --- /dev/null +++ b/base_layer/wallet/src/tokens/token.rs @@ -0,0 +1,70 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_common_types::types::{Commitment, PublicKey}; + +#[derive(Clone)] +pub struct Token { + name: String, + output_status: String, + asset_public_key: PublicKey, + owner_commitment: Commitment, + unique_id: Vec, +} + +impl Token { + pub fn new( + name: String, + output_status: String, + asset_public_key: PublicKey, + owner_commitment: Commitment, + unique_id: Vec, + ) -> Self { + Self { + name, + output_status, + asset_public_key, + owner_commitment, + unique_id, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn output_status(&self) -> &str { + &self.output_status + } + + pub fn asset_public_key(&self) -> &PublicKey { + &self.asset_public_key + } + + pub fn owner_commitment(&self) -> &Commitment { + &self.owner_commitment + } + + pub fn unique_id(&self) -> &[u8] { + self.unique_id.as_slice() + } +} diff --git a/base_layer/wallet/src/tokens/token_manager.rs b/base_layer/wallet/src/tokens/token_manager.rs new file mode 100644 index 0000000000..87eaecc13b --- /dev/null +++ b/base_layer/wallet/src/tokens/token_manager.rs @@ -0,0 +1,145 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + assets::Asset, + error::WalletError, + output_manager_service::storage::database::{OutputManagerBackend, OutputManagerDatabase}, +}; +use tari_core::transactions::transaction::{OutputFeatures, OutputFlags, Transaction}; + +use crate::{ + output_manager_service::{handle::OutputManagerHandle, storage::models::DbUnblindedOutput}, + tokens::Token, + types::PersistentKeyManager, +}; +use log::*; +use tari_core::transactions::transaction_protocol::TxId; +use tari_crypto::tari_utilities::ByteArray; + +const LOG_TARGET: &str = "wallet::tokens::token_manager"; + +pub(crate) struct TokenManager { + output_database: OutputManagerDatabase, + output_manager: OutputManagerHandle, + // transaction_service: TransactionServiceHandle +} +impl TokenManager { + pub fn new(backend: T, output_manager: OutputManagerHandle) -> Self { + Self { + output_database: OutputManagerDatabase::new(backend), + output_manager, + } + } + + pub async fn list_owned(&self) -> Result, WalletError> { + let outputs = self + .output_database + .fetch_with_features(OutputFlags::NON_FUNGIBLE) + .await + .map_err(|err| WalletError::OutputManagerError(err.into()))?; + + // These will include assets registrations + + debug!( + target: LOG_TARGET, + "Found {} owned outputs that contain tokens", + outputs.len() + ); + let assets: Vec = outputs + .into_iter() + .filter(|ub| { + // Filter out asset registrations that don't have a parent pub key + ub.unblinded_output.parent_public_key.is_some() + }) + .map(|unblinded_output| convert_to_token(unblinded_output)) + .collect::>()?; + Ok(assets) + } +} + +fn convert_to_token(unblinded_output: DbUnblindedOutput) -> Result { + if unblinded_output.unblinded_output.features.metadata.is_empty() { + // TODO: sort out unwraps + return Ok(Token::new( + "".to_string(), + unblinded_output.status.to_string(), + unblinded_output + .unblinded_output + .parent_public_key + .as_ref() + .map(|a| a.clone()) + .unwrap(), + unblinded_output.commitment, + unblinded_output.unblinded_output.unique_id.unwrap_or_default(), + )); + } + let version = unblinded_output.unblinded_output.features.metadata[0]; + + let deserializer = get_deserializer(version); + + let metadata = deserializer.deserialize(&unblinded_output.unblinded_output.features.metadata[1..]); + Ok(Token::new( + metadata.name, + unblinded_output.status.to_string(), + unblinded_output + .unblinded_output + .parent_public_key + .as_ref() + .map(|a| a.clone()) + .unwrap(), + unblinded_output.commitment, + unblinded_output.unblinded_output.unique_id.unwrap_or_default(), + )) +} + +fn get_deserializer(_version: u8) -> impl TokenMetadataDeserializer { + V1TokenMetadataSerializer {} +} + +pub trait TokenMetadataDeserializer { + fn deserialize(&self, metadata: &[u8]) -> TokenMetadata; +} +pub trait TokenMetadataSerializer { + fn serialize(&self, model: &TokenMetadata) -> Vec; +} + +pub struct V1TokenMetadataSerializer {} + +// TODO: Replace with proto serializer +impl TokenMetadataDeserializer for V1TokenMetadataSerializer { + fn deserialize(&self, metadata: &[u8]) -> TokenMetadata { + TokenMetadata { + name: String::from_utf8(Vec::from(metadata)).unwrap(), + } + } +} + +impl TokenMetadataSerializer for V1TokenMetadataSerializer { + fn serialize(&self, model: &TokenMetadata) -> Vec { + model.name.clone().into_bytes() + } +} + +pub struct TokenMetadata { + name: String, +} diff --git a/base_layer/wallet/src/tokens/token_manager_handle.rs b/base_layer/wallet/src/tokens/token_manager_handle.rs new file mode 100644 index 0000000000..daf1fad5d2 --- /dev/null +++ b/base_layer/wallet/src/tokens/token_manager_handle.rs @@ -0,0 +1,51 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + error::WalletError, + tokens::{ + infrastructure::{TokenManagerRequest, TokenManagerResponse}, + Token, + }, +}; +use tari_service_framework::{reply_channel::SenderService, Service}; + +use tari_core::transactions::{transaction::Transaction, transaction_protocol::TxId}; + +#[derive(Clone)] +pub struct TokenManagerHandle { + handle: SenderService>, +} + +impl TokenManagerHandle { + pub fn new(sender: SenderService>) -> Self { + Self { handle: sender } + } + + pub async fn list_owned_tokens(&mut self) -> Result, WalletError> { + match self.handle.call(TokenManagerRequest::ListOwned {}).await?? { + TokenManagerResponse::ListOwned { tokens } => Ok(tokens), + /* _ => Err(WalletError::UnexpectedApiResponse{ method: "list_owned_tokens".to_string(), api: + * "TokenManagerService".to_string()}), */ + } + } +} diff --git a/base_layer/wallet/src/transaction_service/error.rs b/base_layer/wallet/src/transaction_service/error.rs index 05e9e4af2b..cea41bcb11 100644 --- a/base_layer/wallet/src/transaction_service/error.rs +++ b/base_layer/wallet/src/transaction_service/error.rs @@ -20,10 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - output_manager_service::{error::OutputManagerError, TxId}, - transaction_service::storage::database::DbKey, -}; +use crate::{output_manager_service::{error::OutputManagerError}, transaction_service::storage::database::DbKey, OperationId}; use diesel::result::Error as DieselError; use futures::channel::oneshot::Canceled; use serde_json::Error as SerdeJsonError; @@ -35,6 +32,7 @@ use tari_service_framework::reply_channel::TransportChannelError; use thiserror::Error; use time::OutOfRangeError; use tokio::sync::broadcast::error::RecvError; +use tari_core::transactions::transaction_protocol::TxId; #[derive(Debug, Error)] pub enum TransactionServiceError { @@ -184,13 +182,14 @@ pub enum TransactionStorageError { /// include the ID of the protocol #[derive(Debug)] pub struct TransactionServiceProtocolError { + // TODO: Replace with T or something to account for OperationId or TxId pub id: u64, pub error: TransactionServiceError, } impl TransactionServiceProtocolError { - pub fn new(id: u64, error: TransactionServiceError) -> Self { - Self { id, error } + pub fn new>(id: T, error: TransactionServiceError) -> Self { + Self { id: id.into(), error } } } diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index 7a6ab48649..e23f83e606 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -20,13 +20,10 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - output_manager_service::TxId, - transaction_service::{ - error::TransactionServiceError, - storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, - }, -}; +use crate::{transaction_service::{ + error::TransactionServiceError, + storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, +}, OperationId}; use aes_gcm::Aes256Gcm; use std::{collections::HashMap, fmt, sync::Arc}; use tari_comms::types::CommsPublicKey; @@ -36,6 +33,10 @@ use tokio::sync::broadcast; use tower::Service; use crate::types::ValidationRetryStrategy; +use tari_core::transactions::transaction_protocol::TxId; +use std::fmt::Formatter; + + /// API Request enum #[allow(clippy::large_enum_variant)] @@ -49,11 +50,11 @@ pub enum TransactionServiceRequest { GetCompletedTransaction(TxId), GetAnyTransaction(TxId), SetBaseNodePublicKey(CommsPublicKey), - SendTransaction(CommsPublicKey, MicroTari, MicroTari, String), - SendOneSidedTransaction(CommsPublicKey, MicroTari, MicroTari, String), + SendTransaction{dest_pubkey: CommsPublicKey, amount: MicroTari, unique_id: Option>, fee_per_gram: MicroTari, message: String}, + SendOneSidedTransaction{dest_pubkey: CommsPublicKey, amount: MicroTari, unique_id: Option>, fee_per_gram: MicroTari, message: String}, CancelTransaction(TxId), ImportUtxo(MicroTari, CommsPublicKey, String, Option), - SubmitCoinSplitTransaction(TxId, Transaction, MicroTari, MicroTari, String), + SubmitSelfSendTransaction(TxId, Transaction, MicroTari, MicroTari, String), SetLowPowerMode, SetNormalPowerMode, ApplyEncryption(Box), @@ -63,7 +64,7 @@ pub enum TransactionServiceRequest { RestartBroadcastProtocols, GetNumConfirmationsRequired, SetNumConfirmationsRequired(u64), - SetCompletedTransactionValidity(u64, bool), + SetCompletedTransactionValidity(TxId, bool), ValidateTransactions(ValidationRetryStrategy), } @@ -78,9 +79,9 @@ impl fmt::Display for TransactionServiceRequest { Self::GetCancelledCompletedTransactions => f.write_str("GetCancelledCompletedTransactions"), Self::GetCompletedTransaction(t) => f.write_str(&format!("GetCompletedTransaction({})", t)), Self::SetBaseNodePublicKey(k) => f.write_str(&format!("SetBaseNodePublicKey ({})", k)), - Self::SendTransaction(k, v, _, msg) => f.write_str(&format!("SendTransaction (to {}, {}, {})", k, v, msg)), - Self::SendOneSidedTransaction(k, v, _, msg) => { - f.write_str(&format!("SendOneSidedTransaction (to {}, {}, {})", k, v, msg)) + Self::SendTransaction{ dest_pubkey, amount, message, .. }=> f.write_str(&format!("SendTransaction (to {}, {}, {})", dest_pubkey, amount, message)), + Self::SendOneSidedTransaction{dest_pubkey, amount, message, .. }=> { + f.write_str(&format!("SendOneSidedTransaction (to {}, {}, {})", dest_pubkey, amount, message)) }, Self::CancelTransaction(t) => f.write_str(&format!("CancelTransaction ({})", t)), Self::ImportUtxo(v, k, msg, maturity) => f.write_str(&format!( @@ -90,7 +91,7 @@ impl fmt::Display for TransactionServiceRequest { msg, maturity.unwrap_or(0) )), - Self::SubmitCoinSplitTransaction(tx_id, _, _, _, _) => { + Self::SubmitSelfSendTransaction(tx_id, _, _, _, _) => { f.write_str(&format!("SubmitTransaction ({})", tx_id)) }, Self::SetLowPowerMode => f.write_str("SetLowPowerMode "), @@ -119,9 +120,9 @@ impl fmt::Display for TransactionServiceRequest { pub enum TransactionServiceResponse { TransactionSent(TxId), TransactionCancelled, - PendingInboundTransactions(HashMap), - PendingOutboundTransactions(HashMap), - CompletedTransactions(HashMap), + PendingInboundTransactions(HashMap), + PendingOutboundTransactions(HashMap), + CompletedTransactions(HashMap), CompletedTransaction(Box), BaseNodePublicKeySet, UtxoImported(TxId), @@ -135,7 +136,7 @@ pub enum TransactionServiceResponse { AnyTransaction(Box>), NumConfirmationsRequired(u64), NumConfirmationsSet, - ValidationStarted(u64), + ValidationStarted(OperationId), CompletedTransactionValidityChanged, } @@ -156,15 +157,44 @@ pub enum TransactionEvent { TransactionMined(TxId), TransactionMinedRequestTimedOut(TxId), TransactionMinedUnconfirmed(TxId, u64), - TransactionValidationTimedOut(u64), - TransactionValidationSuccess(u64), - TransactionValidationFailure(u64), - TransactionValidationAborted(u64), - TransactionValidationDelayed(u64), - TransactionBaseNodeConnectionProblem(u64), + TransactionValidationTimedOut(OperationId), + TransactionValidationSuccess(OperationId), + TransactionValidationFailure(OperationId), + TransactionValidationAborted(OperationId), + TransactionValidationDelayed(OperationId), + TransactionBaseNodeConnectionProblem(TxId), Error(String), } +impl fmt::Display for TransactionEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + + match self { + TransactionEvent::MempoolBroadcastTimedOut(tx_id) => {write!(f, "MempoolBroadcastTimedOut for tx:{}", tx_id)} + TransactionEvent::ReceivedTransaction(tx) => {write!(f, "ReceivedTransaction for {}", tx)} + TransactionEvent::ReceivedTransactionReply(tx) => {write!(f, "ReceivedTransactionReply for {}", tx)} + TransactionEvent::ReceivedFinalizedTransaction(tx) => {write!(f, "ReceivedFinalizedTransaction for {}", tx)} + TransactionEvent::TransactionDiscoveryInProgress(tx) => {write!(f, "TransactionDiscoveryInProgress for {}", tx)} + TransactionEvent::TransactionDirectSendResult(tx, success) => {write!(f, "TransactionDirectSendResult for {}: {}", tx, success)} + TransactionEvent::TransactionCompletedImmediately(tx) => {write!(f, "TransactionCompletedImmediately for {}", tx)} + TransactionEvent::TransactionStoreForwardSendResult(tx, success) => {write!(f, "TransactionStoreForwardSendResult for {}:{}", tx, success)} + TransactionEvent::TransactionCancelled(tx) => {write!(f, "TransactionCancelled for {}", tx)} + TransactionEvent::TransactionBroadcast(tx) => {write!(f, "TransactionBroadcast for {}", tx)} + TransactionEvent::TransactionImported(tx) => {write!(f, "TransactionImported for {}", tx)} + TransactionEvent::TransactionMined(tx) => {write!(f, "TransactionMined for {}", tx)} + TransactionEvent::TransactionMinedRequestTimedOut(tx) => {write!(f, "TransactionMinedRequestTimedOut for {}", tx)} + TransactionEvent::TransactionMinedUnconfirmed(tx, height) => {write!(f, "TransactionMinedUnconfirmed for {} at height:{}", tx, height)} + TransactionEvent::TransactionValidationTimedOut(tx) => {write!(f, "TransactionValidationTimedOut for {}", tx)} + TransactionEvent::TransactionValidationSuccess(tx) => {write!(f, "TransactionValidationSuccess for {}", tx)} + TransactionEvent::TransactionValidationFailure(tx) => {write!(f, "TransactionValidationFailure for {}", tx)} + TransactionEvent::TransactionValidationAborted(tx) => {write!(f, "TransactionValidationAborted for {}", tx)} + TransactionEvent::TransactionValidationDelayed(tx) => {write!(f, "TransactionValidationDelayed for {}", tx)} + TransactionEvent::TransactionBaseNodeConnectionProblem(tx) => {write!(f, "TransactionBaseNodeConnectionProblem for {}", tx)} + TransactionEvent::Error(error) => {write!(f, "Error:{}", error)} + } + } +} + pub type TransactionEventSender = broadcast::Sender>; pub type TransactionEventReceiver = broadcast::Receiver>; /// The Transaction Service Handle is a struct that contains the interfaces used to communicate with a running @@ -194,17 +224,19 @@ impl TransactionServiceHandle { &mut self, dest_pubkey: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, message: String, ) -> Result { match self .handle - .call(TransactionServiceRequest::SendTransaction( + .call(TransactionServiceRequest::SendTransaction { dest_pubkey, amount, + unique_id, fee_per_gram, message, - )) + }) .await?? { TransactionServiceResponse::TransactionSent(tx_id) => Ok(tx_id), @@ -216,17 +248,19 @@ impl TransactionServiceHandle { &mut self, dest_pubkey: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, message: String, ) -> Result { match self .handle - .call(TransactionServiceRequest::SendOneSidedTransaction( + .call(TransactionServiceRequest::SendOneSidedTransaction { dest_pubkey, amount, + unique_id, fee_per_gram, message, - )) + }) .await?? { TransactionServiceResponse::TransactionSent(tx_id) => Ok(tx_id), @@ -247,7 +281,7 @@ impl TransactionServiceHandle { pub async fn get_pending_inbound_transactions( &mut self, - ) -> Result, TransactionServiceError> { + ) -> Result, TransactionServiceError> { match self .handle .call(TransactionServiceRequest::GetPendingInboundTransactions) @@ -260,7 +294,7 @@ impl TransactionServiceHandle { pub async fn get_cancelled_pending_inbound_transactions( &mut self, - ) -> Result, TransactionServiceError> { + ) -> Result, TransactionServiceError> { match self .handle .call(TransactionServiceRequest::GetCancelledPendingInboundTransactions) @@ -273,7 +307,7 @@ impl TransactionServiceHandle { pub async fn get_pending_outbound_transactions( &mut self, - ) -> Result, TransactionServiceError> { + ) -> Result, TransactionServiceError> { match self .handle .call(TransactionServiceRequest::GetPendingOutboundTransactions) @@ -286,7 +320,7 @@ impl TransactionServiceHandle { pub async fn get_cancelled_pending_outbound_transactions( &mut self, - ) -> Result, TransactionServiceError> { + ) -> Result, TransactionServiceError> { match self .handle .call(TransactionServiceRequest::GetCancelledPendingOutboundTransactions) @@ -299,7 +333,7 @@ impl TransactionServiceHandle { pub async fn get_completed_transactions( &mut self, - ) -> Result, TransactionServiceError> { + ) -> Result, TransactionServiceError> { match self .handle .call(TransactionServiceRequest::GetCompletedTransactions) @@ -312,7 +346,7 @@ impl TransactionServiceHandle { pub async fn get_cancelled_completed_transactions( &mut self, - ) -> Result, TransactionServiceError> { + ) -> Result, TransactionServiceError> { match self .handle .call(TransactionServiceRequest::GetCancelledCompletedTransactions) @@ -397,7 +431,7 @@ impl TransactionServiceHandle { ) -> Result<(), TransactionServiceError> { match self .handle - .call(TransactionServiceRequest::SubmitCoinSplitTransaction( + .call(TransactionServiceRequest::SubmitSelfSendTransaction( tx_id, tx, fee, amount, message, )) .await?? @@ -510,7 +544,7 @@ impl TransactionServiceHandle { pub async fn validate_transactions( &mut self, retry_strategy: ValidationRetryStrategy, - ) -> Result { + ) -> Result { match self .handle .call(TransactionServiceRequest::ValidateTransactions(retry_strategy)) diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs index 4a28383226..64d001ba2c 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs @@ -20,16 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - output_manager_service::TxId, - transaction_service::{ - error::{TransactionServiceError, TransactionServiceProtocolError}, - handle::TransactionEvent, - service::TransactionServiceResources, - storage::{ - database::TransactionBackend, - models::{CompletedTransaction, TransactionStatus}, - }, +use crate::transaction_service::{ + error::{TransactionServiceError, TransactionServiceProtocolError}, + handle::TransactionEvent, + service::TransactionServiceResources, + storage::{ + database::TransactionBackend, + models::{CompletedTransaction, TransactionStatus}, }, }; use futures::FutureExt; @@ -42,7 +39,7 @@ use tari_core::{ proto::wallet_rpc::{TxLocation, TxQueryResponse, TxSubmissionRejectionReason, TxSubmissionResponse}, rpc::BaseNodeWalletRpcClient, }, - transactions::transaction::Transaction, + transactions::{transaction::Transaction, transaction_protocol::TxId}, }; use tari_crypto::tari_utilities::hex::Hex; use tokio::{sync::broadcast, time::sleep}; @@ -86,7 +83,7 @@ where TBackend: TransactionBackend + 'static } /// The task that defines the execution of the protocol. - pub async fn execute(mut self) -> Result { + pub async fn execute(mut self) -> Result { let mut timeout_update_receiver = self.timeout_update_receiver.take().ok_or_else(|| { TransactionServiceProtocolError::new(self.tx_id, TransactionServiceError::InvalidStateError) })?; diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_coinbase_monitoring_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_coinbase_monitoring_protocol.rs index f368d33d89..681c304d62 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_coinbase_monitoring_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_coinbase_monitoring_protocol.rs @@ -20,23 +20,23 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - output_manager_service::TxId, - transaction_service::{ - error::{TransactionServiceError, TransactionServiceProtocolError}, - handle::TransactionEvent, - service::TransactionServiceResources, - storage::{database::TransactionBackend, models::CompletedTransaction}, - }, +use crate::transaction_service::{ + error::{TransactionServiceError, TransactionServiceProtocolError}, + handle::TransactionEvent, + service::TransactionServiceResources, + storage::{database::TransactionBackend, models::CompletedTransaction}, }; use futures::FutureExt; use log::*; use std::{convert::TryFrom, sync::Arc, time::Duration}; use tari_common_types::types::Signature; use tari_comms::{peer_manager::NodeId, types::CommsPublicKey, PeerConnection}; -use tari_core::base_node::{ - proto::wallet_rpc::{TxLocation, TxQueryResponse}, - rpc::BaseNodeWalletRpcClient, +use tari_core::{ + base_node::{ + proto::wallet_rpc::{TxLocation, TxQueryResponse}, + rpc::BaseNodeWalletRpcClient, + }, + transactions::transaction_protocol::TxId, }; use tari_crypto::tari_utilities::{hex::Hex, Hashable}; use tokio::{sync::broadcast, time::sleep}; @@ -83,7 +83,7 @@ where TBackend: TransactionBackend + 'static } /// The task that defines the execution of the protocol. - pub async fn execute(mut self) -> Result { + pub async fn execute(mut self) -> Result { let mut base_node_update_receiver = self.base_node_update_receiver.take().ok_or_else(|| { TransactionServiceProtocolError::new(self.tx_id, TransactionServiceError::InvalidStateError) })?; diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs index 0a6bd89fb2..fa37c5c074 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs @@ -20,18 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - output_manager_service::TxId, - transaction_service::{ - error::{TransactionServiceError, TransactionServiceProtocolError}, - handle::TransactionEvent, - service::TransactionServiceResources, - storage::{ - database::TransactionBackend, - models::{CompletedTransaction, InboundTransaction, TransactionDirection, TransactionStatus}, - }, - tasks::send_transaction_reply::send_transaction_reply, +use crate::transaction_service::{ + error::{TransactionServiceError, TransactionServiceProtocolError}, + handle::TransactionEvent, + service::TransactionServiceResources, + storage::{ + database::TransactionBackend, + models::{CompletedTransaction, InboundTransaction, TransactionDirection, TransactionStatus}, }, + tasks::send_transaction_reply::send_transaction_reply, }; use chrono::Utc; use futures::future::FutureExt; @@ -42,7 +39,7 @@ use tokio::sync::{mpsc, oneshot}; use tari_core::transactions::{ transaction::Transaction, - transaction_protocol::{recipient::RecipientState, sender::TransactionSenderMessage}, + transaction_protocol::{recipient::RecipientState, sender::TransactionSenderMessage, TxId}, }; use tari_crypto::tari_utilities::Hashable; use tokio::time::sleep; @@ -59,7 +56,7 @@ pub enum TransactionReceiveProtocolStage { pub struct TransactionReceiveProtocol where TBackend: TransactionBackend + 'static { - id: u64, + id: TxId, source_pubkey: CommsPublicKey, sender_message: TransactionSenderMessage, stage: TransactionReceiveProtocolStage, @@ -72,7 +69,7 @@ impl TransactionReceiveProtocol where TBackend: TransactionBackend + 'static { pub fn new( - id: u64, + id: TxId, source_pubkey: CommsPublicKey, sender_message: TransactionSenderMessage, stage: TransactionReceiveProtocolStage, @@ -91,7 +88,7 @@ where TBackend: TransactionBackend + 'static } } - pub async fn execute(mut self) -> Result { + pub async fn execute(mut self) -> Result { info!( target: LOG_TARGET, "Starting Transaction Receive protocol for TxId: {} at Stage {:?}", self.id, self.stage @@ -146,6 +143,7 @@ where TBackend: TransactionBackend + 'static data.tx_id, self.source_pubkey.clone(), amount, + data.unique_id, rtp, TransactionStatus::Pending, data.message.clone(), @@ -306,6 +304,7 @@ where TBackend: TransactionBackend + 'static #[allow(unused_assignments)] let mut incoming_finalized_transaction = None; + loop { loop { let resend_timeout = sleep(self.resources.config.transaction_resend_period).fuse(); @@ -445,6 +444,7 @@ where TBackend: TransactionBackend + 'static self.source_pubkey.clone(), self.resources.node_identity.public_key().clone(), inbound_tx.amount, + rtp_output.unique_id.clone(), finalized_transaction.body.get_total_fee(), finalized_transaction.clone(), TransactionStatus::Completed, diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs index e88c9013e6..a4f9514c70 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs @@ -47,7 +47,7 @@ use tari_comms_dht::{ use tari_core::transactions::{ tari_amount::MicroTari, transaction::KernelFeatures, - transaction_protocol::{proto, recipient::RecipientSignedMessage, sender::SingleRoundSenderData}, + transaction_protocol::{proto, recipient::RecipientSignedMessage, sender::SingleRoundSenderData, TxId}, SenderTransactionProtocol, }; use tari_crypto::script; @@ -69,7 +69,7 @@ pub enum TransactionSendProtocolStage { pub struct TransactionSendProtocol where TBackend: TransactionBackend + 'static { - id: u64, + id: TxId, dest_pubkey: CommsPublicKey, amount: MicroTari, fee_per_gram: MicroTari, @@ -86,7 +86,7 @@ impl TransactionSendProtocol where TBackend: TransactionBackend + 'static { pub fn new( - id: u64, + id: TxId, resources: TransactionServiceResources, transaction_reply_receiver: Receiver<(CommsPublicKey, RecipientSignedMessage)>, cancellation_receiver: oneshot::Receiver<()>, @@ -114,7 +114,7 @@ where TBackend: TransactionBackend + 'static } /// Execute the Transaction Send Protocol as an async task. - pub async fn execute(mut self) -> Result { + pub async fn execute(mut self) -> Result { info!( target: LOG_TARGET, "Starting Transaction Send protocol for TxId: {} at Stage {:?}", self.id, self.stage @@ -155,6 +155,7 @@ where TBackend: TransactionBackend + 'static .prepare_transaction_to_send( self.id, self.amount, + None, // TODO: is this supposed to be populated? self.fee_per_gram, None, self.message.clone(), @@ -230,6 +231,8 @@ where TBackend: TransactionBackend + 'static tx_id, self.dest_pubkey.clone(), self.amount, + // TODO: put value in here + None, fee, sender_protocol.clone(), TransactionStatus::Pending, @@ -486,6 +489,7 @@ where TBackend: TransactionBackend + 'static self.resources.node_identity.public_key().clone(), outbound_tx.destination_public_key.clone(), outbound_tx.amount, + outbound_tx.unique_id, outbound_tx.fee, tx.clone(), TransactionStatus::Completed, diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs index dcf072c272..59066ecb09 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs @@ -31,6 +31,7 @@ use crate::{ }, }, types::ValidationRetryStrategy, + OperationId, }; use futures::FutureExt; use log::*; @@ -42,6 +43,7 @@ use tari_core::{ rpc::BaseNodeWalletRpcClient, }, proto::{base_node::Signatures as SignaturesProto, types::Signature as SignatureProto}, + transactions::transaction_protocol::TxId, }; use tokio::{sync::broadcast, time::sleep}; @@ -50,7 +52,7 @@ const LOG_TARGET: &str = "wallet::transaction_service::protocols::validation_pro pub struct TransactionValidationProtocol where TBackend: TransactionBackend + 'static { - id: u64, + id: OperationId, resources: TransactionServiceResources, timeout: Duration, base_node_public_key: CommsPublicKey, @@ -69,7 +71,7 @@ impl TransactionValidationProtocol where TBackend: TransactionBackend + 'static { pub fn new( - id: u64, + id: OperationId, resources: TransactionServiceResources, base_node_public_key: CommsPublicKey, timeout: Duration, @@ -90,7 +92,7 @@ where TBackend: TransactionBackend + 'static } /// The task that defines the execution of the protocol. - pub async fn execute(mut self) -> Result { + pub async fn execute(mut self) -> Result { let mut timeout_update_receiver = self .timeout_update_receiver .take() diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 12aa052382..864878d020 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -60,7 +60,7 @@ use tari_shutdown::ShutdownSignal; use tokio::sync::{mpsc, mpsc::Sender, oneshot}; use crate::{ - output_manager_service::{handle::OutputManagerHandle, TxId}, + output_manager_service::handle::OutputManagerHandle, transaction_service::{ config::TransactionServiceConfig, error::{TransactionServiceError, TransactionServiceProtocolError}, @@ -83,7 +83,9 @@ use crate::{ }, }, types::{HashDigest, ValidationRetryStrategy}, + OperationId, }; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::service"; @@ -126,11 +128,11 @@ pub struct TransactionService< resources: TransactionServiceResources, pending_transaction_reply_senders: HashMap>, base_node_response_senders: HashMap)>, - send_transaction_cancellation_senders: HashMap>, - finalized_transaction_senders: HashMap>, - receiver_transaction_cancellation_senders: HashMap>, - active_transaction_broadcast_protocols: HashSet, - active_coinbase_monitoring_protocols: HashSet, + send_transaction_cancellation_senders: HashMap>, + finalized_transaction_senders: HashMap>, + receiver_transaction_cancellation_senders: HashMap>, + active_transaction_broadcast_protocols: HashSet, + active_coinbase_monitoring_protocols: HashSet, timeout_update_publisher: broadcast::Sender, base_node_update_publisher: broadcast::Sender, power_mode: PowerMode, @@ -253,23 +255,23 @@ where let mut shutdown = self.resources.shutdown_signal.clone(); let mut send_transaction_protocol_handles: FuturesUnordered< - JoinHandle>, + JoinHandle>, > = FuturesUnordered::new(); let mut receive_transaction_protocol_handles: FuturesUnordered< - JoinHandle>, + JoinHandle>, > = FuturesUnordered::new(); let mut transaction_broadcast_protocol_handles: FuturesUnordered< - JoinHandle>, + JoinHandle>, > = FuturesUnordered::new(); let mut coinbase_transaction_monitoring_protocol_handles: FuturesUnordered< - JoinHandle>, + JoinHandle>, > = FuturesUnordered::new(); let mut transaction_validation_protocol_handles: FuturesUnordered< - JoinHandle>, + JoinHandle>, > = FuturesUnordered::new(); info!(target: LOG_TARGET, "Transaction Service started"); @@ -490,18 +492,18 @@ where async fn handle_request( &mut self, request: TransactionServiceRequest, - send_transaction_join_handles: &mut FuturesUnordered>>, + send_transaction_join_handles: &mut FuturesUnordered>>, receive_transaction_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, coinbase_monitoring_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, transaction_validation_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, reply_channel: oneshot::Sender>, ) -> Result<(), TransactionServiceError> { @@ -509,11 +511,18 @@ where trace!(target: LOG_TARGET, "Handling Service Request: {}", request); let response = match request { - TransactionServiceRequest::SendTransaction(dest_pubkey, amount, fee_per_gram, message) => { + TransactionServiceRequest::SendTransaction { + dest_pubkey, + amount, + unique_id, + fee_per_gram, + message, + } => { let rp = reply_channel.take().expect("Cannot be missing"); self.send_transaction( dest_pubkey, amount, + unique_id, fee_per_gram, message, send_transaction_join_handles, @@ -523,10 +532,17 @@ where .await?; return Ok(()); }, - TransactionServiceRequest::SendOneSidedTransaction(dest_pubkey, amount, fee_per_gram, message) => self + TransactionServiceRequest::SendOneSidedTransaction { + dest_pubkey, + amount, + unique_id, + fee_per_gram, + message, + } => self .send_one_sided_transaction( dest_pubkey, amount, + unique_id, fee_per_gram, message, transaction_broadcast_join_handles, @@ -582,8 +598,8 @@ where .add_utxo_import_transaction(value, source_public_key, message, maturity) .await .map(TransactionServiceResponse::UtxoImported), - TransactionServiceRequest::SubmitCoinSplitTransaction(tx_id, tx, fee, amount, message) => self - .submit_coin_split_transaction(transaction_broadcast_join_handles, tx_id, tx, fee, amount, message) + TransactionServiceRequest::SubmitSelfSendTransaction(tx_id, tx, fee, amount, message) => self + .submit_self_send_transaction(transaction_broadcast_join_handles, tx_id, tx, fee, amount, message) .await .map(|_| TransactionServiceResponse::TransactionSubmitted), TransactionServiceRequest::GenerateCoinbaseTransaction(reward, fees, block_height) => self @@ -657,15 +673,16 @@ where &mut self, dest_pubkey: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, message: String, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, reply_channel: oneshot::Sender>, ) -> Result<(), TransactionServiceError> { - let tx_id = OsRng.next_u64(); + let tx_id = TxId::new_random(); // If we're paying ourselves, let's complete and submit the transaction immediately if self.node_identity.public_key() == &dest_pubkey { @@ -676,7 +693,7 @@ where let (fee, transaction) = self .output_manager_service - .create_pay_to_self_transaction(tx_id, amount, fee_per_gram, None, message.clone()) + .create_pay_to_self_transaction(tx_id, amount, unique_id.clone(), fee_per_gram, None, message.clone()) .await?; // Notify that the transaction was successfully resolved. @@ -691,6 +708,7 @@ where self.node_identity.public_key().clone(), self.node_identity.public_key().clone(), amount, + unique_id, fee, transaction, TransactionStatus::Completed, @@ -746,10 +764,11 @@ where &mut self, dest_pubkey: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, message: String, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, ) -> Result { if self.node_identity.public_key() == &dest_pubkey { @@ -759,7 +778,7 @@ where )); } - let tx_id = OsRng.next_u64(); + let tx_id = TxId::new_random(); // Prepare sender part of the transaction let mut stp = self @@ -767,6 +786,7 @@ where .prepare_transaction_to_send( tx_id, amount, + unique_id.clone(), fee_per_gram, None, message.clone(), @@ -857,6 +877,7 @@ where self.resources.node_identity.public_key().clone(), dest_pubkey.clone(), amount, + unique_id, fee, tx.clone(), TransactionStatus::Completed, @@ -1003,9 +1024,9 @@ where /// Handle the final clean up after a Send Transaction protocol completes async fn complete_send_transaction_protocol( &mut self, - join_result: Result, + join_result: Result, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, ) { match join_result { @@ -1039,8 +1060,8 @@ where ); }, Err(TransactionServiceProtocolError { id, error }) => { - let _ = self.pending_transaction_reply_senders.remove(&id); - let _ = self.send_transaction_cancellation_senders.remove(&id); + let _ = self.pending_transaction_reply_senders.remove(&id.into()); + let _ = self.send_transaction_cancellation_senders.remove(&id.into()); if let TransactionServiceError::Shutdown = error { return; } @@ -1113,7 +1134,7 @@ where source_pubkey: CommsPublicKey, transaction_cancelled: proto::TransactionCancelledMessage, ) -> Result<(), TransactionServiceError> { - let tx_id = transaction_cancelled.tx_id; + let tx_id = transaction_cancelled.tx_id.into(); // Check that an inbound transaction exists to be cancelled and that the Source Public key for that transaction // is the same as the cancellation message @@ -1135,7 +1156,7 @@ where #[allow(clippy::map_entry)] async fn restart_all_send_transaction_protocols( &mut self, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { let outbound_txs = self.db.get_pending_outbound_transactions().await?; for (tx_id, tx) in outbound_txs { @@ -1179,7 +1200,7 @@ where source_pubkey: CommsPublicKey, sender_message: proto::TransactionSenderMessage, traced_message_tag: u64, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { let sender_message: TransactionSenderMessage = sender_message .try_into() @@ -1287,9 +1308,9 @@ where &mut self, source_pubkey: CommsPublicKey, finalized_transaction: proto::TransactionFinalizedMessage, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { - let tx_id = finalized_transaction.tx_id; + let tx_id = finalized_transaction.tx_id.into(); let transaction: Transaction = finalized_transaction .transaction .ok_or_else(|| { @@ -1352,9 +1373,9 @@ where /// Handle the final clean up after a Send Transaction protocol completes async fn complete_receive_transaction_protocol( &mut self, - join_result: Result, + join_result: Result, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, ) { match join_result { @@ -1390,8 +1411,8 @@ where ); }, Err(TransactionServiceProtocolError { id, error }) => { - let _ = self.finalized_transaction_senders.remove(&id); - let _ = self.receiver_transaction_cancellation_senders.remove(&id); + let _ = self.finalized_transaction_senders.remove(&id.into()); + let _ = self.receiver_transaction_cancellation_senders.remove(&id.into()); match error { TransactionServiceError::RepeatedMessageError => debug!( target: LOG_TARGET, @@ -1417,7 +1438,7 @@ where async fn restart_all_receive_transaction_protocols( &mut self, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { let inbound_txs = self.db.get_pending_inbound_transactions().await?; for (tx_id, tx) in inbound_txs { @@ -1431,7 +1452,7 @@ where &mut self, tx_id: TxId, source_public_key: CommsPublicKey, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) { if !self.pending_transaction_reply_senders.contains_key(&tx_id) { debug!( @@ -1479,9 +1500,9 @@ where async fn restart_transaction_negotiation_protocols( &mut self, - send_transaction_join_handles: &mut FuturesUnordered>>, + send_transaction_join_handles: &mut FuturesUnordered>>, receive_transaction_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, ) -> Result<(), TransactionServiceError> { trace!(target: LOG_TARGET, "Restarting transaction negotiation protocols"); @@ -1511,13 +1532,13 @@ where async fn start_transaction_validation_protocol( &mut self, retry_strategy: ValidationRetryStrategy, - join_handles: &mut FuturesUnordered>>, - ) -> Result { + join_handles: &mut FuturesUnordered>>, + ) -> Result { if self.base_node_public_key.is_none() { return Err(TransactionServiceError::NoBaseNodeKeysProvided); } trace!(target: LOG_TARGET, "Starting transaction validation protocols"); - let id = OsRng.next_u64(); + let id = OperationId::new_random(); let timeout = match self.power_mode { PowerMode::Normal => self.config.broadcast_monitoring_timeout, PowerMode::Low => self.config.low_power_polling_timeout, @@ -1545,7 +1566,7 @@ where /// Handle the final clean up after a Transaction Validation protocol completes async fn complete_transaction_validation_protocol( &mut self, - join_result: Result, + join_result: Result, ) { match join_result { Ok(id) => { @@ -1571,9 +1592,9 @@ where async fn restart_broadcast_protocols( &mut self, - broadcast_join_handles: &mut FuturesUnordered>>, + broadcast_join_handles: &mut FuturesUnordered>>, coinbase_transaction_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, ) -> Result<(), TransactionServiceError> { if self.base_node_public_key.is_none() { @@ -1608,7 +1629,7 @@ where async fn broadcast_completed_transaction( &mut self, completed_tx: CompletedTransaction, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { let tx_id = completed_tx.tx_id; if !(completed_tx.status == TransactionStatus::Completed || @@ -1653,7 +1674,7 @@ where /// node. async fn broadcast_all_completed_transactions( &mut self, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { trace!(target: LOG_TARGET, "Attempting to Broadcast all Completed Transactions"); let completed_txs = self.db.get_completed_transactions().await?; @@ -1673,7 +1694,7 @@ where /// Handle the final clean up after a Transaction Broadcast protocol completes async fn complete_transaction_broadcast_protocol( &mut self, - join_result: Result, + join_result: Result, ) { match join_result { Ok(id) => { @@ -1684,7 +1705,7 @@ where let _ = self.active_transaction_broadcast_protocols.remove(&id); }, Err(TransactionServiceProtocolError { id, error }) => { - let _ = self.active_transaction_broadcast_protocols.remove(&id); + let _ = self.active_transaction_broadcast_protocols.remove(&TxId::from(id)); if let TransactionServiceError::Shutdown = error { return; @@ -1749,7 +1770,7 @@ where message: String, maturity: Option, ) -> Result { - let tx_id = OsRng.next_u64(); + let tx_id = TxId::new_random(); self.db .add_utxo_import_transaction( tx_id, @@ -1778,7 +1799,7 @@ where async fn submit_transaction( &mut self, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, completed_transaction: CompletedTransaction, ) -> Result<(), TransactionServiceError> { @@ -1799,10 +1820,10 @@ where /// Submit a completed coin split transaction to the Transaction Manager. This is different from /// `submit_transaction` in that it will expose less information about the completed transaction. - pub async fn submit_coin_split_transaction( + pub async fn submit_self_send_transaction( &mut self, transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, tx_id: TxId, tx: Transaction, @@ -1817,6 +1838,7 @@ where self.node_identity.public_key().clone(), self.node_identity.public_key().clone(), amount, + None, fee, tx, TransactionStatus::Completed, @@ -1836,7 +1858,7 @@ where fees: MicroTari, block_height: u64, coinbase_monitoring_protocol_join_handles: &mut FuturesUnordered< - JoinHandle>, + JoinHandle>, >, ) -> Result { let amount = reward + fees; @@ -1861,7 +1883,7 @@ where }, None => { // otherwise create a new coinbase tx - let tx_id = OsRng.next_u64(); + let tx_id = TxId::new_random(); let tx = self .output_manager_service .get_coinbase_transaction(tx_id, reward, fees, block_height) @@ -1880,6 +1902,7 @@ where self.node_identity.public_key().clone(), self.node_identity.public_key().clone(), amount, + None, MicroTari::from(0), tx.clone(), TransactionStatus::Coinbase, @@ -1930,7 +1953,7 @@ where async fn start_coinbase_transaction_monitoring_protocol( &mut self, tx_id: TxId, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { let completed_tx = self.db.get_completed_transaction(tx_id).await?; @@ -1977,7 +2000,7 @@ where /// Handle the final clean up after a Coinbase Transaction Monitoring protocol completes fn complete_coinbase_transaction_monitoring_protocol( &mut self, - join_result: Result, + join_result: Result, ) { match join_result { Ok(id) => { @@ -1990,7 +2013,7 @@ where ); }, Err(TransactionServiceProtocolError { id, error }) => { - let _ = self.active_coinbase_monitoring_protocols.remove(&id); + let _ = self.active_coinbase_monitoring_protocols.remove(&TxId::from(id)); if let TransactionServiceError::Shutdown = error { return; } @@ -2009,7 +2032,7 @@ where /// they have been mined async fn restart_chain_monitoring_for_all_coinbase_transactions( &mut self, - join_handles: &mut FuturesUnordered>>, + join_handles: &mut FuturesUnordered>>, ) -> Result<(), TransactionServiceError> { trace!( target: LOG_TARGET, diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs index 7cbaa52c85..acdd0ad5aa 100644 --- a/base_layer/wallet/src/transaction_service/storage/database.rs +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - output_manager_service::TxId, transaction_service::{ error::TransactionStorageError, storage::models::{ @@ -46,6 +45,7 @@ use std::{ use tari_common_types::types::BlindingFactor; use tari_comms::types::CommsPublicKey; use tari_core::transactions::{tari_amount::MicroTari, transaction::Transaction}; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::database"; @@ -615,6 +615,7 @@ where T: TransactionBackend + 'static source_public_key.clone(), comms_public_key.clone(), amount, + None, MicroTari::from(0), Transaction::new( Vec::new(), diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index 4d1f57f238..2c749f6a50 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{output_manager_service::TxId, transaction_service::error::TransactionStorageError}; +use crate::{transaction_service::error::TransactionStorageError}; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; use std::{ @@ -35,6 +35,7 @@ use tari_core::transactions::{ ReceiverTransactionProtocol, SenderTransactionProtocol, }; +use tari_core::transactions::transaction_protocol::TxId; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum TransactionStatus { @@ -100,6 +101,7 @@ pub struct InboundTransaction { pub tx_id: TxId, pub source_public_key: CommsPublicKey, pub amount: MicroTari, + pub unique_id: Option>, pub receiver_protocol: ReceiverTransactionProtocol, pub status: TransactionStatus, pub message: String, @@ -115,6 +117,7 @@ impl InboundTransaction { tx_id: TxId, source_public_key: CommsPublicKey, amount: MicroTari, + unique_id: Option>, receiver_protocol: ReceiverTransactionProtocol, status: TransactionStatus, message: String, @@ -124,6 +127,7 @@ impl InboundTransaction { tx_id, source_public_key, amount, + unique_id, receiver_protocol, status, message, @@ -150,6 +154,7 @@ pub struct OutboundTransaction { pub direct_send_success: bool, pub send_count: u32, pub last_send_timestamp: Option, + pub unique_id: Option> } impl OutboundTransaction { @@ -158,6 +163,7 @@ impl OutboundTransaction { tx_id: TxId, destination_public_key: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee: MicroTari, sender_protocol: SenderTransactionProtocol, status: TransactionStatus, @@ -169,6 +175,7 @@ impl OutboundTransaction { tx_id, destination_public_key, amount, + unique_id, fee, sender_protocol, status, @@ -188,6 +195,7 @@ pub struct CompletedTransaction { pub source_public_key: CommsPublicKey, pub destination_public_key: CommsPublicKey, pub amount: MicroTari, + pub unique_id: Option>, pub fee: MicroTari, pub transaction: Transaction, pub status: TransactionStatus, @@ -210,6 +218,7 @@ impl CompletedTransaction { source_public_key: CommsPublicKey, destination_public_key: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee: MicroTari, transaction: Transaction, status: TransactionStatus, @@ -223,6 +232,7 @@ impl CompletedTransaction { source_public_key, destination_public_key, amount, + unique_id, fee, transaction, status, @@ -279,6 +289,7 @@ impl From for InboundTransaction { tx_id: ct.tx_id, source_public_key: ct.source_public_key, amount: ct.amount, + unique_id: ct.unique_id, receiver_protocol: ReceiverTransactionProtocol::new_placeholder(), status: ct.status, message: ct.message, @@ -297,6 +308,7 @@ impl From for OutboundTransaction { tx_id: ct.tx_id, destination_public_key: ct.destination_public_key, amount: ct.amount, + unique_id: ct.unique_id.clone(), fee: ct.fee, sender_protocol: SenderTransactionProtocol::new_placeholder(), status: ct.status, @@ -317,6 +329,7 @@ impl From for CompletedTransaction { source_public_key: Default::default(), destination_public_key: tx.destination_public_key, amount: tx.amount, + unique_id: tx.unique_id, fee: tx.fee, status: tx.status, message: tx.message, @@ -341,6 +354,7 @@ impl From for CompletedTransaction { source_public_key: tx.source_public_key, destination_public_key: Default::default(), amount: tx.amount, + unique_id: tx.unique_id, fee: MicroTari::from(0), status: tx.status, message: tx.message, diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs index 700590562e..4bd40b1145 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -41,7 +41,6 @@ use tari_comms::types::CommsPublicKey; use tari_core::transactions::tari_amount::MicroTari; use crate::{ - output_manager_service::TxId, schema::{completed_transactions, inbound_transactions, outbound_transactions}, storage::sqlite_utilities::WalletDbConnection, transaction_service::{ @@ -60,6 +59,7 @@ use crate::{ }, util::encryption::{decrypt_bytes_integral_nonce, encrypt_bytes_integral_nonce, Encryptable}, }; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::database::sqlite_db"; @@ -294,7 +294,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { let mut result = HashMap::new(); for o in OutboundTransactionSql::index_by_cancelled(&(*conn), false)?.iter_mut() { self.decrypt_if_necessary(o)?; - result.insert(o.tx_id as u64, OutboundTransaction::try_from((*o).clone())?); + result.insert((o.tx_id as u64).into(), OutboundTransaction::try_from((*o).clone())?); } Some(DbValue::PendingOutboundTransactions(result)) @@ -303,7 +303,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { let mut result = HashMap::new(); for i in InboundTransactionSql::index_by_cancelled(&(*conn), false)?.iter_mut() { self.decrypt_if_necessary(i)?; - result.insert(i.tx_id as u64, InboundTransaction::try_from((*i).clone())?); + result.insert((i.tx_id as u64).into(), InboundTransaction::try_from((*i).clone())?); } Some(DbValue::PendingInboundTransactions(result)) @@ -312,7 +312,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { let mut result = HashMap::new(); for c in CompletedTransactionSql::index_by_cancelled(&(*conn), false)?.iter_mut() { self.decrypt_if_necessary(c)?; - result.insert(c.tx_id as u64, CompletedTransaction::try_from((*c).clone())?); + result.insert((c.tx_id as u64).into(), CompletedTransaction::try_from((*c).clone())?); } Some(DbValue::CompletedTransactions(result)) @@ -321,7 +321,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { let mut result = HashMap::new(); for o in OutboundTransactionSql::index_by_cancelled(&(*conn), true)?.iter_mut() { self.decrypt_if_necessary(o)?; - result.insert(o.tx_id as u64, OutboundTransaction::try_from((*o).clone())?); + result.insert((o.tx_id as u64).into(), OutboundTransaction::try_from((*o).clone())?); } Some(DbValue::PendingOutboundTransactions(result)) @@ -330,7 +330,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { let mut result = HashMap::new(); for i in InboundTransactionSql::index_by_cancelled(&(*conn), true)?.iter_mut() { self.decrypt_if_necessary(i)?; - result.insert(i.tx_id as u64, InboundTransaction::try_from((*i).clone())?); + result.insert((i.tx_id as u64).into(), InboundTransaction::try_from((*i).clone())?); } Some(DbValue::PendingInboundTransactions(result)) @@ -339,7 +339,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { let mut result = HashMap::new(); for c in CompletedTransactionSql::index_by_cancelled(&(*conn), true)?.iter_mut() { self.decrypt_if_necessary(c)?; - result.insert(c.tx_id as u64, CompletedTransaction::try_from((*c).clone())?); + result.insert((c.tx_id as u64).into(), CompletedTransaction::try_from((*c).clone())?); } Some(DbValue::CompletedTransactions(result)) @@ -416,7 +416,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { } } - fn transaction_exists(&self, tx_id: u64) -> Result { + fn transaction_exists(&self, tx_id: TxId) -> Result { let conn = self.database_connection.acquire_lock(); Ok( @@ -428,7 +428,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { fn get_pending_transaction_counterparty_pub_key_by_tx_id( &self, - tx_id: u64, + tx_id: TxId, ) -> Result { let conn = self.database_connection.acquire_lock(); @@ -448,7 +448,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { fn complete_outbound_transaction( &self, - tx_id: u64, + tx_id: TxId, completed_transaction: CompletedTransaction, ) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); @@ -476,7 +476,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { fn complete_inbound_transaction( &self, - tx_id: u64, + tx_id: TxId, completed_transaction: CompletedTransaction, ) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); @@ -502,7 +502,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn broadcast_completed_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn broadcast_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { @@ -534,7 +534,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn mine_completed_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn mine_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { @@ -564,7 +564,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn cancel_completed_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn cancel_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -582,7 +582,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { fn set_pending_transaction_cancellation_status( &self, - tx_id: u64, + tx_id: TxId, cancelled: bool, ) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); @@ -605,7 +605,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn mark_direct_send_success(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn mark_direct_send_success(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match InboundTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -778,7 +778,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(None) } - fn increment_send_count(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn increment_send_count(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); if let Ok(tx) = CompletedTransactionSql::find(tx_id, &conn) { @@ -820,7 +820,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn confirm_broadcast_or_coinbase_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn confirm_broadcast_or_coinbase_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -844,7 +844,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn unconfirm_mined_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + fn unconfirm_mined_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -866,7 +866,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn set_completed_transaction_validity(&self, tx_id: u64, valid: bool) -> Result<(), TransactionStorageError> { + fn set_completed_transaction_validity(&self, tx_id: TxId, valid: bool) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -882,7 +882,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn update_confirmations(&self, tx_id: u64, confirmations: u64) -> Result<(), TransactionStorageError> { + fn update_confirmations(&self, tx_id: TxId, confirmations: u64) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -898,7 +898,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn update_mined_height(&self, tx_id: u64, mined_height: u64) -> Result<(), TransactionStorageError> { + fn update_mined_height(&self, tx_id: TxId, mined_height: u64) -> Result<(), TransactionStorageError> { let conn = self.database_connection.acquire_lock(); match CompletedTransactionSql::find_by_cancelled(tx_id, false, &(*conn)) { Ok(v) => { @@ -928,6 +928,7 @@ struct InboundTransactionSql { direct_send_success: i32, send_count: i32, last_send_timestamp: Option, + unique_id: Option>, } impl InboundTransactionSql { @@ -953,7 +954,7 @@ impl InboundTransactionSql { pub fn find(tx_id: TxId, conn: &SqliteConnection) -> Result { Ok(inbound_transactions::table - .filter(inbound_transactions::tx_id.eq(tx_id as i64)) + .filter(inbound_transactions::tx_id.eq(tx_id.as_u64() as i64)) .first::(conn)?) } @@ -963,7 +964,7 @@ impl InboundTransactionSql { conn: &SqliteConnection, ) -> Result { Ok(inbound_transactions::table - .filter(inbound_transactions::tx_id.eq(tx_id as i64)) + .filter(inbound_transactions::tx_id.eq(tx_id.as_u64() as i64)) .filter(inbound_transactions::cancelled.eq(cancelled as i32)) .first::(conn)?) } @@ -1051,9 +1052,10 @@ impl TryFrom for InboundTransactionSql { fn try_from(i: InboundTransaction) -> Result { Ok(Self { - tx_id: i.tx_id as i64, + tx_id: i.tx_id.as_u64() as i64, source_public_key: i.source_public_key.to_vec(), amount: u64::from(i.amount) as i64, + unique_id: i.unique_id, receiver_protocol: serde_json::to_string(&i.receiver_protocol)?, message: i.message, timestamp: i.timestamp, @@ -1070,10 +1072,11 @@ impl TryFrom for InboundTransaction { fn try_from(i: InboundTransactionSql) -> Result { Ok(Self { - tx_id: i.tx_id as u64, + tx_id: (i.tx_id as u64).into(), source_public_key: PublicKey::from_vec(&i.source_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid Source Publickey".to_string()))?, amount: MicroTari::from(i.amount as u64), + unique_id: i.unique_id, receiver_protocol: serde_json::from_str(&i.receiver_protocol)?, status: TransactionStatus::Pending, message: i.message, @@ -1111,6 +1114,7 @@ struct OutboundTransactionSql { direct_send_success: i32, send_count: i32, last_send_timestamp: Option, + unique_id: Option>, } impl OutboundTransactionSql { @@ -1136,7 +1140,7 @@ impl OutboundTransactionSql { pub fn find(tx_id: TxId, conn: &SqliteConnection) -> Result { Ok(outbound_transactions::table - .filter(outbound_transactions::tx_id.eq(tx_id as i64)) + .filter(outbound_transactions::tx_id.eq(tx_id.as_u64() as i64)) .first::(conn)?) } @@ -1146,7 +1150,7 @@ impl OutboundTransactionSql { conn: &SqliteConnection, ) -> Result { Ok(outbound_transactions::table - .filter(outbound_transactions::tx_id.eq(tx_id as i64)) + .filter(outbound_transactions::tx_id.eq(tx_id.as_u64() as i64)) .filter(outbound_transactions::cancelled.eq(cancelled as i32)) .first::(conn)?) } @@ -1234,7 +1238,7 @@ impl TryFrom for OutboundTransactionSql { fn try_from(o: OutboundTransaction) -> Result { Ok(Self { - tx_id: o.tx_id as i64, + tx_id: o.tx_id.as_u64() as i64, destination_public_key: o.destination_public_key.to_vec(), amount: u64::from(o.amount) as i64, fee: u64::from(o.fee) as i64, @@ -1245,6 +1249,7 @@ impl TryFrom for OutboundTransactionSql { direct_send_success: o.direct_send_success as i32, send_count: o.send_count as i32, last_send_timestamp: o.last_send_timestamp, + unique_id: o.unique_id, }) } } @@ -1254,10 +1259,11 @@ impl TryFrom for OutboundTransaction { fn try_from(o: OutboundTransactionSql) -> Result { Ok(Self { - tx_id: o.tx_id as u64, + tx_id: (o.tx_id as u64).into(), destination_public_key: PublicKey::from_vec(&o.destination_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid destination PublicKey".to_string()))?, amount: MicroTari::from(o.amount as u64), + unique_id: o.unique_id, fee: MicroTari::from(o.fee as u64), sender_protocol: serde_json::from_str(&o.sender_protocol)?, status: TransactionStatus::Pending, @@ -1302,6 +1308,7 @@ struct CompletedTransactionSql { valid: i32, confirmations: Option, mined_height: Option, + unique_id: Option>, } impl CompletedTransactionSql { @@ -1337,7 +1344,7 @@ impl CompletedTransactionSql { pub fn find(tx_id: TxId, conn: &SqliteConnection) -> Result { Ok(completed_transactions::table - .filter(completed_transactions::tx_id.eq(tx_id as i64)) + .filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64)) .first::(conn)?) } @@ -1347,7 +1354,7 @@ impl CompletedTransactionSql { conn: &SqliteConnection, ) -> Result { Ok(completed_transactions::table - .filter(completed_transactions::tx_id.eq(tx_id as i64)) + .filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64)) .filter(completed_transactions::cancelled.eq(cancelled as i32)) .first::(conn)?) } @@ -1557,10 +1564,11 @@ impl TryFrom for CompletedTransactionSql { fn try_from(c: CompletedTransaction) -> Result { Ok(Self { - tx_id: c.tx_id as i64, + tx_id: c.tx_id.as_u64() as i64, source_public_key: c.source_public_key.to_vec(), destination_public_key: c.destination_public_key.to_vec(), amount: u64::from(c.amount) as i64, + unique_id: c.unique_id, fee: u64::from(c.fee) as i64, transaction_protocol: serde_json::to_string(&c.transaction)?, status: c.status as i32, @@ -1583,12 +1591,13 @@ impl TryFrom for CompletedTransaction { fn try_from(c: CompletedTransactionSql) -> Result { Ok(Self { - tx_id: c.tx_id as u64, + tx_id: (c.tx_id as u64).into(), source_public_key: PublicKey::from_vec(&c.source_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid source Publickey".to_string()))?, destination_public_key: PublicKey::from_vec(&c.destination_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid destination PublicKey".to_string()))?, amount: MicroTari::from(c.amount as u64), + unique_id: c.unique_id, fee: MicroTari::from(c.fee as u64), transaction: serde_json::from_str(&c.transaction_protocol)?, status: TransactionStatus::try_from(c.status)?, diff --git a/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs b/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs index 957ab33a48..f92e07e675 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs @@ -20,7 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - output_manager_service::TxId, transaction_service::{ config::TransactionRoutingMechanism, error::TransactionServiceError, @@ -36,6 +35,7 @@ use tari_comms_dht::{ }; use tari_core::transactions::{transaction::Transaction, transaction_protocol::proto}; use tari_p2p::tari_message::TariMessageType; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::tasks::send_finalized_transaction"; const LOG_TARGET_STRESS: &str = "stress_test::send_finalized_transaction"; @@ -62,7 +62,7 @@ pub async fn send_finalized_transaction_message( }, TransactionRoutingMechanism::StoreAndForwardOnly => { let finalized_transaction_message = proto::TransactionFinalizedMessage { - tx_id, + tx_id: tx_id.into(), transaction: Some(transaction.clone().into()), }; let store_and_forward_send_result = send_transaction_finalized_message_store_and_forward( @@ -90,7 +90,7 @@ pub async fn send_finalized_transaction_message_direct( transaction_routing_mechanism: TransactionRoutingMechanism, ) -> Result<(), TransactionServiceError> { let finalized_transaction_message = proto::TransactionFinalizedMessage { - tx_id, + tx_id: tx_id.into(), transaction: Some(transaction.clone().into()), }; let mut store_and_forward_send_result = false; diff --git a/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs b/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs index 326093d986..cf610e8ac7 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs @@ -19,13 +19,13 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{output_manager_service::TxId, transaction_service::error::TransactionServiceError}; +use crate::{transaction_service::error::TransactionServiceError}; use tari_comms::{peer_manager::NodeId, types::CommsPublicKey}; use tari_comms_dht::{ domain_message::OutboundDomainMessage, outbound::{OutboundEncryption, OutboundMessageRequester}, }; -use tari_core::transactions::transaction_protocol::proto; +use tari_core::transactions::transaction_protocol::{proto, TxId}; use tari_p2p::tari_message::TariMessageType; pub async fn send_transaction_cancelled_message( @@ -33,7 +33,7 @@ pub async fn send_transaction_cancelled_message( destination_public_key: CommsPublicKey, mut outbound_message_service: OutboundMessageRequester, ) -> Result<(), TransactionServiceError> { - let proto_message = proto::TransactionCancelledMessage { tx_id }; + let proto_message = proto::TransactionCancelledMessage { tx_id: tx_id.into() }; // Send both direct and SAF we are not going to monitor the progress on these messages for potential resend as // they are just courtesy messages diff --git a/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs b/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs index ab16ceb9b6..43684ec641 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs @@ -20,7 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::output_manager_service::TxId; use log::*; use tari_comms_dht::{domain_message::OutboundDomainMessage, outbound::SendMessageResponse}; use tari_p2p::tari_message::TariMessageType; @@ -34,7 +33,7 @@ use crate::transaction_service::{ use std::time::Duration; use tari_comms::{peer_manager::NodeId, types::CommsPublicKey}; use tari_comms_dht::outbound::{OutboundEncryption, OutboundMessageRequester}; -use tari_core::transactions::transaction_protocol::proto; +use tari_core::transactions::transaction_protocol::{proto, TxId}; const LOG_TARGET: &str = "wallet::transaction_service::tasks::send_transaction_reply"; diff --git a/base_layer/wallet/src/transaction_service/tasks/wait_on_dial.rs b/base_layer/wallet/src/transaction_service/tasks/wait_on_dial.rs index d9c6328dbb..2940f366c0 100644 --- a/base_layer/wallet/src/transaction_service/tasks/wait_on_dial.rs +++ b/base_layer/wallet/src/transaction_service/tasks/wait_on_dial.rs @@ -20,11 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::output_manager_service::TxId; use log::*; use std::time::Duration; use tari_comms::types::CommsPublicKey; use tari_comms_dht::outbound::MessageSendStates; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::tasks"; const LOG_TARGET_STRESS: &str = "stress_test::transaction_service::tasks"; diff --git a/base_layer/wallet/src/types.rs b/base_layer/wallet/src/types.rs index 25457cc666..3017a0810e 100644 --- a/base_layer/wallet/src/types.rs +++ b/base_layer/wallet/src/types.rs @@ -20,8 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::error::WalletError; +use rand::rngs::OsRng; +use tari_common_types::types::{PrivateKey, PublicKey}; use tari_core::transactions::tari_amount::MicroTari; -use tari_crypto::common::Blake256; +use tari_crypto::{common::Blake256, keys::PublicKey as OtherPublicKey}; +use tari_key_manager::key_manager::KeyManager; /// The default fee per gram that the wallet will use to build transactions. /// TODO discuss what the default fee value should actually be @@ -38,3 +42,25 @@ pub enum ValidationRetryStrategy { Limited(u8), UntilSuccess, } + +pub(crate) trait PersistentKeyManager { + fn create_and_store_new(&mut self) -> Result; +} + +pub(crate) struct MockPersistentKeyManager { + key_manager: KeyManager, +} + +impl MockPersistentKeyManager { + pub fn new() -> Self { + Self { + key_manager: KeyManager::new(&mut OsRng), + } + } +} + +impl PersistentKeyManager for MockPersistentKeyManager { + fn create_and_store_new(&mut self) -> Result { + Ok(PublicKey::from_secret_key(&self.key_manager.next_key().unwrap().k)) + } +} diff --git a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs index 31955c981c..a32f5371cc 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs @@ -62,7 +62,7 @@ use tari_shutdown::ShutdownSignal; use crate::{ error::WalletError, - output_manager_service::{handle::OutputManagerHandle, TxId}, + output_manager_service::handle::OutputManagerHandle, storage::{ database::{WalletBackend, WalletDatabase}, sqlite_db::WalletSqliteDatabase, @@ -74,6 +74,7 @@ use crate::{ }, WalletSqlite, }; +use tari_core::transactions::transaction_protocol::TxId; use tokio::time::MissedTickBehavior; pub const LOG_TARGET: &str = "wallet::utxo_scanning"; diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index f29b78cdfc..19cd0a9dfa 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -66,6 +66,7 @@ use tari_service_framework::StackBuilder; use tari_shutdown::ShutdownSignal; use crate::{ + assets::AssetManagerHandle, base_node_service::{handle::BaseNodeServiceHandle, BaseNodeServiceInitializer}, config::{WalletConfig, KEY_MANAGER_COMMS_SECRET_KEY_BRANCH_KEY}, connectivity_service::{WalletConnectivityHandle, WalletConnectivityInitializer}, @@ -76,7 +77,6 @@ use crate::{ handle::OutputManagerHandle, storage::{database::OutputManagerBackend, models::KnownOneSidedPaymentScript}, OutputManagerServiceInitializer, - TxId, }, storage::database::{WalletBackend, WalletDatabase}, transaction_service::{ @@ -88,6 +88,12 @@ use crate::{ utxo_scanner_service::{handle::UtxoScannerHandle, UtxoScannerServiceInitializer}, }; +use crate::{ + assets::infrastructure::initializer::AssetManagerServiceInitializer, + tokens::{infrastructure::initializer::TokenManagerServiceInitializer, TokenManagerHandle}, +}; +use tari_core::transactions::transaction_protocol::TxId; + const LOG_TARGET: &str = "wallet"; /// A structure containing the config and services that a Wallet application will require. This struct will start up all @@ -109,6 +115,8 @@ where pub contacts_service: ContactsServiceHandle, pub base_node_service: BaseNodeServiceHandle, pub utxo_scanner_service: UtxoScannerHandle, + pub asset_manager: AssetManagerHandle, + pub token_manager: TokenManagerHandle, pub updater_service: Option, pub db: WalletDatabase, pub factories: CryptoFactories, @@ -176,7 +184,7 @@ where .add_initializer(P2pInitializer::new(comms_config, publisher)) .add_initializer(OutputManagerServiceInitializer::new( config.output_manager_service_config.unwrap_or_default(), - output_manager_backend, + output_manager_backend.clone(), factories.clone(), config.network, master_secret_key, @@ -199,7 +207,9 @@ where wallet_database.clone(), factories.clone(), node_identity.clone(), - )); + )) + .add_initializer(AssetManagerServiceInitializer::new(output_manager_backend.clone())) + .add_initializer(TokenManagerServiceInitializer::new(output_manager_backend)); // Check if we have update config. FFI wallets don't do this, the update on mobile is done differently. let stack = match config.updater_config { @@ -230,6 +240,8 @@ where let base_node_service_handle = handles.expect_handle::(); let utxo_scanner_service_handle = handles.expect_handle::(); + let asset_manager_handle = handles.expect_handle::(); + let token_manager_handle = handles.expect_handle::(); let wallet_connectivity = handles.expect_handle::(); let updater_handle = config .updater_config @@ -264,6 +276,10 @@ where wallet_connectivity, db: wallet_database, factories, + asset_manager: asset_manager_handle, + token_manager: token_manager_handle, + #[cfg(feature = "test_harness")] + transaction_backend: transaction_backend_handle, _u: PhantomData, _v: PhantomData, _w: PhantomData, @@ -387,6 +403,9 @@ where script_private_key.clone(), sender_offset_public_key.clone(), metadata_signature, + // TODO: Allow importing of unique ids + None, + None, ); let tx_id = self diff --git a/base_layer/wallet_ffi/src/callback_handler.rs b/base_layer/wallet_ffi/src/callback_handler.rs index c5af01c7b6..23c3d8c3ac 100644 --- a/base_layer/wallet_ffi/src/callback_handler.rs +++ b/base_layer/wallet_ffi/src/callback_handler.rs @@ -52,20 +52,17 @@ use log::*; use tari_comms::types::CommsPublicKey; use tari_comms_dht::event::{DhtEvent, DhtEventReceiver}; use tari_shutdown::ShutdownSignal; -use tari_wallet::{ - output_manager_service::{ - handle::{OutputManagerEvent, OutputManagerEventReceiver}, - TxId, - TxoValidationType, +use tari_wallet::{output_manager_service::{ + handle::{OutputManagerEvent, OutputManagerEventReceiver}, + TxoValidationType, +}, transaction_service::{ + handle::{TransactionEvent, TransactionEventReceiver}, + storage::{ + database::{TransactionBackend, TransactionDatabase}, + models::{CompletedTransaction, InboundTransaction}, }, - transaction_service::{ - handle::{TransactionEvent, TransactionEventReceiver}, - storage::{ - database::{TransactionBackend, TransactionDatabase}, - models::{CompletedTransaction, InboundTransaction}, - }, - }, -}; +}, OperationId}; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::callback_handler"; @@ -86,8 +83,8 @@ where TBackend: TransactionBackend + 'static callback_transaction_broadcast: unsafe extern "C" fn(*mut CompletedTransaction), callback_transaction_mined: unsafe extern "C" fn(*mut CompletedTransaction), callback_transaction_mined_unconfirmed: unsafe extern "C" fn(*mut CompletedTransaction, u64), - callback_direct_send_result: unsafe extern "C" fn(TxId, bool), - callback_store_and_forward_send_result: unsafe extern "C" fn(TxId, bool), + callback_direct_send_result: unsafe extern "C" fn(u64, bool), + callback_store_and_forward_send_result: unsafe extern "C" fn(u64, bool), callback_transaction_cancellation: unsafe extern "C" fn(*mut CompletedTransaction), callback_utxo_validation_complete: unsafe extern "C" fn(u64, u8), callback_stxo_validation_complete: unsafe extern "C" fn(u64, u8), @@ -119,13 +116,13 @@ where TBackend: TransactionBackend + 'static callback_transaction_broadcast: unsafe extern "C" fn(*mut CompletedTransaction), callback_transaction_mined: unsafe extern "C" fn(*mut CompletedTransaction), callback_transaction_mined_unconfirmed: unsafe extern "C" fn(*mut CompletedTransaction, u64), - callback_direct_send_result: unsafe extern "C" fn(TxId, bool), - callback_store_and_forward_send_result: unsafe extern "C" fn(TxId, bool), + callback_direct_send_result: unsafe extern "C" fn(u64, bool), + callback_store_and_forward_send_result: unsafe extern "C" fn(u64, bool), callback_transaction_cancellation: unsafe extern "C" fn(*mut CompletedTransaction), - callback_utxo_validation_complete: unsafe extern "C" fn(TxId, u8), - callback_stxo_validation_complete: unsafe extern "C" fn(TxId, u8), - callback_invalid_txo_validation_complete: unsafe extern "C" fn(TxId, u8), - callback_transaction_validation_complete: unsafe extern "C" fn(TxId, u8), + callback_utxo_validation_complete: unsafe extern "C" fn(u64, u8), + callback_stxo_validation_complete: unsafe extern "C" fn(u64, u8), + callback_invalid_txo_validation_complete: unsafe extern "C" fn(u64, u8), + callback_transaction_validation_complete: unsafe extern "C" fn(u64, u8), callback_saf_messages_received: unsafe extern "C" fn(), ) -> Self { info!( @@ -318,7 +315,7 @@ where TBackend: TransactionBackend + 'static Ok(tx) => { debug!( target: LOG_TARGET, - "Calling Received Transaction callback function for TxId: {}", tx_id + "Calling Received Transaction callback function for u64: {}", tx_id ); let boxing = Box::into_raw(Box::new(tx)); unsafe { @@ -337,7 +334,7 @@ where TBackend: TransactionBackend + 'static Ok(tx) => { debug!( target: LOG_TARGET, - "Calling Received Transaction Reply callback function for TxId: {}", tx_id + "Calling Received Transaction Reply callback function for u64: {}", tx_id ); let boxing = Box::into_raw(Box::new(tx)); unsafe { @@ -370,7 +367,7 @@ where TBackend: TransactionBackend + 'static "Calling Direct Send Result callback function for TxId: {} with result {}", tx_id, result ); unsafe { - (self.callback_direct_send_result)(tx_id, result); + (self.callback_direct_send_result)(tx_id.as_u64(), result); } } @@ -380,19 +377,19 @@ where TBackend: TransactionBackend + 'static "Calling Store and Forward Send Result callback function for TxId: {} with result {}", tx_id, result ); unsafe { - (self.callback_store_and_forward_send_result)(tx_id, result); + (self.callback_store_and_forward_send_result)(tx_id.as_u64(), result); } } async fn receive_transaction_cancellation(&mut self, tx_id: TxId) { let mut transaction = None; - if let Ok(tx) = self.db.get_cancelled_completed_transaction(tx_id).await { + if let Ok(tx) = self.db.get_cancelled_completed_transaction(tx_id.into()).await { transaction = Some(tx); - } else if let Ok(tx) = self.db.get_cancelled_pending_outbound_transaction(tx_id).await { + } else if let Ok(tx) = self.db.get_cancelled_pending_outbound_transaction(tx_id.into()).await { let mut outbound_tx = CompletedTransaction::from(tx); outbound_tx.source_public_key = self.comms_public_key.clone(); transaction = Some(outbound_tx); - } else if let Ok(tx) = self.db.get_cancelled_pending_inbound_transaction(tx_id).await { + } else if let Ok(tx) = self.db.get_cancelled_pending_inbound_transaction(tx_id.into()).await { let mut inbound_tx = CompletedTransaction::from(tx); inbound_tx.destination_public_key = self.comms_public_key.clone(); transaction = Some(inbound_tx); @@ -417,7 +414,7 @@ where TBackend: TransactionBackend + 'static } async fn receive_transaction_broadcast_event(&mut self, tx_id: TxId) { - match self.db.get_completed_transaction(tx_id).await { + match self.db.get_completed_transaction(tx_id.into()).await { Ok(tx) => { debug!( target: LOG_TARGET, @@ -433,7 +430,7 @@ where TBackend: TransactionBackend + 'static } async fn receive_transaction_mined_event(&mut self, tx_id: TxId) { - match self.db.get_completed_transaction(tx_id).await { + match self.db.get_completed_transaction(tx_id.into()).await { Ok(tx) => { debug!( target: LOG_TARGET, @@ -449,7 +446,7 @@ where TBackend: TransactionBackend + 'static } async fn receive_transaction_mined_unconfirmed_event(&mut self, tx_id: TxId, confirmations: u64) { - match self.db.get_completed_transaction(tx_id).await { + match self.db.get_completed_transaction(tx_id.into()).await { Ok(tx) => { debug!( target: LOG_TARGET, @@ -464,7 +461,7 @@ where TBackend: TransactionBackend + 'static } } - fn transaction_validation_complete_event(&mut self, request_key: u64, result: CallbackValidationResults) { + fn transaction_validation_complete_event(&mut self, request_key: OperationId, result: CallbackValidationResults) { debug!( target: LOG_TARGET, "Calling Transaction Validation Complete callback function for Request Key: {} with result {:?}", @@ -473,17 +470,17 @@ where TBackend: TransactionBackend + 'static ); match result { CallbackValidationResults::Success => unsafe { - (self.callback_transaction_validation_complete)(request_key, CallbackValidationResults::Success as u8); + (self.callback_transaction_validation_complete)(request_key.as_u64(), CallbackValidationResults::Success as u8); }, CallbackValidationResults::Aborted => unsafe { - (self.callback_transaction_validation_complete)(request_key, CallbackValidationResults::Aborted as u8); + (self.callback_transaction_validation_complete)(request_key.as_u64(), CallbackValidationResults::Aborted as u8); }, CallbackValidationResults::Failure => unsafe { - (self.callback_transaction_validation_complete)(request_key, CallbackValidationResults::Failure as u8); + (self.callback_transaction_validation_complete)(request_key.as_u64(), CallbackValidationResults::Failure as u8); }, CallbackValidationResults::BaseNodeNotInSync => unsafe { (self.callback_transaction_validation_complete)( - request_key, + request_key.as_u64(), CallbackValidationResults::BaseNodeNotInSync as u8, ); }, diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 21306353f6..1114c37974 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -204,6 +204,7 @@ mod callback_handler; mod enums; mod error; mod tasks; +use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet_ffi"; @@ -1441,7 +1442,7 @@ pub unsafe extern "C" fn completed_transaction_get_transaction_id( ptr::swap(error_out, &mut error as *mut c_int); return 0; } - (*transaction).tx_id as c_ulonglong + (*transaction).tx_id.as_u64() as c_ulonglong } /// Gets the destination TariPublicKey of a TariCompletedTransaction @@ -1948,7 +1949,7 @@ pub unsafe extern "C" fn pending_outbound_transaction_get_transaction_id( ptr::swap(error_out, &mut error as *mut c_int); return 0; } - (*transaction).tx_id as c_ulonglong + (*transaction).tx_id.as_u64() as c_ulonglong } /// Gets the destination TariPublicKey of a TariPendingOutboundTransaction @@ -2175,7 +2176,7 @@ pub unsafe extern "C" fn pending_inbound_transaction_get_transaction_id( ptr::swap(error_out, &mut error as *mut c_int); return 0; } - (*transaction).tx_id as c_ulonglong + (*transaction).tx_id.as_u64() as c_ulonglong } /// Gets the source TariPublicKey of a TariPendingInboundTransaction @@ -3453,10 +3454,11 @@ pub unsafe extern "C" fn wallet_send_transaction( .block_on((*wallet).wallet.transaction_service.send_transaction( (*dest_public_key).clone(), MicroTari::from(amount), + None, MicroTari::from(fee_per_gram), message_string, )) { - Ok(tx_id) => tx_id, + Ok(tx_id) => tx_id.as_u64(), Err(e) => { error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -3938,7 +3940,7 @@ pub unsafe extern "C" fn wallet_get_completed_transaction_by_id( match completed_transactions { Ok(completed_transactions) => { - if let Some(tx) = completed_transactions.get(&transaction_id) { + if let Some(tx) = completed_transactions.get(&TxId::from(transaction_id)) { if tx.status != TransactionStatus::Completed && tx.status != TransactionStatus::Broadcast { let completed = tx.clone(); return Box::into_raw(Box::new(completed)); @@ -3978,6 +3980,7 @@ pub unsafe extern "C" fn wallet_get_pending_inbound_transaction_by_id( error_out: *mut c_int, ) -> *mut TariPendingInboundTransaction { let mut error = 0; + let transaction_id = TxId::from(transaction_id); ptr::swap(error_out, &mut error as *mut c_int); if wallet.is_null() { error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; @@ -4051,6 +4054,7 @@ pub unsafe extern "C" fn wallet_get_pending_outbound_transaction_by_id( error_out: *mut c_int, ) -> *mut TariPendingOutboundTransaction { let mut error = 0; + let transaction_id = TxId::from(transaction_id); ptr::swap(error_out, &mut error as *mut c_int); if wallet.is_null() { error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; @@ -4125,6 +4129,7 @@ pub unsafe extern "C" fn wallet_get_cancelled_transaction_by_id( error_out: *mut c_int, ) -> *mut TariCompletedTransaction { let mut error = 0; + let transaction_id = TxId::from(transaction_id); ptr::swap(error_out, &mut error as *mut c_int); if wallet.is_null() { error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; @@ -4303,7 +4308,7 @@ pub unsafe extern "C" fn wallet_import_utxo( &(*spending_key).clone(), &Default::default(), )) { - Ok(tx_id) => tx_id, + Ok(tx_id) => tx_id.as_u64(), Err(e) => { error = LibWalletError::from(e).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -4341,7 +4346,7 @@ pub unsafe extern "C" fn wallet_cancel_pending_transaction( match (*wallet) .runtime - .block_on((*wallet).wallet.transaction_service.cancel_transaction(transaction_id)) + .block_on((*wallet).wallet.transaction_service.cancel_transaction(TxId::from(transaction_id))) { Ok(_) => true, Err(e) => { @@ -4547,7 +4552,7 @@ pub unsafe extern "C" fn wallet_start_transaction_validation( .transaction_service .validate_transactions(ValidationRetryStrategy::Limited(0)), ) { - Ok(request_key) => request_key, + Ok(request_key) => request_key.as_u64(), Err(e) => { error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -4650,7 +4655,7 @@ pub unsafe extern "C" fn wallet_coin_split( message, Some(lock_height), )) { - Ok(request_key) => request_key, + Ok(request_key) => request_key.as_u64(), Err(e) => { error = LibWalletError::from(e).code; ptr::swap(error_out, &mut error as *mut c_int); diff --git a/common/src/configuration/bootstrap.rs b/common/src/configuration/bootstrap.rs index 79c98f8d97..36570e4f55 100644 --- a/common/src/configuration/bootstrap.rs +++ b/common/src/configuration/bootstrap.rs @@ -250,6 +250,12 @@ impl ConfigBootstrap { Some(&self.base_path), )) }, + ApplicationType::DanNode => { + self.log_config = normalize_path(dir_utils::default_path( + DEFAULT_BASE_NODE_LOG_CONFIG, + Some(&self.base_path), + )) + }, } } @@ -300,6 +306,9 @@ impl ConfigBootstrap { ApplicationType::MiningNode => { install_configuration(&self.log_config, logging::install_default_mining_node_logfile_config) }, + ApplicationType::DanNode => { + install_configuration(&self.log_config, logging::install_default_base_node_logfile_config) + } } } }; @@ -348,6 +357,7 @@ pub enum ApplicationType { MergeMiningProxy, MiningNode, StratumTranscoder, + DanNode } impl ApplicationType { @@ -358,6 +368,7 @@ impl ApplicationType { ConsoleWallet => "Tari Console Wallet", MergeMiningProxy => "Tari Merge Mining Proxy", MiningNode => "Tari Mining Node", + DanNode => "Digital Assets Network Node", StratumTranscoder => "Tari Stratum Transcoder", } } @@ -370,6 +381,7 @@ impl ApplicationType { MergeMiningProxy => "merge_mining_proxy", MiningNode => "miner", StratumTranscoder => "stratum-transcoder", + DanNode =>"dan-node" } } } @@ -384,6 +396,7 @@ impl FromStr for ApplicationType { "console-wallet" | "console_wallet" => Ok(ConsoleWallet), "mm-proxy" | "mm_proxy" => Ok(MergeMiningProxy), "miner" => Ok(MiningNode), + "dan-node" => Ok(DanNode), "stratum-proxy" => Ok(StratumTranscoder), _ => Err(ConfigError::new("Invalid ApplicationType", None)), } diff --git a/common/src/configuration/dan_config.rs b/common/src/configuration/dan_config.rs new file mode 100644 index 0000000000..abb19b28d5 --- /dev/null +++ b/common/src/configuration/dan_config.rs @@ -0,0 +1,58 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ConfigurationError; +use config::Config; +use serde::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +pub struct DanNodeConfig { + pub committee: Vec, + pub phase_timeout: u64, + pub template_id: String, +} + +impl DanNodeConfig { + pub fn convert_if_present(cfg: Config) -> Result, ConfigurationError> { + let section: DanNodeConfig = match cfg.get("dan_node") { + Ok(s) => s, + Err(e) => { + dbg!(e); + return Ok(None); + }, + }; + Ok(Some(section)) + // dbg!(§ion); + // if section.is_empty() { + // Ok(None) + // } else { + // Ok(Some(Self { + // committee: section + // .get("committee") + // .ok_or_else(|| ConfigurationError::new("dan_node.committee", "missing committee"))? + // .into_array()? + // .into_iter() + // .map(|c| c.into_str()) + // .collect::, ConfigError>>()?, + // })) + } +} diff --git a/common/src/configuration/global.rs b/common/src/configuration/global.rs index 7092262ab6..52a84196de 100644 --- a/common/src/configuration/global.rs +++ b/common/src/configuration/global.rs @@ -23,7 +23,7 @@ //! # Global configuration of tari base layer system use crate::{ - configuration::{bootstrap::ApplicationType, Network}, + configuration::{bootstrap::ApplicationType,DanNodeConfig, Network}, ConfigurationError, }; use config::{Config, ConfigError, Environment}; @@ -133,6 +133,7 @@ pub struct GlobalConfig { pub flood_ban_max_msg_count: usize, pub mine_on_tip_only: bool, pub validate_tip_timeout_sec: u64, + pub dan_node: Option, pub mining_pool_address: String, pub mining_wallet_address: String, pub mining_worker_name: String, @@ -775,6 +776,7 @@ fn convert_node_config( flood_ban_max_msg_count, mine_on_tip_only, validate_tip_timeout_sec, + dan_node: DanNodeConfig::convert_if_present(cfg)?, mining_pool_address, mining_wallet_address, mining_worker_name, diff --git a/common/src/configuration/mod.rs b/common/src/configuration/mod.rs index d1e9b50594..2c3ad21c63 100644 --- a/common/src/configuration/mod.rs +++ b/common/src/configuration/mod.rs @@ -40,6 +40,8 @@ pub mod global; pub mod loader; mod network; pub use network::Network; +mod dan_config; pub mod seconds; pub mod utils; pub mod writer; +pub use dan_config::DanNodeConfig; diff --git a/common/src/configuration/network.rs b/common/src/configuration/network.rs index 1498b1e623..f9db42c923 100644 --- a/common/src/configuration/network.rs +++ b/common/src/configuration/network.rs @@ -36,7 +36,7 @@ pub enum Network { LocalNet = 0x10, Ridcully = 0x21, Stibbons = 0x22, - Weatherwax = 0x23, + Weatherwax = 0xa3, Igor = 0x24, } diff --git a/comms/src/connection_manager/wire_mode.rs b/comms/src/connection_manager/wire_mode.rs index d8b5cac5d0..2ae7477988 100644 --- a/comms/src/connection_manager/wire_mode.rs +++ b/comms/src/connection_manager/wire_mode.rs @@ -22,7 +22,7 @@ use std::convert::TryFrom; -pub(crate) const LIVENESS_WIRE_MODE: u8 = 0x46; // E +pub(crate) const LIVENESS_WIRE_MODE: u8 = 0xa6; // E pub enum WireMode { Comms(u8), From 87e3d656d1f5f53e262567b2a4c633bd2e4b5b0c Mon Sep 17 00:00:00 2001 From: striderDM <51991544+StriderDM@users.noreply.github.com> Date: Thu, 9 Sep 2021 17:19:10 +0200 Subject: [PATCH 02/77] feature: added SideChainCheckpointFeatures Added SideChainCheckpointFeatures struct. Added conversions for SideChainCheckpointFeatures. Added storage for SideChainCheckpointFeatures. cargo-fmt --- applications/tari_app_grpc/proto/types.proto | 5 + .../src/conversions/output_features.rs | 26 ++- .../src/conversions/transaction_output.rs | 16 +- .../src/conversions/unblinded_output.rs | 10 +- .../src/automation/command_parser.rs | 41 +++-- .../tari_console_wallet/src/notifier/mod.rs | 3 +- .../src/ui/components/assets_tab.rs | 63 ++++--- .../src/ui/components/events_component.rs | 40 +++-- .../src/ui/components/mod.rs | 6 +- .../src/ui/components/styles.rs | 7 +- .../src/ui/components/tokens_component.rs | 72 ++++++-- .../src/ui/components/transactions_tab.rs | 5 +- .../src/ui/state/app_state.rs | 15 +- applications/tari_dan_node/src/cmd_args.rs | 6 +- .../src/dan_layer/models/replica_info.rs | 4 +- .../dan_layer/services/bft_replica_service.rs | 34 ++-- .../src/dan_layer/storage/asset_data_store.rs | 53 +++--- .../src/dan_layer/workers/states/starting.rs | 9 +- applications/tari_explorer/views/blocks.hbs | 1 + .../comms_interface/comms_request.rs | 9 +- .../comms_interface/comms_response.rs | 6 +- .../comms_interface/local_interface.rs | 19 +- base_layer/core/src/proto/transaction.proto | 4 + base_layer/core/src/proto/transaction.rs | 40 ++++- .../core/src/transactions/coinbase_builder.rs | 2 +- .../core/src/transactions/transaction/mod.rs | 165 +++++++++++------- .../transactions/transaction_protocol/mod.rs | 2 +- .../proto/transaction_sender.rs | 10 +- .../transaction_protocol/recipient.rs | 2 +- .../transaction_protocol/sender.rs | 8 +- .../transaction_protocol/single_receiver.rs | 2 +- .../transaction_protocol/tx_id.rs | 21 +-- .../2021-07-07-121212_add_unique_id/up.sql | 1 + .../infrastructure/asset_manager_service.rs | 46 +++-- .../src/assets/infrastructure/initializer.rs | 31 +--- base_layer/wallet/src/error.rs | 5 +- base_layer/wallet/src/lib.rs | 3 +- base_layer/wallet/src/operation_id.rs | 21 +-- .../recovery/standard_outputs_recoverer.rs | 14 +- .../src/output_manager_service/storage/mod.rs | 2 +- .../storage/sqlite_db/mod.rs | 8 + .../storage/sqlite_db/new_output_sql.rs | 17 +- .../storage/sqlite_db/output_sql.rs | 1 + .../tasks/txo_validation_task.rs | 5 +- base_layer/wallet/src/schema.rs | 1 + .../src/tokens/infrastructure/initializer.rs | 32 +--- .../wallet/src/tokens/infrastructure/mod.rs | 4 +- .../infrastructure/token_manager_service.rs | 16 +- base_layer/wallet/src/tokens/mod.rs | 6 +- .../wallet/src/transaction_service/error.rs | 14 +- .../wallet/src/transaction_service/handle.rs | 138 +++++++++++---- .../transaction_service/storage/database.rs | 21 +-- .../src/transaction_service/storage/models.rs | 6 +- .../tasks/send_finalized_transaction.rs | 16 +- .../tasks/send_transaction_cancelled.rs | 2 +- base_layer/wallet_ffi/src/callback_handler.rs | 39 +++-- base_layer/wallet_ffi/src/lib.rs | 10 +- common/src/configuration/bootstrap.rs | 6 +- common/src/configuration/global.rs | 2 +- 59 files changed, 724 insertions(+), 449 deletions(-) diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index accc648c82..7f61b31399 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -209,6 +209,7 @@ message OutputFeatures { AssetOutputFeatures asset = 16; MintNonFungibleFeatures mint_non_fungible = 17; + SideChainCheckpointFeatures sidechain_checkpoint = 18; } message AssetOutputFeatures { @@ -220,6 +221,10 @@ message MintNonFungibleFeatures { bytes asset_owner_commitment = 2; } +message SideChainCheckpointFeatures { + bytes merkle_root = 1; +} + // The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, // cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should diff --git a/applications/tari_app_grpc/src/conversions/output_features.rs b/applications/tari_app_grpc/src/conversions/output_features.rs index 66fc3f3d9a..f0bd39e7e4 100644 --- a/applications/tari_app_grpc/src/conversions/output_features.rs +++ b/applications/tari_app_grpc/src/conversions/output_features.rs @@ -23,7 +23,13 @@ use crate::tari_rpc as grpc; use std::convert::{TryFrom, TryInto}; use tari_common_types::types::{Commitment, PublicKey}; -use tari_core::transactions::transaction::{AssetOutputFeatures, MintNonFungibleFeatures, OutputFeatures, OutputFlags}; +use tari_core::transactions::transaction::{ + AssetOutputFeatures, + MintNonFungibleFeatures, + OutputFeatures, + OutputFlags, + SideChainCheckpointFeatures, +}; use tari_crypto::tari_utilities::ByteArray; impl TryFrom for OutputFeatures { @@ -37,6 +43,7 @@ impl TryFrom for OutputFeatures { metadata: features.metadata, asset: features.asset.map(|a| a.try_into()).transpose()?, mint_non_fungible: features.mint_non_fungible.map(|m| m.try_into()).transpose()?, + sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), }) } } @@ -49,6 +56,7 @@ impl From for grpc::OutputFeatures { metadata: features.metadata, asset: features.asset.map(|a| a.into()), mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), + sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), } } } @@ -96,3 +104,19 @@ impl From for grpc::MintNonFungibleFeatures { } } } + +impl From for grpc::SideChainCheckpointFeatures { + fn from(value: SideChainCheckpointFeatures) -> Self { + Self { + merkle_root: value.merkle_root.as_bytes().to_vec(), + } + } +} + +impl From for SideChainCheckpointFeatures { + fn from(value: grpc::SideChainCheckpointFeatures) -> Self { + Self { + merkle_root: value.merkle_root.as_bytes().to_vec(), + } + } +} diff --git a/applications/tari_app_grpc/src/conversions/transaction_output.rs b/applications/tari_app_grpc/src/conversions/transaction_output.rs index b5946251df..a0689e138f 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_output.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_output.rs @@ -54,13 +54,19 @@ impl TryFrom for TransactionOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; - let unique_id = if output.unique_id.is_empty() { None} else { Some(output.unique_id.clone())}; + let unique_id = if output.unique_id.is_empty() { + None + } else { + Some(output.unique_id.clone()) + }; let parent_public_key = if output.parent_public_key.is_empty() { None } else { - Some(PublicKey::from_bytes(output.parent_public_key.as_bytes()) - .map_err(|err| format!("parent_public_key {:?}", err))?) + Some( + PublicKey::from_bytes(output.parent_public_key.as_bytes()) + .map_err(|err| format!("parent_public_key {:?}", err))?, + ) }; Ok(Self { @@ -71,7 +77,7 @@ impl TryFrom for TransactionOutput { script, sender_offset_public_key, metadata_signature, - parent_public_key + parent_public_key, }) } } @@ -92,7 +98,7 @@ impl From for grpc::TransactionOutput { signature_v: Vec::from(output.metadata_signature.v().as_bytes()), }), unique_id: output.unique_id.unwrap_or_default(), - parent_public_key: output.parent_public_key.map(|b| b.to_vec()).unwrap_or_default() + parent_public_key: output.parent_public_key.map(|b| b.to_vec()).unwrap_or_default(), } } } diff --git a/applications/tari_app_grpc/src/conversions/unblinded_output.rs b/applications/tari_app_grpc/src/conversions/unblinded_output.rs index bdb2047b47..8a62661c1a 100644 --- a/applications/tari_app_grpc/src/conversions/unblinded_output.rs +++ b/applications/tari_app_grpc/src/conversions/unblinded_output.rs @@ -46,7 +46,7 @@ impl From for grpc::UnblindedOutput { signature_u: Vec::from(output.metadata_signature.u().as_bytes()), signature_v: Vec::from(output.metadata_signature.v().as_bytes()), }), - unique_id: output.unique_id.unwrap_or_default() + unique_id: output.unique_id.unwrap_or_default(), } } } @@ -80,7 +80,11 @@ impl TryFrom for UnblindedOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; - let unique_id = if output.unique_id.is_empty() { None } else {Some(output.unique_id.clone())}; + let unique_id = if output.unique_id.is_empty() { + None + } else { + Some(output.unique_id.clone()) + }; Ok(Self { value: MicroTari::from(output.value), spending_key, @@ -93,7 +97,7 @@ impl TryFrom for UnblindedOutput { unique_id, // TODO: Remove this none - parent_public_key: None + parent_public_key: None, }) } } diff --git a/applications/tari_console_wallet/src/automation/command_parser.rs b/applications/tari_console_wallet/src/automation/command_parser.rs index c0b7421067..b35cf31c0e 100644 --- a/applications/tari_console_wallet/src/automation/command_parser.rs +++ b/applications/tari_console_wallet/src/automation/command_parser.rs @@ -32,9 +32,9 @@ use std::{ use tari_app_utilities::utilities::parse_emoji_id_or_public_key; use tari_comms::multiaddr::Multiaddr; +use std::iter::Peekable; use tari_common_types::types::PublicKey; use tari_core::transactions::tari_amount::MicroTari; -use std::iter::Peekable; #[derive(Debug)] pub struct ParsedCommand { @@ -60,7 +60,7 @@ impl Display for ParsedCommand { SetCustomBaseNode => "set-custom-base-node", ClearCustomBaseNode => "clear-custom-base-node", RegisterAsset => "register-asset", - MintTokens=> "mint-tokens" + MintTokens => "mint-tokens", }; let args = self @@ -130,7 +130,7 @@ pub fn parse_command(command: &str) -> Result { ClearCustomBaseNode => Vec::new(), RegisterAsset => parser_builder(args).text().build()?, // mint-tokens pub_key nft_id1 nft_id2 - MintTokens => parser_builder(args).pub_key().text_array().build()? + MintTokens => parser_builder(args).pub_key().text_array().build()?, }; Ok(ParsedCommand { command, args }) @@ -138,25 +138,31 @@ pub fn parse_command(command: &str) -> Result { struct ArgParser<'a> { args: Peekable>, - result: Vec> + result: Vec>, } impl<'a> ArgParser<'a> { fn new(args: SplitWhitespace<'a>) -> Self { - Self{ - args: args.peekable(), result: vec![] + Self { + args: args.peekable(), + result: vec![], } } + fn text(mut self) -> Self { - let text_result = self.args.next().map(|t| ParsedArgument::Text(t.to_string())) - .ok_or_else(|| ParseError::Empty("text".to_string())); + let text_result = self + .args + .next() + .map(|t| ParsedArgument::Text(t.to_string())) + .ok_or_else(|| ParseError::Empty("text".to_string())); self.result.push(text_result); - self + self } + fn text_array(self) -> Self { let mut me = self; while me.args.peek().is_some() { - me = me.text(); + me = me.text(); } me @@ -164,15 +170,16 @@ impl<'a> ArgParser<'a> { fn pub_key(mut self) -> Self { // public key/emoji id - let pubkey = self.args + let pubkey = self + .args .next() .ok_or_else(|| ParseError::Empty("public key or emoji id".to_string())); - let result = pubkey.and_then(|pb| { - match parse_emoji_id_or_public_key(pb).ok_or(ParseError::PublicKey) { + let result = pubkey.and_then( + |pb| match parse_emoji_id_or_public_key(pb).ok_or(ParseError::PublicKey) { Ok(pk) => Ok(ParsedArgument::PublicKey(pk)), - Err(err) => Err(err) - } - }); + Err(err) => Err(err), + }, + ); self.result.push(result); self } @@ -182,7 +189,7 @@ impl<'a> ArgParser<'a> { for r in self.result { result.push(r?); } - Ok(result) + Ok(result) } } diff --git a/applications/tari_console_wallet/src/notifier/mod.rs b/applications/tari_console_wallet/src/notifier/mod.rs index bd6e3b0436..9d69f3ec30 100644 --- a/applications/tari_console_wallet/src/notifier/mod.rs +++ b/applications/tari_console_wallet/src/notifier/mod.rs @@ -26,7 +26,7 @@ use std::{ path::PathBuf, process::{Command, Output}, }; -use tari_core::tari_utilities::hex::Hex; +use tari_core::{tari_utilities::hex::Hex, transactions::transaction_protocol::TxId}; use tari_wallet::{ transaction_service::storage::models::{ CompletedTransaction, @@ -37,7 +37,6 @@ use tari_wallet::{ WalletSqlite, }; use tokio::runtime::Handle; -use tari_core::transactions::transaction_protocol::TxId; pub const LOG_TARGET: &str = "wallet::notifier"; const RECEIVED: &str = "received"; diff --git a/applications/tari_console_wallet/src/ui/components/assets_tab.rs b/applications/tari_console_wallet/src/ui/components/assets_tab.rs index f8a1edaa7e..9719655543 100644 --- a/applications/tari_console_wallet/src/ui/components/assets_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/assets_tab.rs @@ -20,42 +20,66 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::ui::components::{Component, styles}; -use tui::backend::Backend; -use tui::Frame; -use tui::layout::{Rect, Constraint}; -use crate::ui::state::AppState; -use tui::widgets::{Table, Block, Row, TableState, Borders}; +use crate::ui::{ + components::{styles, Component}, + state::AppState, +}; use tari_crypto::tari_utilities::hex::Hex; +use tui::{ + backend::Backend, + layout::{Constraint, Rect}, + widgets::{Block, Borders, Row, Table, TableState}, + Frame, +}; pub struct AssetsTab { table_state: TableState, } impl AssetsTab { - pub fn new() -> Self { - Self{ table_state: TableState::default(),} + Self { + table_state: TableState::default(), + } } } -impl Component for AssetsTab { +impl Component for AssetsTab { fn draw(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) { - let assets = app_state.get_owned_assets(); - - let assets :Vec<_>= assets.iter().map(|r| (r.name().to_string(), r.registration_output_status().to_string(), r.public_key().to_hex(), r.owner_commitment().to_hex())).collect(); - let rows : Vec<_>= assets.iter().map(|v| Row::new(vec![v.0.as_str(), v.1.as_str(), v.2.as_str(), v.3.as_str()])).collect(); + let assets: Vec<_> = assets + .iter() + .map(|r| { + ( + r.name().to_string(), + r.registration_output_status().to_string(), + r.public_key().to_hex(), + r.owner_commitment().to_hex(), + ) + }) + .collect(); + let rows: Vec<_> = assets + .iter() + .map(|v| Row::new(vec![v.0.as_str(), v.1.as_str(), v.2.as_str(), v.3.as_str()])) + .collect(); let table = Table::new(rows) - .header(Row::new(vec!["Name", "Status" , "Pub Key", "Owner"]).style(styles::header_row())).block(Block::default().title("Assets").borders(Borders::ALL)).widths(&[Constraint::Length(30), Constraint::Length(20), Constraint::Length(64), Constraint::Length(64)]).highlight_style(styles::highlight()).highlight_symbol(">>"); + .header(Row::new(vec!["Name", "Status", "Pub Key", "Owner"]).style(styles::header_row())) + .block(Block::default().title("Assets").borders(Borders::ALL)) + .widths(&[ + Constraint::Length(30), + Constraint::Length(20), + Constraint::Length(64), + Constraint::Length(64), + ]) + .highlight_style(styles::highlight()) + .highlight_symbol(">>"); f.render_stateful_widget(table, area, &mut self.table_state) } fn on_up(&mut self, _app_state: &mut AppState) { - let index =self.table_state.selected().unwrap_or_default(); - if index == 0 { + let index = self.table_state.selected().unwrap_or_default(); + if index == 0 { self.table_state.select(None); } else { self.table_state.select(Some(index - 1)); @@ -63,13 +87,12 @@ impl Component for AssetsTab { } fn on_down(&mut self, app_state: &mut AppState) { - let index =self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let index = self.table_state.selected().map(|s| s + 1).unwrap_or_default(); let assets = app_state.get_owned_assets(); - if index > assets.len().saturating_sub(1) { + if index > assets.len().saturating_sub(1) { self.table_state.select(None); } else { self.table_state.select(Some(index)); } } } - diff --git a/applications/tari_console_wallet/src/ui/components/events_component.rs b/applications/tari_console_wallet/src/ui/components/events_component.rs index fc0ffc3962..16f4ffb48d 100644 --- a/applications/tari_console_wallet/src/ui/components/events_component.rs +++ b/applications/tari_console_wallet/src/ui/components/events_component.rs @@ -1,35 +1,45 @@ -use tui::backend::Backend; -use crate::ui::components::{Component, styles}; -use tui::Frame; -use tui::layout::{Rect, Constraint}; -use crate::ui::state::AppState; -use tui::widgets::{Row, Table, Block, Borders, TableState}; +use crate::ui::{ + components::{styles, Component}, + state::AppState, +}; +use tui::{ + backend::Backend, + layout::{Constraint, Rect}, + widgets::{Block, Borders, Row, Table, TableState}, + Frame, +}; pub struct EventsComponent { table_state: TableState, } - impl EventsComponent { - pub fn new() -> Self{ + pub fn new() -> Self { Self { table_state: TableState::default(), } } } -impl Component for EventsComponent { +impl Component for EventsComponent { fn draw(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) { let events = app_state.get_all_events(); - let rows :Vec<_>= events.iter().map(|e| Row::new(vec![e.event_type.as_str(), e.desc.as_str()])).collect(); + let rows: Vec<_> = events + .iter() + .map(|e| Row::new(vec![e.event_type.as_str(), e.desc.as_str()])) + .collect(); let table = Table::new(rows) - .header(Row::new(vec!["Type", "Desc"]).style(styles::header_row())).block(Block::default().title("Events").borders(Borders::ALL)).widths(&[Constraint::Length(20), Constraint::Length(120)]).highlight_style(styles::highlight()).highlight_symbol(">>"); + .header(Row::new(vec!["Type", "Desc"]).style(styles::header_row())) + .block(Block::default().title("Events").borders(Borders::ALL)) + .widths(&[Constraint::Length(20), Constraint::Length(120)]) + .highlight_style(styles::highlight()) + .highlight_symbol(">>"); f.render_stateful_widget(table, area, &mut self.table_state) } fn on_up(&mut self, _app_state: &mut AppState) { - let index =self.table_state.selected().unwrap_or_default(); - if index == 0 { + let index = self.table_state.selected().unwrap_or_default(); + if index == 0 { self.table_state.select(None); } else { self.table_state.select(Some(index - 1)); @@ -37,9 +47,9 @@ impl Component for EventsComponent { } fn on_down(&mut self, app_state: &mut AppState) { - let index =self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let index = self.table_state.selected().map(|s| s + 1).unwrap_or_default(); let events = app_state.get_all_events(); - if index > events.len() - 1 { + if index > events.len() - 1 { self.table_state.select(None); } else { self.table_state.select(Some(index)); diff --git a/applications/tari_console_wallet/src/ui/components/mod.rs b/applications/tari_console_wallet/src/ui/components/mod.rs index 50123e7700..80b299bdc5 100644 --- a/applications/tari_console_wallet/src/ui/components/mod.rs +++ b/applications/tari_console_wallet/src/ui/components/mod.rs @@ -20,6 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod assets_tab; pub mod balance; pub mod base_node; mod component; @@ -29,11 +30,10 @@ pub mod network_tab; pub mod notification_tab; pub mod receive_tab; pub mod send_tab; +mod styles; pub mod tabs_container; -pub mod transactions_tab; pub mod tokens_component; -pub mod assets_tab; -mod styles; +pub mod transactions_tab; pub use self::component::*; pub mod events_component; diff --git a/applications/tari_console_wallet/src/ui/components/styles.rs b/applications/tari_console_wallet/src/ui/components/styles.rs index b09c4b0880..7ae02afc76 100644 --- a/applications/tari_console_wallet/src/ui/components/styles.rs +++ b/applications/tari_console_wallet/src/ui/components/styles.rs @@ -1,10 +1,9 @@ -use tui::style::{Style, Color, Modifier}; +use tui::style::{Color, Modifier, Style}; pub fn header_row() -> Style { - Style::default().fg(Color::Magenta) + Style::default().fg(Color::Magenta) } pub fn highlight() -> Style { - Style::default().add_modifier(Modifier::BOLD).fg(Color::Magenta) + Style::default().add_modifier(Modifier::BOLD).fg(Color::Magenta) } - diff --git a/applications/tari_console_wallet/src/ui/components/tokens_component.rs b/applications/tari_console_wallet/src/ui/components/tokens_component.rs index 3bd9597b67..196e3a61be 100644 --- a/applications/tari_console_wallet/src/ui/components/tokens_component.rs +++ b/applications/tari_console_wallet/src/ui/components/tokens_component.rs @@ -20,41 +20,76 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ui::components::{Component, styles}; -use tui::backend::Backend; -use tui::Frame; -use tui::layout::{Rect, Constraint}; -use crate::ui::state::AppState; -use tui::widgets::{Table, Block, Row, TableState, Borders}; +use crate::ui::{ + components::{styles, Component}, + state::AppState, +}; use tari_crypto::tari_utilities::hex::Hex; +use tui::{ + backend::Backend, + layout::{Constraint, Rect}, + widgets::{Block, Borders, Row, Table, TableState}, + Frame, +}; pub struct TokensComponent { table_state: TableState, } impl TokensComponent { - pub fn new() -> Self { - Self{ table_state: TableState::default(),} + Self { + table_state: TableState::default(), + } } } -impl Component for TokensComponent { +impl Component for TokensComponent { fn draw(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) { - let tokens = app_state.get_owned_tokens(); - - let tokens :Vec<_>= tokens.iter().map(|r| (r.name().to_string(), r.output_status().to_string(), r.asset_public_key().to_hex(), Vec::from(r.unique_id()).to_hex(), r.owner_commitment().to_hex())).collect(); - let rows : Vec<_>= tokens.iter().map(|v| Row::new(vec![v.0.as_str(), v.1.as_str(), v.2.as_str(), v.3.as_str(), v.4.as_str()])).collect(); + let tokens: Vec<_> = tokens + .iter() + .map(|r| { + ( + r.name().to_string(), + r.output_status().to_string(), + r.asset_public_key().to_hex(), + Vec::from(r.unique_id()).to_hex(), + r.owner_commitment().to_hex(), + ) + }) + .collect(); + let rows: Vec<_> = tokens + .iter() + .map(|v| { + Row::new(vec![ + v.0.as_str(), + v.1.as_str(), + v.2.as_str(), + v.3.as_str(), + v.4.as_str(), + ]) + }) + .collect(); let table = Table::new(rows) - .header(Row::new(vec!["Name", "Status" , "Asset Pub Key", "Unique ID", "Owner"]).style(styles::header_row())).block(Block::default().title("Tokens").borders(Borders::ALL)).widths(&[Constraint::Length(30), Constraint::Length(20), Constraint::Length(32), Constraint::Length(32), Constraint::Length(64)]).highlight_style(styles::highlight()).highlight_symbol(">>"); + .header(Row::new(vec!["Name", "Status", "Asset Pub Key", "Unique ID", "Owner"]).style(styles::header_row())) + .block(Block::default().title("Tokens").borders(Borders::ALL)) + .widths(&[ + Constraint::Length(30), + Constraint::Length(20), + Constraint::Length(32), + Constraint::Length(32), + Constraint::Length(64), + ]) + .highlight_style(styles::highlight()) + .highlight_symbol(">>"); f.render_stateful_widget(table, area, &mut self.table_state) } fn on_up(&mut self, _app_state: &mut AppState) { - let index =self.table_state.selected().unwrap_or_default(); - if index == 0 { + let index = self.table_state.selected().unwrap_or_default(); + if index == 0 { self.table_state.select(None); } else { self.table_state.select(Some(index - 1)); @@ -62,13 +97,12 @@ impl Component for TokensComponent { } fn on_down(&mut self, app_state: &mut AppState) { - let index =self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let index = self.table_state.selected().map(|s| s + 1).unwrap_or_default(); let tokens = app_state.get_owned_tokens(); - if index > tokens.len().saturating_sub(1) { + if index > tokens.len().saturating_sub(1) { self.table_state.select(None); } else { self.table_state.select(Some(index)); } } } - diff --git a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs index efcc084895..8326ebe865 100644 --- a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::ui::{ - components::{balance::Balance, Component}, + components::{balance::Balance, styles, Component}, state::AppState, widgets::{draw_dialog, MultiColumnList, WindowedListState}, MAX_WIDTH, @@ -22,7 +22,6 @@ use tui::{ widgets::{Block, Borders, ListItem, Paragraph, Wrap}, Frame, }; -use crate::ui::components::styles; pub struct TransactionsTab { balance: Balance, @@ -536,7 +535,7 @@ impl Component for TransactionsTab { 'r' => { // TODO: use this result let _res = Handle::current().block_on(app_state.rebroadcast_all()); - } + }, 'a' => app_state.toggle_abandoned_coinbase_filter(), '\n' => match self.selected_tx_list { SelectedTransactionList::None => {}, diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index ea84d8ef24..56ad5aff98 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -74,10 +74,9 @@ use crate::{ utils::db::{CUSTOM_BASE_NODE_ADDRESS_KEY, CUSTOM_BASE_NODE_PUBLIC_KEY_KEY}, wallet_modes::PeerConfig, }; -use tari_wallet::assets::Asset; -use tari_wallet::tokens::Token; -use tari_core::transactions::transaction_protocol::TxId; use std::collections::VecDeque; +use tari_core::transactions::transaction_protocol::TxId; +use tari_wallet::{assets::Asset, tokens::Token}; const LOG_TARGET: &str = "wallet::console_wallet::app_state"; @@ -166,6 +165,7 @@ impl AppState { } Ok(()) } + pub async fn update_cache(&mut self) { let update = match self.cache_update_cooldown { Some(last_update) => last_update.elapsed() > self.config.cache_update_cooldown, @@ -302,7 +302,6 @@ impl AppState { let mut tx_service = inner.wallet.transaction_service.clone(); tx_service.restart_broadcast_protocols().await?; Ok(()) - } pub fn get_identity(&self) -> &MyIdentity { @@ -501,7 +500,7 @@ impl AppStateInner { } } - pub fn add_event(&mut self, event : EventListItem) { + pub fn add_event(&mut self, event: EventListItem) { if self.data.all_events.len() > 30 { self.data.all_events.pop_back(); } @@ -690,7 +689,6 @@ impl AppStateInner { Ok(()) } - pub async fn refresh_assets_state(&mut self) -> Result<(), UiError> { let asset_utxos = self.wallet.asset_manager.list_owned_assets().await?; self.data.owned_assets = asset_utxos; @@ -924,11 +922,10 @@ struct AppStateData { new_notification_count: u32, } - #[derive(Clone)] -pub struct EventListItem{ +pub struct EventListItem { pub event_type: String, - pub desc: String + pub desc: String, } impl AppStateData { diff --git a/applications/tari_dan_node/src/cmd_args.rs b/applications/tari_dan_node/src/cmd_args.rs index da996ccbee..fcdae7c301 100644 --- a/applications/tari_dan_node/src/cmd_args.rs +++ b/applications/tari_dan_node/src/cmd_args.rs @@ -28,9 +28,7 @@ pub fn get_operation_mode() -> OperationMode { } pub enum OperationMode { - Run + Run, } -pub struct DanNodeConfig { - -} +pub struct DanNodeConfig {} diff --git a/applications/tari_dan_node/src/dan_layer/models/replica_info.rs b/applications/tari_dan_node/src/dan_layer/models/replica_info.rs index e9aa54aa5c..3d09e1fc85 100644 --- a/applications/tari_dan_node/src/dan_layer/models/replica_info.rs +++ b/applications/tari_dan_node/src/dan_layer/models/replica_info.rs @@ -20,6 +20,4 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub struct ReplicaInfo { - -} +pub struct ReplicaInfo {} diff --git a/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs b/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs index 0b2d27d592..7d69499b77 100644 --- a/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs +++ b/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs @@ -20,19 +20,18 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::dan_layer::models::{ViewId, View}; -use tari_comms::NodeIdentity; -use tari_comms::peer_manager::NodeId; +use crate::dan_layer::models::{View, ViewId}; +use tari_comms::{peer_manager::NodeId, NodeIdentity}; pub trait BftReplicaService { - fn current_view(&self ) -> View; + fn current_view(&self) -> View; } pub struct ConcreteBftReplicaService { current_view: ViewId, node_identity: NodeIdentity, committee: Vec, - position_in_committee: usize + position_in_committee: usize, } impl ConcreteBftReplicaService { @@ -43,19 +42,24 @@ impl ConcreteBftReplicaService { } committee.sort(); - let position_in_committee = committee.iter().position(|n| n == node_identity.node_id()).expect("NodeID should always be present since we add it"); - Self { current_view: ViewId(0), node_identity, committee, position_in_committee} + let position_in_committee = committee + .iter() + .position(|n| n == node_identity.node_id()) + .expect("NodeID should always be present since we add it"); + Self { + current_view: ViewId(0), + node_identity, + committee, + position_in_committee, + } } - - } -impl BftReplicaService for ConcreteBftReplicaService{ +impl BftReplicaService for ConcreteBftReplicaService { fn current_view(&self) -> View { - View { - view_id: self.current_view, - is_leader: self.current_view.current_leader(self.committee.len()) == self.position_in_committee - } + View { + view_id: self.current_view, + is_leader: self.current_view.current_leader(self.committee.len()) == self.position_in_committee, + } } } - diff --git a/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs b/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs index 606d9bae7c..18aff9b2c8 100644 --- a/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs +++ b/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs @@ -20,33 +20,33 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::dan_layer::models::TokenId; -use patricia_tree::PatriciaMap; -use patricia_tree::node::{NodeEncoder, NodeDecoder, Node}; -use bytecodec::null::{NullEncoder, NullDecoder}; -use bytecodec::Encode; -use bytecodec::io::{IoEncodeExt, IoDecodeExt}; -use std::path::PathBuf; -use crate::digital_assets_error::DigitalAssetError; -use std::fs::File; -use bytecodec::bytes::{BytesEncoder, BytesDecoder, CopyableBytesDecoder}; +use crate::{dan_layer::models::TokenId, digital_assets_error::DigitalAssetError}; +use bytecodec::{ + bytes::{BytesDecoder, BytesEncoder, CopyableBytesDecoder}, + io::{IoDecodeExt, IoEncodeExt}, + json_codec::{JsonDecoder, JsonEncoder}, + null::{NullDecoder, NullEncoder}, + Encode, +}; +use patricia_tree::{ + node::{Node, NodeDecoder, NodeEncoder}, + PatriciaMap, +}; use serde_json::Value; -use bytecodec::json_codec::{JsonDecoder, JsonEncoder}; +use std::{fs::File, path::PathBuf}; pub trait AssetDataStore { - fn replace_metadata(&mut self, token_id:&TokenId, metadata: Vec) -> Result<(), DigitalAssetError>; + fn replace_metadata(&mut self, token_id: &TokenId, metadata: Vec) -> Result<(), DigitalAssetError>; } pub struct FileAssetDataStore { - token_metadata: PatriciaMap, - // None if dirty and must be regenerated - merkle_root: Option>, - output_file: PathBuf - + token_metadata: PatriciaMap, + // None if dirty and must be regenerated + merkle_root: Option>, + output_file: PathBuf, } impl FileAssetDataStore { - pub fn load_or_create(output_file: PathBuf) -> Self { dbg!(&output_file); let token_metadata = match File::open(output_file.as_path()) { @@ -56,13 +56,13 @@ impl FileAssetDataStore { let set = PatriciaMap::from(node); set }, - Err(_) => PatriciaMap::new() + Err(_) => PatriciaMap::new(), }; Self { token_metadata, merkle_root: None, - output_file + output_file, } } @@ -87,23 +87,22 @@ impl AssetDataStore for FileAssetDataStore { } #[cfg(test)] -mod test{ +mod test { use super::*; - use std::path::PathBuf; - use std::env::consts::OS; - use tari_test_utils::paths::{temp_tari_path, create_temporary_data_path}; use crate::dan_layer::models::TokenId; + use std::{env::consts::OS, path::PathBuf}; + use tari_test_utils::paths::{create_temporary_data_path, temp_tari_path}; #[test] fn test_create() { let temp_path = create_temporary_data_path().join("file-asset-data-store"); { let mut store = FileAssetDataStore::load_or_create(temp_path.clone()); - store.replace_metadata(&TokenId(vec![11u8]), Vec::from("[1,2,3]".as_bytes())).unwrap(); + store + .replace_metadata(&TokenId(vec![11u8]), Vec::from("[1,2,3]".as_bytes())) + .unwrap(); } let store2 = FileAssetDataStore::load_or_create(temp_path); dbg!(store2.token_metadata); - } - } diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs b/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs index 280f2ab1ce..e4676457be 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs +++ b/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs @@ -20,15 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::digital_assets_error::DigitalAssetError; -use crate::dan_layer::workers::states::ConsensusWorkerStateEvent; +use crate::{dan_layer::workers::states::ConsensusWorkerStateEvent, digital_assets_error::DigitalAssetError}; -pub struct Starting { - -} +pub struct Starting {} impl Starting { - pub async fn next_event(&self ) -> Result { + pub async fn next_event(&self) -> Result { Ok(ConsensusWorkerStateEvent::Initialized) } } diff --git a/applications/tari_explorer/views/blocks.hbs b/applications/tari_explorer/views/blocks.hbs index 4007f0259a..8b0981cd69 100644 --- a/applications/tari_explorer/views/blocks.hbs +++ b/applications/tari_explorer/views/blocks.hbs @@ -57,6 +57,7 @@ {{hex features.metadata}} {{ json features.asset }} {{ json features.mint_non_fungible }} + {{ json features.sidechain_features }} {{ hex unique_id}} {{ hex parent_public_key }} diff --git a/base_layer/core/src/base_node/comms_interface/comms_request.rs b/base_layer/core/src/base_node/comms_interface/comms_request.rs index dfef55328f..bf29dbbbe3 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_request.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_request.rs @@ -52,7 +52,10 @@ pub enum NodeCommsRequest { GetNewBlockTemplate(GetNewBlockTemplateRequest), GetNewBlock(NewBlockTemplate), FetchKernelByExcessSig(Signature), - FetchTokens { asset_public_key: Vec, unique_ids: Vec>} + FetchTokens { + asset_public_key: Vec, + unique_ids: Vec>, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -85,7 +88,9 @@ impl Display for NodeCommsRequest { s.get_public_nonce().to_hex(), s.get_signature().to_hex() ), - FetchTokens { .. } => { write!(f, "FetchTokens")} + FetchTokens { .. } => { + write!(f, "FetchTokens") + }, } } } diff --git a/base_layer/core/src/base_node/comms_interface/comms_response.rs b/base_layer/core/src/base_node/comms_interface/comms_response.rs index 1846997f07..d8d7c96b0d 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_response.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_response.rs @@ -49,7 +49,9 @@ pub enum NodeCommsResponse { TargetDifficulty(Difficulty), FetchHeadersAfterResponse(Vec), MmrNodes(Vec, Vec), - FetchTokensResponse{ outputs: Vec } + FetchTokensResponse { + outputs: Vec, + }, } impl Display for NodeCommsResponse { @@ -77,7 +79,7 @@ impl Display for NodeCommsResponse { TargetDifficulty(_) => write!(f, "TargetDifficulty"), FetchHeadersAfterResponse(_) => write!(f, "FetchHeadersAfterResponse"), MmrNodes(_, _) => write!(f, "MmrNodes"), - FetchTokensResponse { .. } => write!(f, "FetchTokensResponse") + FetchTokensResponse { .. } => write!(f, "FetchTokensResponse"), } } } diff --git a/base_layer/core/src/base_node/comms_interface/local_interface.rs b/base_layer/core/src/base_node/comms_interface/local_interface.rs index 2c55790fe4..098c246d2f 100644 --- a/base_layer/core/src/base_node/comms_interface/local_interface.rs +++ b/base_layer/core/src/base_node/comms_interface/local_interface.rs @@ -105,10 +105,21 @@ impl LocalNodeCommsInterface { } } - pub async fn get_tokens(&mut self, asset_public_key: Vec, unique_ids: Vec>) -> Result, CommsInterfaceError> { - match self.request_sender.call(NodeCommsRequest::FetchTokens{ asset_public_key, unique_ids}).await?? { - NodeCommsResponse::FetchTokensResponse{outputs} => Ok(outputs) , - _ => Err(CommsInterfaceError::UnexpectedApiResponse) + pub async fn get_tokens( + &mut self, + asset_public_key: Vec, + unique_ids: Vec>, + ) -> Result, CommsInterfaceError> { + match self + .request_sender + .call(NodeCommsRequest::FetchTokens { + asset_public_key, + unique_ids, + }) + .await?? + { + NodeCommsResponse::FetchTokensResponse { outputs } => Ok(outputs), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), } } diff --git a/base_layer/core/src/proto/transaction.proto b/base_layer/core/src/proto/transaction.proto index aa31f92cc1..0a14d6b557 100644 --- a/base_layer/core/src/proto/transaction.proto +++ b/base_layer/core/src/proto/transaction.proto @@ -78,6 +78,7 @@ message OutputFeatures { AssetOutputFeatures asset = 16; MintNonFungibleFeatures mint_non_fungible = 17; + SideChainCheckpointFeatures sidechain_checkpoint = 18; } message AssetOutputFeatures { @@ -89,6 +90,9 @@ message MintNonFungibleFeatures { Commitment asset_owner_commitment = 2; } +message SideChainCheckpointFeatures { + bytes merkle_root = 1; +} // The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, // cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should // be sorted by their Blake2b-256bit digest hash diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index ba26443dc0..40812fe3bd 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -34,6 +34,7 @@ use crate::{ MintNonFungibleFeatures, OutputFeatures, OutputFlags, + SideChainCheckpointFeatures, Transaction, TransactionInput, TransactionKernel, @@ -176,9 +177,10 @@ impl TryFrom for TransactionOutput { }; let parent_public_key = if output.parent_public_key.is_empty() { - None } - else { - Some(PublicKey::from_bytes(output.parent_public_key.as_bytes()).map_err(| err | format!("{:?}", err))?) }; + None + } else { + Some(PublicKey::from_bytes(output.parent_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?) + }; Ok(Self { features, @@ -188,7 +190,7 @@ impl TryFrom for TransactionOutput { sender_offset_public_key, metadata_signature, unique_id, - parent_public_key + parent_public_key, }) } } @@ -203,7 +205,10 @@ impl From for proto::types::TransactionOutput { sender_offset_public_key: output.sender_offset_public_key.as_bytes().to_vec(), metadata_signature: Some(output.metadata_signature.into()), unique_id: output.unique_id.unwrap_or_default(), - parent_public_key: output.parent_public_key.map(|pp| pp.as_bytes().to_vec()).unwrap_or_default() + parent_public_key: output + .parent_public_key + .map(|pp| pp.as_bytes().to_vec()) + .unwrap_or_default(), } } } @@ -227,6 +232,10 @@ impl TryFrom for OutputFeatures { Some(m) => Some(m.try_into()?), None => None, }, + sidechain_checkpoint: match features.sidechain_checkpoint { + Some(a) => Some(a.into()), + None => None, + }, }) } } @@ -239,6 +248,7 @@ impl From for proto::types::OutputFeatures { metadata: features.metadata, asset: features.asset.map(|a| a.into()), mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), + sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), } } } @@ -270,8 +280,9 @@ impl TryFrom for MintNonFungibleFeatures let asset_owner_commitment = value .asset_owner_commitment - - .map(|c| Commitment::from_bytes(&c.data)) .ok_or_else(|| "asset_owner_commitment is missing".to_string())?.map_err(|err| err.to_string())?; + .map(|c| Commitment::from_bytes(&c.data)) + .ok_or_else(|| "asset_owner_commitment is missing".to_string())? + .map_err(|err| err.to_string())?; Ok(Self { asset_public_key, asset_owner_commitment, @@ -288,6 +299,21 @@ impl From for proto::types::MintNonFungibleFeatures { } } +impl From for SideChainCheckpointFeatures { + fn from(value: proto::types::SideChainCheckpointFeatures) -> Self { + let merkle_root = value.merkle_root.as_bytes().to_vec(); + Self { merkle_root } + } +} + +impl From for proto::types::SideChainCheckpointFeatures { + fn from(value: SideChainCheckpointFeatures) -> Self { + Self { + merkle_root: value.merkle_root.as_bytes().to_vec(), + } + } +} + //---------------------------------- AggregateBody --------------------------------------------// impl TryFrom for AggregateBody { diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index 7e2612ed53..dd78f16104 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -209,7 +209,7 @@ impl CoinbaseBuilder { sender_offset_public_key, metadata_sig, None, - None + None, ); // TODO: Verify bullet proof? let output = if let Some(rewind_data) = self.rewind_data.as_ref() { diff --git a/base_layer/core/src/transactions/transaction/mod.rs b/base_layer/core/src/transactions/transaction/mod.rs index 4b2de64c5b..df1e708be5 100644 --- a/base_layer/core/src/transactions/transaction/mod.rs +++ b/base_layer/core/src/transactions/transaction/mod.rs @@ -98,7 +98,7 @@ impl KernelFeatures { #[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] pub struct AssetOutputFeatures { - pub public_key: PublicKey + pub public_key: PublicKey, } #[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] @@ -108,6 +108,11 @@ pub struct MintNonFungibleFeatures { // pub proof_of_ownership: ComSignature } +#[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] +pub struct SideChainCheckpointFeatures { + pub merkle_root: Vec, +} + /// Options for UTXO's #[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] pub struct OutputFeatures { @@ -118,7 +123,8 @@ pub struct OutputFeatures { pub maturity: u64, pub metadata: Vec, pub asset: Option, - pub mint_non_fungible: Option + pub mint_non_fungible: Option, + pub sidechain_checkpoint: Option, } impl OutputFeatures { @@ -132,7 +138,7 @@ impl OutputFeatures { OutputFeatures { flags: OutputFlags::COINBASE_OUTPUT, maturity: maturity_height, - ..Default::default() + ..Default::default() } } @@ -153,26 +159,28 @@ impl OutputFeatures { } pub fn for_asset_registration(metadata: Vec, public_key: PublicKey) -> OutputFeatures { - Self{ + Self { flags: OutputFlags::ASSET_REGISTRATION, maturity: 0, metadata, - asset: Some(AssetOutputFeatures{ - public_key - }), + asset: Some(AssetOutputFeatures { public_key }), ..Default::default() } } - pub fn for_minting(metadata: Vec, asset_public_key: PublicKey, asset_owner_commitment: Commitment)-> OutputFeatures { + pub fn for_minting( + metadata: Vec, + asset_public_key: PublicKey, + asset_owner_commitment: Commitment, + ) -> OutputFeatures { Self { flags: OutputFlags::MINT_NON_FUNGIBLE, metadata, - mint_non_fungible: Some(MintNonFungibleFeatures{ + mint_non_fungible: Some(MintNonFungibleFeatures { asset_public_key, - asset_owner_commitment + asset_owner_commitment, }), - .. Default::default() + ..Default::default() } } } @@ -184,8 +192,8 @@ impl Default for OutputFeatures { maturity: 0, metadata: vec![], asset: None, - mint_non_fungible: None - + mint_non_fungible: None, + sidechain_checkpoint: None, } } } @@ -264,25 +272,24 @@ pub enum TransactionError { #[derive(Debug, Clone)] pub struct UnblindedOutputBuilder { value: MicroTari, - spending_key: BlindingFactor, - features: OutputFeatures, - script: Option, - input_data: Option, + spending_key: BlindingFactor, + features: OutputFeatures, + script: Option, + input_data: Option, script_private_key: Option, - sender_offset_public_key: Option, - metadata_signature: Option, + sender_offset_public_key: Option, + metadata_signature: Option, metadata_signed_by_receiver: bool, metadata_signed_by_sender: bool, - unique_id: Option>, - parent_public_key: Option + unique_id: Option>, + parent_public_key: Option, } - impl UnblindedOutputBuilder { - pub fn new(value: MicroTari, spending_key: BlindingFactor) -> Self { - Self{ - value, spending_key, + Self { + value, + spending_key, features: OutputFeatures::default(), script: None, input_data: None, @@ -292,18 +299,26 @@ impl UnblindedOutputBuilder { metadata_signed_by_receiver: false, metadata_signed_by_sender: false, unique_id: None, - parent_public_key: None + parent_public_key: None, } } - pub fn sign_as_receiver(&mut self, sender_offset_public_key: PublicKey, public_nonce_commitment: PublicKey) -> Result<(), TransactionError> { + pub fn sign_as_receiver( + &mut self, + sender_offset_public_key: PublicKey, + public_nonce_commitment: PublicKey, + ) -> Result<(), TransactionError> { self.sender_offset_public_key = Some(sender_offset_public_key.clone()); - let metadata_partial = TransactionOutput::create_partial_metadata_signature(&self.value, &self.spending_key, - self.script.as_ref().ok_or_else(||TransactionError::ValidationError("script must be set".to_string()))?, - &self.features, + let metadata_partial = TransactionOutput::create_partial_metadata_signature( + &self.value, + &self.spending_key, + self.script + .as_ref() + .ok_or_else(|| TransactionError::ValidationError("script must be set".to_string()))?, + &self.features, &sender_offset_public_key, - &public_nonce_commitment + &public_nonce_commitment, )?; self.metadata_signature = Some(metadata_partial); self.metadata_signed_by_receiver = true; @@ -311,63 +326,87 @@ impl UnblindedOutputBuilder { } pub fn sign_as_sender(&mut self, sender_offset_private_key: &PrivateKey) -> Result<(), TransactionError> { - let metadata_sig = TransactionOutput::create_final_metadata_signature(&self.value,&self.spending_key, self.script.as_ref().ok_or_else(||TransactionError::ValidationError("script must be set".to_string()))?, - &self.features, - &sender_offset_private_key - )?; + let metadata_sig = TransactionOutput::create_final_metadata_signature( + &self.value, + &self.spending_key, + self.script + .as_ref() + .ok_or_else(|| TransactionError::ValidationError("script must be set".to_string()))?, + &self.features, + &sender_offset_private_key, + )?; self.metadata_signature = Some(metadata_sig); self.metadata_signed_by_sender = true; Ok(()) } - pub fn try_build(self) -> Result { + pub fn try_build(self) -> Result { if !self.metadata_signed_by_receiver { - return Err(TransactionError::ValidationError("Cannot build output because it has not been signed by the receiver".to_string())); + return Err(TransactionError::ValidationError( + "Cannot build output because it has not been signed by the receiver".to_string(), + )); } if !self.metadata_signed_by_sender { - return Err(TransactionError::ValidationError("Cannot build output because it has not been signed by the sender".to_string())); + return Err(TransactionError::ValidationError( + "Cannot build output because it has not been signed by the sender".to_string(), + )); } - let ub = UnblindedOutput{ + let ub = UnblindedOutput { value: self.value, spending_key: self.spending_key, - features:self.features, - script:self.script.ok_or_else(||TransactionError::ValidationError("script must be set".to_string()))?, - input_data: self.input_data.ok_or_else(||TransactionError::ValidationError("input_data must be set".to_string()))?, - script_private_key: self.script_private_key.ok_or_else(||TransactionError::ValidationError("script_private_key must be set".to_string()))?, - sender_offset_public_key: self.sender_offset_public_key.ok_or_else(||TransactionError::ValidationError("sender_offset_public_key must be set".to_string()))?, - metadata_signature: self.metadata_signature.ok_or_else(||TransactionError::ValidationError("metadata_signature must be set".to_string()))?, + features: self.features, + script: self + .script + .ok_or_else(|| TransactionError::ValidationError("script must be set".to_string()))?, + input_data: self + .input_data + .ok_or_else(|| TransactionError::ValidationError("input_data must be set".to_string()))?, + script_private_key: self + .script_private_key + .ok_or_else(|| TransactionError::ValidationError("script_private_key must be set".to_string()))?, + sender_offset_public_key: self + .sender_offset_public_key + .ok_or_else(|| TransactionError::ValidationError("sender_offset_public_key must be set".to_string()))?, + metadata_signature: self + .metadata_signature + .ok_or_else(|| TransactionError::ValidationError("metadata_signature must be set".to_string()))?, unique_id: self.unique_id, - parent_public_key: self.parent_public_key + parent_public_key: self.parent_public_key, }; Ok(ub) } + pub fn with_features(mut self, features: OutputFeatures) -> Self { self.features = features; self } + pub fn with_script(mut self, script: TariScript) -> Self { - self.script =Some( script); + self.script = Some(script); self } + pub fn with_input_data(mut self, input_data: ExecutionStack) -> Self { - self.input_data =Some( input_data); + self.input_data = Some(input_data); self } + pub fn with_script_private_key(mut self, script_private_key: PrivateKey) -> Self { - self.script_private_key =Some( script_private_key); + self.script_private_key = Some(script_private_key); self } - pub fn with_unique_id(mut self, unique_id: Option>)-> Self { + + pub fn with_unique_id(mut self, unique_id: Option>) -> Self { self.unique_id = unique_id; self } - pub fn with_parent_public_key(mut self, parent_public_key: Option)-> Self { + + pub fn with_parent_public_key(mut self, parent_public_key: Option) -> Self { self.parent_public_key = parent_public_key; self } } - /// An unblinded output is one where the value and spending key (blinding factor) are known. This can be used to /// build both inputs and outputs (every input comes from an output) // TODO: Try to get rid of 'Serialize' and 'Deserialize' traits here; see related comment at 'struct RawTransactionInfo' @@ -382,7 +421,7 @@ pub struct UnblindedOutput { pub sender_offset_public_key: PublicKey, pub metadata_signature: ComSignature, pub unique_id: Option>, - pub parent_public_key: Option + pub parent_public_key: Option, } impl UnblindedOutput { @@ -398,7 +437,7 @@ impl UnblindedOutput { sender_offset_public_key: PublicKey, metadata_signature: ComSignature, unique_id: Option>, - parent_public_key: Option + parent_public_key: Option, ) -> UnblindedOutput { UnblindedOutput { value, @@ -410,7 +449,7 @@ impl UnblindedOutput { sender_offset_public_key, metadata_signature, unique_id, - parent_public_key + parent_public_key, } } @@ -448,7 +487,11 @@ impl UnblindedOutput { }) } - pub fn as_transaction_output(&self, factories: &CryptoFactories, verify_proof: bool) -> Result { + pub fn as_transaction_output( + &self, + factories: &CryptoFactories, + verify_proof: bool, + ) -> Result { let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); let output = TransactionOutput { features: self.features.clone(), @@ -463,7 +506,7 @@ impl UnblindedOutput { sender_offset_public_key: self.sender_offset_public_key.clone(), metadata_signature: self.metadata_signature.clone(), unique_id: self.unique_id.clone(), - parent_public_key: self.parent_public_key.clone() + parent_public_key: self.parent_public_key.clone(), }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. if verify_proof && !output.verify_range_proof(&factories.range_proof)? { @@ -478,7 +521,7 @@ impl UnblindedOutput { &self, factories: &CryptoFactories, rewind_data: &RewindData, - verify_proof: bool + verify_proof: bool, ) -> Result { let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); @@ -501,7 +544,7 @@ impl UnblindedOutput { sender_offset_public_key: self.sender_offset_public_key.clone(), metadata_signature: self.metadata_signature.clone(), unique_id: self.unique_id.clone(), - parent_public_key: self.parent_public_key.clone() + parent_public_key: self.parent_public_key.clone(), }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. if verify_proof && !output.verify_range_proof(&factories.range_proof)? { @@ -748,7 +791,7 @@ impl TransactionOutput { sender_offset_public_key: PublicKey, metadata_signature: ComSignature, unique_id: Option>, - parent_public_key: Option + parent_public_key: Option, ) -> TransactionOutput { TransactionOutput { features, @@ -758,7 +801,7 @@ impl TransactionOutput { sender_offset_public_key, metadata_signature, unique_id, - parent_public_key + parent_public_key, } } diff --git a/base_layer/core/src/transactions/transaction_protocol/mod.rs b/base_layer/core/src/transactions/transaction_protocol/mod.rs index 462a2fdd08..01ab79e7fa 100644 --- a/base_layer/core/src/transactions/transaction_protocol/mod.rs +++ b/base_layer/core/src/transactions/transaction_protocol/mod.rs @@ -83,8 +83,8 @@ pub mod proto; pub mod recipient; pub mod sender; -pub mod single_receiver; pub mod sender_transaction_protocol_builder; +pub mod single_receiver; mod tx_id; pub use tx_id::*; diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs index 75083be7b4..e2e16adb05 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs @@ -106,7 +106,11 @@ impl TryFrom for SingleRoundSenderData { .map(TryInto::try_into) .ok_or_else(|| "Transaction output features not provided".to_string())??; - let unique_id = if data.unique_id.is_empty() { None} else {Some(data.unique_id.clone())}; + let unique_id = if data.unique_id.is_empty() { + None + } else { + Some(data.unique_id.clone()) + }; Ok(Self { tx_id: data.tx_id.into(), amount: data.amount.into(), @@ -118,7 +122,7 @@ impl TryFrom for SingleRoundSenderData { script: TariScript::from_bytes(&data.script).map_err(|err| err.to_string())?, sender_offset_public_key, public_commitment_nonce, - unique_id + unique_id, }) } } @@ -138,7 +142,7 @@ impl From for proto::SingleRoundSenderData { script: sender_data.script.as_bytes(), sender_offset_public_key: sender_data.sender_offset_public_key.to_vec(), public_commitment_nonce: sender_data.public_commitment_nonce.to_vec(), - unique_id: sender_data.unique_id.unwrap_or_default() + unique_id: sender_data.unique_id.unwrap_or_default(), } } } diff --git a/base_layer/core/src/transactions/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs index 890749f77b..edc57f842e 100644 --- a/base_layer/core/src/transactions/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -32,10 +32,10 @@ use crate::transactions::{ single_receiver::SingleReceiverTransactionProtocol, RewindData, TransactionProtocolError, + TxId, }, }; use tari_common_types::types::{MessageHash, PrivateKey, PublicKey, Signature}; -use crate::transactions::transaction_protocol::TxId; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[allow(clippy::large_enum_variant)] diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 294d846af0..523408a086 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -53,10 +53,10 @@ use crate::transactions::{ sender_transaction_protocol_builder::SenderTransactionProtocolBuilder, TransactionMetadata, TransactionProtocolError as TPE, + TxId, }, }; use tari_common_types::types::{BlindingFactor, ComSignature, PrivateKey, PublicKey, RangeProofService, Signature}; -use crate::transactions::transaction_protocol::TxId; //---------------------------------------- Local Data types ----------------------------------------------------// @@ -98,7 +98,7 @@ pub(super) struct RawTransactionInfo { pub recipient_info: RecipientInfo, pub signatures: Vec, pub message: String, - pub unique_id: Option> + pub unique_id: Option>, } #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] @@ -124,7 +124,7 @@ pub struct SingleRoundSenderData { /// The sender's portion of the public commitment nonce pub public_commitment_nonce: PublicKey, /// Unique id on the blockchain, if present - pub unique_id: Option> + pub unique_id: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -374,7 +374,7 @@ impl SenderTransactionProtocol { script: recipient_script, sender_offset_public_key: PublicKey::from_secret_key(recipient_script_offset_secret_key), public_commitment_nonce: PublicKey::from_secret_key(&private_commitment_nonce), - unique_id: info.unique_id.clone() + unique_id: info.unique_id.clone(), }) }, _ => Err(TPE::InvalidStateError), diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 609e91e66c..07c86d2e38 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -129,7 +129,7 @@ impl SingleReceiverTransactionProtocol { sender_info.sender_offset_public_key.clone(), partial_metadata_signature, None, - None + None, ); Ok(output) } diff --git a/base_layer/core/src/transactions/transaction_protocol/tx_id.rs b/base_layer/core/src/transactions/transaction_protocol/tx_id.rs index 90eb1c603c..25317a96ed 100644 --- a/base_layer/core/src/transactions/transaction_protocol/tx_id.rs +++ b/base_layer/core/src/transactions/transaction_protocol/tx_id.rs @@ -20,13 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt; -use std::fmt::Formatter; -use serde::{Serialize, Deserialize}; -use std::hash::{Hash, Hasher}; -use rand::rngs::OsRng; -use rand::RngCore; - +use rand::{rngs::OsRng, RngCore}; +use serde::{Deserialize, Serialize}; +use std::{ + fmt, + fmt::Formatter, + hash::{Hash, Hasher}, +}; #[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)] pub struct TxId(u64); @@ -53,10 +53,7 @@ impl PartialEq for TxId { } } - -impl Eq for TxId { - -} +impl Eq for TxId {} impl From for TxId { fn from(s: u64) -> Self { @@ -65,7 +62,7 @@ impl From for TxId { } impl From for u64 { - fn from(s: TxId) ->Self { + fn from(s: TxId) -> Self { s.0 } } diff --git a/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql index 2e84723709..0c42be0c8b 100644 --- a/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql +++ b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql @@ -5,5 +5,6 @@ alter table outputs add metadata blob; alter table outputs add features_asset_public_key blob; alter table outputs add features_mint_asset_public_key blob; alter table outputs add features_mint_asset_owner_commitment blob; +alter table outputs add features_sidechain_checkpoint_merkle_root blob; alter table outputs add parent_public_key blob; alter table inbound_transactions add unique_id blob; diff --git a/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs b/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs index 28e19de2d7..82a1471a26 100644 --- a/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs +++ b/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs @@ -26,18 +26,13 @@ use crate::{ AssetManager, }, error::WalletError, - output_manager_service::storage::{ - database::{OutputManagerBackend}, - }, -}; -use tari_service_framework::{ - reply_channel::{Receiver, }, + output_manager_service::{handle::OutputManagerHandle, storage::database::OutputManagerBackend}, + types::MockPersistentKeyManager, }; use futures::{pin_mut, StreamExt}; -use tari_shutdown::ShutdownSignal; use log::*; -use crate::output_manager_service::handle::OutputManagerHandle; -use crate::types::MockPersistentKeyManager; +use tari_service_framework::reply_channel::Receiver; +use tari_shutdown::ShutdownSignal; const LOG_TARGET: &str = "wallet::assets::infrastructure::asset_manager_service"; @@ -48,7 +43,7 @@ pub struct AssetManagerService { impl AssetManagerService { pub fn new(backend: T, output_manager: OutputManagerHandle) -> Self { Self { - manager: AssetManager::::new(backend, output_manager, MockPersistentKeyManager::new()), + manager: AssetManager::::new(backend, output_manager, MockPersistentKeyManager::new()), } } @@ -93,18 +88,31 @@ impl AssetManagerService { AssetManagerRequest::ListOwned { .. } => Ok(AssetManagerResponse::ListOwned { assets: self.manager.list_owned().await?, }), - AssetManagerRequest::CreateRegistrationTransaction {name} => { - let (tx_id, transaction) =self.manager.create_registration_transaction(name).await?; - Ok(AssetManagerResponse::CreateRegistrationTransaction {transaction: Box::new(transaction), tx_id}) - } + AssetManagerRequest::CreateRegistrationTransaction { name } => { + let (tx_id, transaction) = self.manager.create_registration_transaction(name).await?; + Ok(AssetManagerResponse::CreateRegistrationTransaction { + transaction: Box::new(transaction), + tx_id, + }) + }, AssetManagerRequest::GetOwnedAsset { public_key } => { let asset = self.manager.get_owned_asset_by_pub_key(public_key).await?; - Ok(AssetManagerResponse::GetOwnedAsset { asset: Box::new(asset)}) + Ok(AssetManagerResponse::GetOwnedAsset { asset: Box::new(asset) }) + }, + AssetManagerRequest::CreateMintingTransaction { + asset_public_key, + asset_owner_commitment, + unique_ids, + } => { + let (tx_id, transaction) = self + .manager + .create_minting_transaction(*asset_public_key, *asset_owner_commitment, unique_ids) + .await?; + Ok(AssetManagerResponse::CreateMintingTransaction { + transaction: Box::new(transaction), + tx_id, + }) }, - AssetManagerRequest::CreateMintingTransaction { asset_public_key, asset_owner_commitment, unique_ids } => { - let (tx_id, transaction) =self.manager.create_minting_transaction(*asset_public_key, *asset_owner_commitment, unique_ids).await?; - Ok(AssetManagerResponse::CreateMintingTransaction {transaction: Box::new(transaction), tx_id}) - } } } } diff --git a/base_layer/wallet/src/assets/infrastructure/initializer.rs b/base_layer/wallet/src/assets/infrastructure/initializer.rs index 12f9425cee..218cc71b7f 100644 --- a/base_layer/wallet/src/assets/infrastructure/initializer.rs +++ b/base_layer/wallet/src/assets/infrastructure/initializer.rs @@ -22,15 +22,11 @@ use crate::output_manager_service::storage::database::OutputManagerBackend; - -use crate::assets::AssetManagerHandle; -use crate::assets::infrastructure::AssetManagerService; +use crate::assets::{infrastructure::AssetManagerService, AssetManagerHandle}; use log::*; use futures::{future, Future}; - - use tari_service_framework::{ reply_channel, ServiceInitializationError, @@ -39,36 +35,29 @@ use tari_service_framework::{ }; use crate::output_manager_service::handle::OutputManagerHandle; -use tari_service_framework::{ - async_trait, -}; +use tari_service_framework::async_trait; const LOG_TARGET: &str = "wallet::assets::infrastructure::initializer"; pub struct AssetManagerServiceInitializer - where T: OutputManagerBackend +where T: OutputManagerBackend { - backend: Option + backend: Option, } impl AssetManagerServiceInitializer - where T: OutputManagerBackend + 'static +where T: OutputManagerBackend + 'static { - pub fn new(backend: T - - ) -> Self { - Self { - backend: Some(backend) - } + pub fn new(backend: T) -> Self { + Self { backend: Some(backend) } } } #[async_trait] impl ServiceInitializer for AssetManagerServiceInitializer - where T: OutputManagerBackend + 'static +where T: OutputManagerBackend + 'static { async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { - let (sender, receiver) = reply_channel::unbounded(); let handle = AssetManagerHandle::new(sender); @@ -77,8 +66,7 @@ impl ServiceInitializer for AssetManagerServiceInitializer let backend = self.backend.take().expect("this expect pattern is dumb"); context.spawn_when_ready(move |handles| async move { - - let output_manager = handles.expect_handle::(); + let output_manager = handles.expect_handle::(); // let transaction_service = handles.expect_handle::(); let service = AssetManagerService::new(backend, output_manager); @@ -91,4 +79,3 @@ impl ServiceInitializer for AssetManagerServiceInitializer Ok(()) } } - diff --git a/base_layer/wallet/src/error.rs b/base_layer/wallet/src/error.rs index ff111e877a..2852bdb651 100644 --- a/base_layer/wallet/src/error.rs +++ b/base_layer/wallet/src/error.rs @@ -40,9 +40,8 @@ use tari_comms_dht::store_forward::StoreAndForwardError; use tari_core::transactions::transaction::TransactionError; use tari_crypto::tari_utilities::{hex::HexError, ByteArrayError}; use tari_p2p::{initialization::CommsInitializationError, services::liveness::error::LivenessError}; -use tari_service_framework::ServiceInitializationError; +use tari_service_framework::{reply_channel::TransportChannelError, ServiceInitializationError}; use thiserror::Error; -use tari_service_framework::reply_channel::TransportChannelError; #[derive(Debug, Error)] pub enum WalletError { @@ -89,7 +88,7 @@ pub enum WalletError { TransportChannelError(#[from] TransportChannelError), #[error("Unexpected API Response while calling method `{method}` on `{api}`")] - UnexpectedApiResponse{ method: String, api: String}, + UnexpectedApiResponse { method: String, api: String }, } #[derive(Debug, Error)] diff --git a/base_layer/wallet/src/lib.rs b/base_layer/wallet/src/lib.rs index 012b481d84..61407e9017 100644 --- a/base_layer/wallet/src/lib.rs +++ b/base_layer/wallet/src/lib.rs @@ -6,7 +6,6 @@ #![deny(unreachable_patterns)] #![deny(unknown_lints)] #![recursion_limit = "2048"] - #![allow(clippy::too_many_arguments)] #[macro_use] @@ -16,6 +15,7 @@ pub mod base_node_service; pub mod connectivity_service; pub mod contacts_service; pub mod error; +mod operation_id; pub mod output_manager_service; pub mod storage; pub mod test_utils; @@ -24,7 +24,6 @@ pub mod transaction_service; pub mod types; pub mod util; pub mod wallet; -mod operation_id; pub use operation_id::OperationId; #[macro_use] diff --git a/base_layer/wallet/src/operation_id.rs b/base_layer/wallet/src/operation_id.rs index 6a91989062..cb242c78a3 100644 --- a/base_layer/wallet/src/operation_id.rs +++ b/base_layer/wallet/src/operation_id.rs @@ -20,13 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt; -use std::fmt::Formatter; -use serde::{Serialize, Deserialize}; -use std::hash::{Hash, Hasher}; -use rand::rngs::OsRng; -use rand::RngCore; - +use rand::{rngs::OsRng, RngCore}; +use serde::{Deserialize, Serialize}; +use std::{ + fmt, + fmt::Formatter, + hash::{Hash, Hasher}, +}; #[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)] pub struct OperationId(u64); @@ -53,10 +53,7 @@ impl PartialEq for OperationId { } } - -impl Eq for OperationId { - -} +impl Eq for OperationId {} impl From for OperationId { fn from(s: u64) -> Self { @@ -65,7 +62,7 @@ impl From for OperationId { } impl From for u64 { - fn from(s: OperationId) ->Self { + fn from(s: OperationId) -> Self { s.0 } } diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index fb47648786..2add294cd8 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -87,12 +87,20 @@ where TBackend: OutputManagerBackend + 'static output.sender_offset_public_key, output.metadata_signature, output.unique_id, - output.parent_public_key + output.parent_public_key, ) }) }) .map( - |(output, features, script, sender_offset_public_key, metadata_signature, unique_id, parent_public_key)| { + |( + output, + features, + script, + sender_offset_public_key, + metadata_signature, + unique_id, + parent_public_key, + )| { UnblindedOutput::new( output.committed_value, output.blinding_factor.clone(), @@ -103,7 +111,7 @@ where TBackend: OutputManagerBackend + 'static sender_offset_public_key, metadata_signature, unique_id, - parent_public_key + parent_public_key, ) }, ) diff --git a/base_layer/wallet/src/output_manager_service/storage/mod.rs b/base_layer/wallet/src/output_manager_service/storage/mod.rs index 64149adebd..a89c38f074 100644 --- a/base_layer/wallet/src/output_manager_service/storage/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/mod.rs @@ -22,6 +22,6 @@ pub mod database; pub mod models; -pub mod sqlite_db; mod output_status; +pub mod sqlite_db; pub use output_status::*; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 4fdbf7f250..7685bc31ae 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -51,6 +51,7 @@ use tari_core::{ MintNonFungibleFeatures, OutputFeatures, OutputFlags, + SideChainCheckpointFeatures, TransactionOutput, UnblindedOutput, }, @@ -937,12 +938,19 @@ impl TryFrom for DbUnblindedOutput { }), None => None, }; + let sidechain_checkpoint = match o.features_sidechain_checkpoint_merkle_root { + Some(ref merkle_root) => Some(SideChainCheckpointFeatures { + merkle_root: merkle_root.to_owned(), + }), + None => None, + }; let features = OutputFeatures { flags: OutputFlags::from_bits(o.flags as u8).ok_or(OutputManagerStorageError::ConversionError)?, maturity: o.maturity as u64, metadata: o.metadata.unwrap_or_default(), asset: asset_features, mint_non_fungible, + sidechain_checkpoint, }; let unblinded_output = UnblindedOutput::new( MicroTari::from(o.value as u64), diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index 33e319083c..8ef669d227 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -23,14 +23,16 @@ use aes_gcm::{Aes256Gcm, Error as AeadError}; use diesel::{RunQueryDsl, SqliteConnection}; -use tari_core::crypto::tari_utilities::ByteArray; -use tari_core::transactions::transaction_protocol::TxId; +use tari_core::{crypto::tari_utilities::ByteArray, transactions::transaction_protocol::TxId}; -use crate::output_manager_service::error::OutputManagerStorageError; -use crate::output_manager_service::storage::models::DbUnblindedOutput; -use crate::output_manager_service::storage::OutputStatus; -use crate::schema::outputs; -use crate::util::encryption::{decrypt_bytes_integral_nonce, encrypt_bytes_integral_nonce, Encryptable}; +use crate::{ + output_manager_service::{ + error::OutputManagerStorageError, + storage::{models::DbUnblindedOutput, OutputStatus}, + }, + schema::outputs, + util::encryption::{decrypt_bytes_integral_nonce, encrypt_bytes_integral_nonce, Encryptable}, +}; /// This struct represents an Output in the Sql database. A distinct struct is required to define the Sql friendly /// equivalent datatypes for the members. @@ -103,4 +105,3 @@ impl Encryptable for NewOutputSql { Ok(()) } } - diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 8cd46ed5cc..61e43a029f 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -37,6 +37,7 @@ pub struct OutputSql { pub features_asset_public_key: Option>, pub features_mint_asset_public_key: Option>, pub features_mint_asset_owner_commitment: Option>, + pub features_sidechain_checkpoint_merkle_root: Option>, pub parent_public_key: Option>, } diff --git a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs index 3ec966e4c6..85a8844604 100644 --- a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs +++ b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs @@ -420,7 +420,10 @@ where TBackend: OutputManagerBackend + 'static let output = TransactionOutput::try_from(output_proto.clone()).map_err(|err| { OutputManagerProtocolError::new( self.id, - OutputManagerError::ConversionError(format!("Could not convert protobuf TransactionOutput:{}", err)), + OutputManagerError::ConversionError(format!( + "Could not convert protobuf TransactionOutput:{}", + err + )), ) })?; returned_outputs.push(output); diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index 3fad2749ff..efd96802da 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -110,6 +110,7 @@ table! { features_asset_public_key -> Nullable, features_mint_asset_public_key -> Nullable, features_mint_asset_owner_commitment -> Nullable, + features_sidechain_checkpoint_merkle_root -> Nullable, parent_public_key -> Nullable, } } diff --git a/base_layer/wallet/src/tokens/infrastructure/initializer.rs b/base_layer/wallet/src/tokens/infrastructure/initializer.rs index 26fb07e471..5d31d7ebe1 100644 --- a/base_layer/wallet/src/tokens/infrastructure/initializer.rs +++ b/base_layer/wallet/src/tokens/infrastructure/initializer.rs @@ -22,15 +22,11 @@ use crate::output_manager_service::storage::database::OutputManagerBackend; - -use crate::tokens::TokenManagerHandle; -use crate::tokens::infrastructure::token_manager_service::TokenManagerService; +use crate::tokens::{infrastructure::token_manager_service::TokenManagerService, TokenManagerHandle}; use log::*; use futures::{future, Future}; - - use tari_service_framework::{ reply_channel, ServiceInitializationError, @@ -38,38 +34,30 @@ use tari_service_framework::{ ServiceInitializerContext, }; -use tari_service_framework::{ - async_trait, -}; use crate::output_manager_service::handle::OutputManagerHandle; - +use tari_service_framework::async_trait; const LOG_TARGET: &str = "wallet::assets::infrastructure::initializer"; pub struct TokenManagerServiceInitializer - where T: OutputManagerBackend +where T: OutputManagerBackend { - backend: Option + backend: Option, } impl TokenManagerServiceInitializer - where T: OutputManagerBackend + 'static +where T: OutputManagerBackend + 'static { - pub fn new(backend: T - - ) -> Self { - Self { - backend: Some(backend) - } + pub fn new(backend: T) -> Self { + Self { backend: Some(backend) } } } #[async_trait] impl ServiceInitializer for TokenManagerServiceInitializer - where T: OutputManagerBackend + 'static +where T: OutputManagerBackend + 'static { async fn initialize(&mut self, context: ServiceInitializerContext) -> Result<(), ServiceInitializationError> { - let (sender, receiver) = reply_channel::unbounded(); let handle = TokenManagerHandle::new(sender); @@ -78,8 +66,7 @@ impl ServiceInitializer for TokenManagerServiceInitializer let backend = self.backend.take().expect("this expect pattern is dumb"); context.spawn_when_ready(move |handles| async move { - - let output_manager = handles.expect_handle::(); + let output_manager = handles.expect_handle::(); // let transaction_service = handles.expect_handle::(); let service = TokenManagerService::new(backend, output_manager); @@ -92,4 +79,3 @@ impl ServiceInitializer for TokenManagerServiceInitializer Ok(()) } } - diff --git a/base_layer/wallet/src/tokens/infrastructure/mod.rs b/base_layer/wallet/src/tokens/infrastructure/mod.rs index ab7f92da17..2653f60db9 100644 --- a/base_layer/wallet/src/tokens/infrastructure/mod.rs +++ b/base_layer/wallet/src/tokens/infrastructure/mod.rs @@ -26,9 +26,9 @@ pub mod initializer; pub mod token_manager_service; pub enum TokenManagerRequest { - ListOwned{}, + ListOwned {}, } pub enum TokenManagerResponse { - ListOwned{ tokens : Vec}, + ListOwned { tokens: Vec }, } diff --git a/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs b/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs index c0c65725e6..b2ffcf1586 100644 --- a/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs +++ b/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs @@ -21,23 +21,18 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ + error::WalletError, + output_manager_service::{handle::OutputManagerHandle, storage::database::OutputManagerBackend}, tokens::{ infrastructure::{TokenManagerRequest, TokenManagerResponse}, TokenManager, }, - error::WalletError, - output_manager_service::storage::{ - database::{OutputManagerBackend}, - }, -}; -use tari_service_framework::{ - reply_channel::{Receiver, }, + types::MockPersistentKeyManager, }; use futures::{pin_mut, StreamExt}; -use tari_shutdown::ShutdownSignal; use log::*; -use crate::output_manager_service::handle::OutputManagerHandle; -use crate::types::MockPersistentKeyManager; +use tari_service_framework::reply_channel::Receiver; +use tari_shutdown::ShutdownSignal; const LOG_TARGET: &str = "wallet::assets::infrastructure::asset_manager_service"; @@ -93,7 +88,6 @@ impl TokenManagerService { TokenManagerRequest::ListOwned { .. } => Ok(TokenManagerResponse::ListOwned { tokens: self.manager.list_owned().await?, }), - } } } diff --git a/base_layer/wallet/src/tokens/mod.rs b/base_layer/wallet/src/tokens/mod.rs index d9567817d4..71d5cba554 100644 --- a/base_layer/wallet/src/tokens/mod.rs +++ b/base_layer/wallet/src/tokens/mod.rs @@ -20,11 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - mod token; -mod token_manager; pub(crate) mod infrastructure; +mod token; +mod token_manager; mod token_manager_handle; +pub use token::Token; pub(crate) use token_manager::TokenManager; pub use token_manager_handle::TokenManagerHandle; -pub use token::Token; diff --git a/base_layer/wallet/src/transaction_service/error.rs b/base_layer/wallet/src/transaction_service/error.rs index cea41bcb11..dbfebe752f 100644 --- a/base_layer/wallet/src/transaction_service/error.rs +++ b/base_layer/wallet/src/transaction_service/error.rs @@ -20,19 +20,25 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{output_manager_service::{error::OutputManagerError}, transaction_service::storage::database::DbKey, OperationId}; +use crate::{ + output_manager_service::error::OutputManagerError, + transaction_service::storage::database::DbKey, + OperationId, +}; use diesel::result::Error as DieselError; use futures::channel::oneshot::Canceled; use serde_json::Error as SerdeJsonError; use tari_comms::{peer_manager::node_id::NodeIdError, protocol::rpc::RpcError}; use tari_comms_dht::outbound::DhtOutboundError; -use tari_core::transactions::{transaction::TransactionError, transaction_protocol::TransactionProtocolError}; +use tari_core::transactions::{ + transaction::TransactionError, + transaction_protocol::{TransactionProtocolError, TxId}, +}; use tari_p2p::services::liveness::error::LivenessError; use tari_service_framework::reply_channel::TransportChannelError; use thiserror::Error; use time::OutOfRangeError; use tokio::sync::broadcast::error::RecvError; -use tari_core::transactions::transaction_protocol::TxId; #[derive(Debug, Error)] pub enum TransactionServiceError { @@ -188,7 +194,7 @@ pub struct TransactionServiceProtocolError { } impl TransactionServiceProtocolError { - pub fn new>(id: T, error: TransactionServiceError) -> Self { + pub fn new>(id: T, error: TransactionServiceError) -> Self { Self { id: id.into(), error } } } diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index e23f83e606..a8352374f8 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -20,10 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{transaction_service::{ - error::TransactionServiceError, - storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, -}, OperationId}; +use crate::{ + transaction_service::{ + error::TransactionServiceError, + storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, + }, + OperationId, +}; use aes_gcm::Aes256Gcm; use std::{collections::HashMap, fmt, sync::Arc}; use tari_comms::types::CommsPublicKey; @@ -33,10 +36,8 @@ use tokio::sync::broadcast; use tower::Service; use crate::types::ValidationRetryStrategy; -use tari_core::transactions::transaction_protocol::TxId; use std::fmt::Formatter; - - +use tari_core::transactions::transaction_protocol::TxId; /// API Request enum #[allow(clippy::large_enum_variant)] @@ -50,8 +51,20 @@ pub enum TransactionServiceRequest { GetCompletedTransaction(TxId), GetAnyTransaction(TxId), SetBaseNodePublicKey(CommsPublicKey), - SendTransaction{dest_pubkey: CommsPublicKey, amount: MicroTari, unique_id: Option>, fee_per_gram: MicroTari, message: String}, - SendOneSidedTransaction{dest_pubkey: CommsPublicKey, amount: MicroTari, unique_id: Option>, fee_per_gram: MicroTari, message: String}, + SendTransaction { + dest_pubkey: CommsPublicKey, + amount: MicroTari, + unique_id: Option>, + fee_per_gram: MicroTari, + message: String, + }, + SendOneSidedTransaction { + dest_pubkey: CommsPublicKey, + amount: MicroTari, + unique_id: Option>, + fee_per_gram: MicroTari, + message: String, + }, CancelTransaction(TxId), ImportUtxo(MicroTari, CommsPublicKey, String, Option), SubmitSelfSendTransaction(TxId, Transaction, MicroTari, MicroTari, String), @@ -79,10 +92,24 @@ impl fmt::Display for TransactionServiceRequest { Self::GetCancelledCompletedTransactions => f.write_str("GetCancelledCompletedTransactions"), Self::GetCompletedTransaction(t) => f.write_str(&format!("GetCompletedTransaction({})", t)), Self::SetBaseNodePublicKey(k) => f.write_str(&format!("SetBaseNodePublicKey ({})", k)), - Self::SendTransaction{ dest_pubkey, amount, message, .. }=> f.write_str(&format!("SendTransaction (to {}, {}, {})", dest_pubkey, amount, message)), - Self::SendOneSidedTransaction{dest_pubkey, amount, message, .. }=> { - f.write_str(&format!("SendOneSidedTransaction (to {}, {}, {})", dest_pubkey, amount, message)) - }, + Self::SendTransaction { + dest_pubkey, + amount, + message, + .. + } => f.write_str(&format!( + "SendTransaction (to {}, {}, {})", + dest_pubkey, amount, message + )), + Self::SendOneSidedTransaction { + dest_pubkey, + amount, + message, + .. + } => f.write_str(&format!( + "SendOneSidedTransaction (to {}, {}, {})", + dest_pubkey, amount, message + )), Self::CancelTransaction(t) => f.write_str(&format!("CancelTransaction ({})", t)), Self::ImportUtxo(v, k, msg, maturity) => f.write_str(&format!( "ImportUtxo (from {}, {}, {} with maturity: {})", @@ -168,29 +195,70 @@ pub enum TransactionEvent { impl fmt::Display for TransactionEvent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - TransactionEvent::MempoolBroadcastTimedOut(tx_id) => {write!(f, "MempoolBroadcastTimedOut for tx:{}", tx_id)} - TransactionEvent::ReceivedTransaction(tx) => {write!(f, "ReceivedTransaction for {}", tx)} - TransactionEvent::ReceivedTransactionReply(tx) => {write!(f, "ReceivedTransactionReply for {}", tx)} - TransactionEvent::ReceivedFinalizedTransaction(tx) => {write!(f, "ReceivedFinalizedTransaction for {}", tx)} - TransactionEvent::TransactionDiscoveryInProgress(tx) => {write!(f, "TransactionDiscoveryInProgress for {}", tx)} - TransactionEvent::TransactionDirectSendResult(tx, success) => {write!(f, "TransactionDirectSendResult for {}: {}", tx, success)} - TransactionEvent::TransactionCompletedImmediately(tx) => {write!(f, "TransactionCompletedImmediately for {}", tx)} - TransactionEvent::TransactionStoreForwardSendResult(tx, success) => {write!(f, "TransactionStoreForwardSendResult for {}:{}", tx, success)} - TransactionEvent::TransactionCancelled(tx) => {write!(f, "TransactionCancelled for {}", tx)} - TransactionEvent::TransactionBroadcast(tx) => {write!(f, "TransactionBroadcast for {}", tx)} - TransactionEvent::TransactionImported(tx) => {write!(f, "TransactionImported for {}", tx)} - TransactionEvent::TransactionMined(tx) => {write!(f, "TransactionMined for {}", tx)} - TransactionEvent::TransactionMinedRequestTimedOut(tx) => {write!(f, "TransactionMinedRequestTimedOut for {}", tx)} - TransactionEvent::TransactionMinedUnconfirmed(tx, height) => {write!(f, "TransactionMinedUnconfirmed for {} at height:{}", tx, height)} - TransactionEvent::TransactionValidationTimedOut(tx) => {write!(f, "TransactionValidationTimedOut for {}", tx)} - TransactionEvent::TransactionValidationSuccess(tx) => {write!(f, "TransactionValidationSuccess for {}", tx)} - TransactionEvent::TransactionValidationFailure(tx) => {write!(f, "TransactionValidationFailure for {}", tx)} - TransactionEvent::TransactionValidationAborted(tx) => {write!(f, "TransactionValidationAborted for {}", tx)} - TransactionEvent::TransactionValidationDelayed(tx) => {write!(f, "TransactionValidationDelayed for {}", tx)} - TransactionEvent::TransactionBaseNodeConnectionProblem(tx) => {write!(f, "TransactionBaseNodeConnectionProblem for {}", tx)} - TransactionEvent::Error(error) => {write!(f, "Error:{}", error)} + TransactionEvent::MempoolBroadcastTimedOut(tx_id) => { + write!(f, "MempoolBroadcastTimedOut for tx:{}", tx_id) + }, + TransactionEvent::ReceivedTransaction(tx) => { + write!(f, "ReceivedTransaction for {}", tx) + }, + TransactionEvent::ReceivedTransactionReply(tx) => { + write!(f, "ReceivedTransactionReply for {}", tx) + }, + TransactionEvent::ReceivedFinalizedTransaction(tx) => { + write!(f, "ReceivedFinalizedTransaction for {}", tx) + }, + TransactionEvent::TransactionDiscoveryInProgress(tx) => { + write!(f, "TransactionDiscoveryInProgress for {}", tx) + }, + TransactionEvent::TransactionDirectSendResult(tx, success) => { + write!(f, "TransactionDirectSendResult for {}: {}", tx, success) + }, + TransactionEvent::TransactionCompletedImmediately(tx) => { + write!(f, "TransactionCompletedImmediately for {}", tx) + }, + TransactionEvent::TransactionStoreForwardSendResult(tx, success) => { + write!(f, "TransactionStoreForwardSendResult for {}:{}", tx, success) + }, + TransactionEvent::TransactionCancelled(tx) => { + write!(f, "TransactionCancelled for {}", tx) + }, + TransactionEvent::TransactionBroadcast(tx) => { + write!(f, "TransactionBroadcast for {}", tx) + }, + TransactionEvent::TransactionImported(tx) => { + write!(f, "TransactionImported for {}", tx) + }, + TransactionEvent::TransactionMined(tx) => { + write!(f, "TransactionMined for {}", tx) + }, + TransactionEvent::TransactionMinedRequestTimedOut(tx) => { + write!(f, "TransactionMinedRequestTimedOut for {}", tx) + }, + TransactionEvent::TransactionMinedUnconfirmed(tx, height) => { + write!(f, "TransactionMinedUnconfirmed for {} at height:{}", tx, height) + }, + TransactionEvent::TransactionValidationTimedOut(tx) => { + write!(f, "TransactionValidationTimedOut for {}", tx) + }, + TransactionEvent::TransactionValidationSuccess(tx) => { + write!(f, "TransactionValidationSuccess for {}", tx) + }, + TransactionEvent::TransactionValidationFailure(tx) => { + write!(f, "TransactionValidationFailure for {}", tx) + }, + TransactionEvent::TransactionValidationAborted(tx) => { + write!(f, "TransactionValidationAborted for {}", tx) + }, + TransactionEvent::TransactionValidationDelayed(tx) => { + write!(f, "TransactionValidationDelayed for {}", tx) + }, + TransactionEvent::TransactionBaseNodeConnectionProblem(tx) => { + write!(f, "TransactionBaseNodeConnectionProblem for {}", tx) + }, + TransactionEvent::Error(error) => { + write!(f, "Error:{}", error) + }, } } } diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs index acdd0ad5aa..c4f8a0ea3d 100644 --- a/base_layer/wallet/src/transaction_service/storage/database.rs +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -20,16 +20,14 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - transaction_service::{ - error::TransactionStorageError, - storage::models::{ - CompletedTransaction, - InboundTransaction, - OutboundTransaction, - TransactionDirection, - TransactionStatus, - }, +use crate::transaction_service::{ + error::TransactionStorageError, + storage::models::{ + CompletedTransaction, + InboundTransaction, + OutboundTransaction, + TransactionDirection, + TransactionStatus, }, }; use aes_gcm::Aes256Gcm; @@ -44,8 +42,7 @@ use std::{ }; use tari_common_types::types::BlindingFactor; use tari_comms::types::CommsPublicKey; -use tari_core::transactions::{tari_amount::MicroTari, transaction::Transaction}; -use tari_core::transactions::transaction_protocol::TxId; +use tari_core::transactions::{tari_amount::MicroTari, transaction::Transaction, transaction_protocol::TxId}; const LOG_TARGET: &str = "wallet::transaction_service::database"; diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index 2c749f6a50..19435179bf 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{transaction_service::error::TransactionStorageError}; +use crate::transaction_service::error::TransactionStorageError; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; use std::{ @@ -32,10 +32,10 @@ use tari_comms::types::CommsPublicKey; use tari_core::transactions::{ tari_amount::MicroTari, transaction::Transaction, + transaction_protocol::TxId, ReceiverTransactionProtocol, SenderTransactionProtocol, }; -use tari_core::transactions::transaction_protocol::TxId; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum TransactionStatus { @@ -154,7 +154,7 @@ pub struct OutboundTransaction { pub direct_send_success: bool, pub send_count: u32, pub last_send_timestamp: Option, - pub unique_id: Option> + pub unique_id: Option>, } impl OutboundTransaction { diff --git a/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs b/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs index f92e07e675..8943e90668 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs @@ -19,12 +19,10 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - transaction_service::{ - config::TransactionRoutingMechanism, - error::TransactionServiceError, - tasks::wait_on_dial::wait_on_dial, - }, +use crate::transaction_service::{ + config::TransactionRoutingMechanism, + error::TransactionServiceError, + tasks::wait_on_dial::wait_on_dial, }; use log::*; use std::time::Duration; @@ -33,9 +31,11 @@ use tari_comms_dht::{ domain_message::OutboundDomainMessage, outbound::{OutboundEncryption, OutboundMessageRequester, SendMessageResponse}, }; -use tari_core::transactions::{transaction::Transaction, transaction_protocol::proto}; +use tari_core::transactions::{ + transaction::Transaction, + transaction_protocol::{proto, TxId}, +}; use tari_p2p::tari_message::TariMessageType; -use tari_core::transactions::transaction_protocol::TxId; const LOG_TARGET: &str = "wallet::transaction_service::tasks::send_finalized_transaction"; const LOG_TARGET_STRESS: &str = "stress_test::send_finalized_transaction"; diff --git a/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs b/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs index cf610e8ac7..375c846da5 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_transaction_cancelled.rs @@ -19,7 +19,7 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{transaction_service::error::TransactionServiceError}; +use crate::transaction_service::error::TransactionServiceError; use tari_comms::{peer_manager::NodeId, types::CommsPublicKey}; use tari_comms_dht::{ domain_message::OutboundDomainMessage, diff --git a/base_layer/wallet_ffi/src/callback_handler.rs b/base_layer/wallet_ffi/src/callback_handler.rs index 23c3d8c3ac..0c264de140 100644 --- a/base_layer/wallet_ffi/src/callback_handler.rs +++ b/base_layer/wallet_ffi/src/callback_handler.rs @@ -51,18 +51,22 @@ use log::*; use tari_comms::types::CommsPublicKey; use tari_comms_dht::event::{DhtEvent, DhtEventReceiver}; +use tari_core::transactions::transaction_protocol::TxId; use tari_shutdown::ShutdownSignal; -use tari_wallet::{output_manager_service::{ - handle::{OutputManagerEvent, OutputManagerEventReceiver}, - TxoValidationType, -}, transaction_service::{ - handle::{TransactionEvent, TransactionEventReceiver}, - storage::{ - database::{TransactionBackend, TransactionDatabase}, - models::{CompletedTransaction, InboundTransaction}, +use tari_wallet::{ + output_manager_service::{ + handle::{OutputManagerEvent, OutputManagerEventReceiver}, + TxoValidationType, }, -}, OperationId}; -use tari_core::transactions::transaction_protocol::TxId; + transaction_service::{ + handle::{TransactionEvent, TransactionEventReceiver}, + storage::{ + database::{TransactionBackend, TransactionDatabase}, + models::{CompletedTransaction, InboundTransaction}, + }, + }, + OperationId, +}; const LOG_TARGET: &str = "wallet::transaction_service::callback_handler"; @@ -470,13 +474,22 @@ where TBackend: TransactionBackend + 'static ); match result { CallbackValidationResults::Success => unsafe { - (self.callback_transaction_validation_complete)(request_key.as_u64(), CallbackValidationResults::Success as u8); + (self.callback_transaction_validation_complete)( + request_key.as_u64(), + CallbackValidationResults::Success as u8, + ); }, CallbackValidationResults::Aborted => unsafe { - (self.callback_transaction_validation_complete)(request_key.as_u64(), CallbackValidationResults::Aborted as u8); + (self.callback_transaction_validation_complete)( + request_key.as_u64(), + CallbackValidationResults::Aborted as u8, + ); }, CallbackValidationResults::Failure => unsafe { - (self.callback_transaction_validation_complete)(request_key.as_u64(), CallbackValidationResults::Failure as u8); + (self.callback_transaction_validation_complete)( + request_key.as_u64(), + CallbackValidationResults::Failure as u8, + ); }, CallbackValidationResults::BaseNodeNotInSync => unsafe { (self.callback_transaction_validation_complete)( diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 1114c37974..e23f182a0b 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -4344,10 +4344,12 @@ pub unsafe extern "C" fn wallet_cancel_pending_transaction( return false; } - match (*wallet) - .runtime - .block_on((*wallet).wallet.transaction_service.cancel_transaction(TxId::from(transaction_id))) - { + match (*wallet).runtime.block_on( + (*wallet) + .wallet + .transaction_service + .cancel_transaction(TxId::from(transaction_id)), + ) { Ok(_) => true, Err(e) => { error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; diff --git a/common/src/configuration/bootstrap.rs b/common/src/configuration/bootstrap.rs index 36570e4f55..289db2aef5 100644 --- a/common/src/configuration/bootstrap.rs +++ b/common/src/configuration/bootstrap.rs @@ -308,7 +308,7 @@ impl ConfigBootstrap { }, ApplicationType::DanNode => { install_configuration(&self.log_config, logging::install_default_base_node_logfile_config) - } + }, } } }; @@ -357,7 +357,7 @@ pub enum ApplicationType { MergeMiningProxy, MiningNode, StratumTranscoder, - DanNode + DanNode, } impl ApplicationType { @@ -381,7 +381,7 @@ impl ApplicationType { MergeMiningProxy => "merge_mining_proxy", MiningNode => "miner", StratumTranscoder => "stratum-transcoder", - DanNode =>"dan-node" + DanNode => "dan-node", } } } diff --git a/common/src/configuration/global.rs b/common/src/configuration/global.rs index 52a84196de..31e557aff7 100644 --- a/common/src/configuration/global.rs +++ b/common/src/configuration/global.rs @@ -23,7 +23,7 @@ //! # Global configuration of tari base layer system use crate::{ - configuration::{bootstrap::ApplicationType,DanNodeConfig, Network}, + configuration::{bootstrap::ApplicationType, DanNodeConfig, Network}, ConfigurationError, }; use config::{Config, ConfigError, Environment}; From 54c2ebc88ae79f84a18df498bfd9838a1f02265a Mon Sep 17 00:00:00 2001 From: Martin Stefcek Date: Fri, 10 Sep 2021 11:56:53 +0200 Subject: [PATCH 03/77] refactor: fix all the errors and warning during wallet compilation --- .../src/grpc/wallet_grpc_server.rs | 1 - .../base_node/comms_interface/inbound_handlers.rs | 12 ++++++------ .../wallet/src/assets/infrastructure/initializer.rs | 2 +- .../wallet/src/output_manager_service/service.rs | 2 +- .../wallet/src/tokens/infrastructure/initializer.rs | 2 +- .../tokens/infrastructure/token_manager_service.rs | 1 - base_layer/wallet/src/tokens/token_manager.rs | 10 +++------- base_layer/wallet/src/tokens/token_manager_handle.rs | 2 -- base_layer/wallet/src/transaction_service/error.rs | 6 +----- .../protocols/transaction_validation_protocol.rs | 1 - base_layer/wallet/src/transaction_service/service.rs | 2 +- 11 files changed, 14 insertions(+), 27 deletions(-) diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index 58b12a76b6..c6db66831b 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -1,4 +1,3 @@ -use crate::wallet_modes::grpc_mode; use futures::{channel::mpsc, future, SinkExt}; use log::*; use std::convert::TryFrom; diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index 818a811854..5b89ffb71d 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -42,9 +42,9 @@ use std::{ sync::Arc, }; use strum_macros::Display; -use tari_common_types::types::{BlockHash, HashOutput, PublicKey}; +use tari_common_types::types::{BlockHash, HashOutput}; use tari_comms::peer_manager::NodeId; -use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex, ByteArray}; +use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex}; use tokio::sync::Semaphore; const LOG_TARGET: &str = "c::bn::comms_interface::inbound_handler"; @@ -418,15 +418,15 @@ where T: BlockchainBackend + 'static Ok(NodeCommsResponse::TransactionKernels(kernels)) }, NodeCommsRequest::FetchTokens { - asset_public_key, - unique_ids, + asset_public_key: _asset_public_key, + unique_ids: _unique_ids, } => { // TODO: use index to prevent reading all outputs let tip_header = self.blockchain_db.fetch_tip_header().await?; debug!(target: LOG_TARGET, "Starting fetch tokens"); - let mut pos = 0; - let mut tokens = vec![]; + let pos = 0; + let tokens = vec![]; // TODO: fix this janky AF way of doing this while pos < tip_header.header().output_mmr_size { unimplemented!("Need to read from db index"); diff --git a/base_layer/wallet/src/assets/infrastructure/initializer.rs b/base_layer/wallet/src/assets/infrastructure/initializer.rs index 218cc71b7f..958dd30c28 100644 --- a/base_layer/wallet/src/assets/infrastructure/initializer.rs +++ b/base_layer/wallet/src/assets/infrastructure/initializer.rs @@ -25,7 +25,7 @@ use crate::output_manager_service::storage::database::OutputManagerBackend; use crate::assets::{infrastructure::AssetManagerService, AssetManagerHandle}; use log::*; -use futures::{future, Future}; +use futures::future; use tari_service_framework::{ reply_channel, diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 2c82c7c69d..444e765373 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -860,7 +860,7 @@ where TBackend: OutputManagerBackend + 'static .build::(&self.resources.factories) .map_err(|e| OutputManagerError::BuildError(e.message))?; if let Some((spending_key, script_private_key)) = change_keys { - let change_script_offset_public_key = stp.get_change_sender_offset_public_key()?.ok_or_else(|| { + let _change_script_offset_public_key = stp.get_change_sender_offset_public_key()?.ok_or_else(|| { OutputManagerError::BuildError( "There should be a change script offset public key available".to_string(), ) diff --git a/base_layer/wallet/src/tokens/infrastructure/initializer.rs b/base_layer/wallet/src/tokens/infrastructure/initializer.rs index 5d31d7ebe1..cac4c5a642 100644 --- a/base_layer/wallet/src/tokens/infrastructure/initializer.rs +++ b/base_layer/wallet/src/tokens/infrastructure/initializer.rs @@ -25,7 +25,7 @@ use crate::output_manager_service::storage::database::OutputManagerBackend; use crate::tokens::{infrastructure::token_manager_service::TokenManagerService, TokenManagerHandle}; use log::*; -use futures::{future, Future}; +use futures::future; use tari_service_framework::{ reply_channel, diff --git a/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs b/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs index b2ffcf1586..6cf0f5e82a 100644 --- a/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs +++ b/base_layer/wallet/src/tokens/infrastructure/token_manager_service.rs @@ -27,7 +27,6 @@ use crate::{ infrastructure::{TokenManagerRequest, TokenManagerResponse}, TokenManager, }, - types::MockPersistentKeyManager, }; use futures::{pin_mut, StreamExt}; use log::*; diff --git a/base_layer/wallet/src/tokens/token_manager.rs b/base_layer/wallet/src/tokens/token_manager.rs index 87eaecc13b..ca81c1b968 100644 --- a/base_layer/wallet/src/tokens/token_manager.rs +++ b/base_layer/wallet/src/tokens/token_manager.rs @@ -21,33 +21,29 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - assets::Asset, error::WalletError, output_manager_service::storage::database::{OutputManagerBackend, OutputManagerDatabase}, }; -use tari_core::transactions::transaction::{OutputFeatures, OutputFlags, Transaction}; +use tari_core::transactions::transaction::OutputFlags; use crate::{ output_manager_service::{handle::OutputManagerHandle, storage::models::DbUnblindedOutput}, tokens::Token, - types::PersistentKeyManager, }; use log::*; -use tari_core::transactions::transaction_protocol::TxId; -use tari_crypto::tari_utilities::ByteArray; const LOG_TARGET: &str = "wallet::tokens::token_manager"; pub(crate) struct TokenManager { output_database: OutputManagerDatabase, - output_manager: OutputManagerHandle, + _output_manager: OutputManagerHandle, // transaction_service: TransactionServiceHandle } impl TokenManager { pub fn new(backend: T, output_manager: OutputManagerHandle) -> Self { Self { output_database: OutputManagerDatabase::new(backend), - output_manager, + _output_manager: output_manager, } } diff --git a/base_layer/wallet/src/tokens/token_manager_handle.rs b/base_layer/wallet/src/tokens/token_manager_handle.rs index daf1fad5d2..ba48da8fc0 100644 --- a/base_layer/wallet/src/tokens/token_manager_handle.rs +++ b/base_layer/wallet/src/tokens/token_manager_handle.rs @@ -29,8 +29,6 @@ use crate::{ }; use tari_service_framework::{reply_channel::SenderService, Service}; -use tari_core::transactions::{transaction::Transaction, transaction_protocol::TxId}; - #[derive(Clone)] pub struct TokenManagerHandle { handle: SenderService>, diff --git a/base_layer/wallet/src/transaction_service/error.rs b/base_layer/wallet/src/transaction_service/error.rs index dbfebe752f..dce40e1091 100644 --- a/base_layer/wallet/src/transaction_service/error.rs +++ b/base_layer/wallet/src/transaction_service/error.rs @@ -20,11 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - output_manager_service::error::OutputManagerError, - transaction_service::storage::database::DbKey, - OperationId, -}; +use crate::{output_manager_service::error::OutputManagerError, transaction_service::storage::database::DbKey}; use diesel::result::Error as DieselError; use futures::channel::oneshot::Canceled; use serde_json::Error as SerdeJsonError; diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs index 59066ecb09..b6a878ed22 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs @@ -43,7 +43,6 @@ use tari_core::{ rpc::BaseNodeWalletRpcClient, }, proto::{base_node::Signatures as SignaturesProto, types::Signature as SignatureProto}, - transactions::transaction_protocol::TxId, }; use tokio::{sync::broadcast, time::sleep}; diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 864878d020..fa0f1bc5dc 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -31,7 +31,7 @@ use chrono::{NaiveDateTime, Utc}; use digest::Digest; use futures::{pin_mut, stream::FuturesUnordered, Stream, StreamExt}; use log::*; -use rand::{rngs::OsRng, RngCore}; +use rand::rngs::OsRng; use tari_crypto::{keys::DiffieHellmanSharedSecret, script, tari_utilities::ByteArray}; use tokio::{sync::broadcast, task::JoinHandle}; From 6f355f7f9fa546fc8660767c66ef00574a6938a1 Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Thu, 9 Sep 2021 12:23:55 +0200 Subject: [PATCH 04/77] feat: add dibbler testnet and genesis block --- base_layer/core/src/blocks/genesis_block.rs | 113 ++++++++++++++++-- .../core/src/consensus/consensus_constants.rs | 32 +++++ .../core/src/consensus/consensus_manager.rs | 11 +- base_layer/core/src/consensus/network.rs | 1 + .../core/src/transactions/crypto_factories.rs | 2 +- .../core/tests/helpers/block_builders.rs | 30 +++-- common/src/configuration/network.rs | 3 + common/src/configuration/utils.rs | 59 +++++++++ 8 files changed, 221 insertions(+), 30 deletions(-) diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index b97829d7c5..990b3a5a25 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -84,8 +84,7 @@ pub fn get_weatherwax_genesis_block() -> ChainBlock { ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap() } -#[allow(deprecated)] -pub fn get_weatherwax_genesis_block_raw() -> Block { +fn get_weatherwax_genesis_block_raw() -> Block { let sig = Signature::new( PublicKey::from_hex("f2139d1cdbcfa670bbb60d4d03d9d50b0a522e674b11280e8064f6dc30e84133").unwrap(), PrivateKey::from_hex("3ff7522d9a744ebf99c7b6664c0e2c8c64d2a7b902a98b78964766f9f7f2b107").unwrap(), @@ -153,7 +152,6 @@ pub fn get_weatherwax_genesis_block_raw() -> Block { } } -/// This will get the ridcully gen block pub fn get_ridcully_genesis_block() -> ChainBlock { // lets get the block let mut block = get_ridcully_genesis_block_raw(); @@ -193,8 +191,7 @@ pub fn get_ridcully_genesis_block() -> ChainBlock { ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap() } -#[allow(deprecated)] -pub fn get_ridcully_genesis_block_raw() -> Block { +fn get_ridcully_genesis_block_raw() -> Block { let sig = Signature::new( PublicKey::from_hex("f2139d1cdbcfa670bbb60d4d03d9d50b0a522e674b11280e8064f6dc30e84133").unwrap(), PrivateKey::from_hex("3ff7522d9a744ebf99c7b6664c0e2c8c64d2a7b902a98b78964766f9f7f2b107").unwrap(), @@ -275,8 +272,7 @@ pub fn get_igor_genesis_block() -> ChainBlock { ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap() } -#[allow(deprecated)] -pub fn get_igor_genesis_block_raw() -> Block { +fn get_igor_genesis_block_raw() -> Block { let sig = Signature::new( PublicKey::from_hex("f2139d1cdbcfa670bbb60d4d03d9d50b0a522e674b11280e8064f6dc30e84133").unwrap(), PrivateKey::from_hex("3ff7522d9a744ebf99c7b6664c0e2c8c64d2a7b902a98b78964766f9f7f2b107").unwrap(), @@ -348,6 +344,91 @@ pub fn get_igor_genesis_block_raw() -> Block { } } +pub fn get_dibbler_genesis_block() -> ChainBlock { + // lets get the block + let block = get_dibbler_genesis_block_raw(); + + let accumulated_data = BlockHeaderAccumulatedData { + hash: block.hash(), + total_kernel_offset: block.header.total_kernel_offset.clone(), + achieved_difficulty: 1.into(), + total_accumulated_difficulty: 1, + accumulated_monero_difficulty: 1.into(), + accumulated_sha_difficulty: 1.into(), + target_difficulty: 1.into(), + }; + ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap() +} + +fn get_dibbler_genesis_block_raw() -> Block { + let sig = Signature::new( + PublicKey::from_hex("6e6b47d8f6c654367858204b4277d854f78e0267baaf26cd513813ee23c16969").unwrap(), + PrivateKey::from_hex("92a310f7bbb351850e599ac6719d530625643fad74cb2902ab4adaf9207ff60d").unwrap(), + ); + let mut body = AggregateBody::new( + vec![], + vec![TransactionOutput { + features: OutputFeatures { + flags: OutputFlags::COINBASE_OUTPUT, + maturity: 60, + ..Default::default() + }, + commitment: Commitment::from_hex( + "c44428c3c707009befb074fb6db679049fd1a6c9b0466b281945c12670e8d706", + ) + .unwrap(), + proof: BulletRangeProof::from_hex("72101d8e581bb12bf153bed6eb321dd151e94e5d966e7545504b2f73a2cfda43bce0d87a9c0b4008762d30edaa4574b787824614b2f150e6456b609a7c2640603cc06570b44c60eeec2b88dc43a2aea484f357ef14ecb078a1f80f40b57d3812beab3a6fe23cacb3644b16967fdfd390daa6443fc03f711b001e9fe551f9f979db76189f4e17ed648bcfa419abd455bb5ec40e1801302d8cb870a3fbe4ad760a7dece961bff85989a75216f24c342c266f8819df33805e1df3dab7e6b8451007c58192a9bc3e660324309e0c34a3a67bab78156cc2443c7718a351136905a80af0268b08fb015b2b32cd6a9f6d839716cf549303c6c34a9db0f92e015a32414bfa5c680cf51439c9810a0783a51cc6176e52bddfd98bad7897742e9c107732791a5047022ec1f4e3c7569214e79cb1903131e43c2a2bd537c0ef04904e667a46961a5961340e17b38ac511176adfb0f94dd3b112589ef35b42c2745dd1d9ed5acadfd3226c51fed42f71c0cacbfefd05abe5d7dc7f799d7f2147c7803762cb1bc0da30fa9eca2e9748f9a55e19fb5ffad19865d370850a98bb165649c7a2814d147718ae8e58d126b7acb3500ce871d709083917873a8618ef04d364376ac142721c6d86c8b4a8d45cbf62310c18656ff950302a6fffc56ca1160e6496be756f40769bfc259043904d9d2c94bb9d69d842dc0bea5dcd708c1d96e47dba856d5cb46f0c69f67a02ec8da5ed5cc96d5f4a375059c2f38607da525e51453040d6647870a4ab6a7656cb5e2161412d1cb5284ba2abe7a21966f6ef3c615a9e4507711880fdb192277d1255318a4a5a9ff3b55a861bcadbee41e9fa653515a24bef2d6adf2db066c59c89bcc8b131dacb20f3f7390922a8a69c7105bb53e204f5e009eed3f5838b8d95e6cd1f00721c21f48419597ece1611b83c6b53abc2dd1b1804").unwrap(), + // For genesis block: A default script can never be spent, intentionally + script: TariScript::default(), + // Script offset never checked for coinbase, thus can use default + sender_offset_public_key: Default::default(), + // For genesis block: Metadata signature will never be checked + metadata_signature: Default::default(), + unique_id: None, + parent_public_key: None, + }], + vec![TransactionKernel { + features: KernelFeatures::COINBASE_KERNEL, + fee: MicroTari(0), + lock_height: 0, + excess: Commitment::from_hex("d0d8c0f6ba4913fd6f30595e0d7268e108cfbc8ae4a602dc6a33daf67fca434e").unwrap(), + excess_sig: sig, + }], + ); + body.sort(); + // set genesis timestamp + let genesis = DateTime::parse_from_rfc2822("09 Sep 2021 00:00:00 +0200").unwrap(); + let timestamp = genesis.timestamp() as u64; + Block { + header: BlockHeader { + version: 0, + height: 0, + prev_hash: vec![0; BLOCK_HASH_LENGTH], + timestamp: timestamp.into(), + output_mr: from_hex("aba0712711944acb920b549c196973c7cccdb38725eb595dbc5da5b33ce0220a").unwrap(), + witness_mr: from_hex("a84f23daa5f85af99f97fb2b13262d61f09e1059a625fb479c1926d2c63948bc").unwrap(), + output_mmr_size: 1, + kernel_mr: from_hex("12624ca9347f092db4fb832f122221d2a2514978ecf44def0438f53001253367").unwrap(), + kernel_mmr_size: 1, + input_mr: vec![0; BLOCK_HASH_LENGTH], + total_kernel_offset: PrivateKey::from_hex( + "0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + total_script_offset: PrivateKey::from_hex( + "0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + nonce: 0, + pow: ProofOfWork { + pow_algo: PowAlgorithm::Sha3, + pow_data: vec![], + }, + }, + body, + } +} + #[cfg(test)] mod test { use super::*; @@ -370,4 +451,22 @@ mod test { let coinbase_kernel = block.block().body.kernels().first().unwrap(); assert!(coinbase_kernel.features.contains(KernelFeatures::COINBASE_KERNEL)); } + + #[test] + fn dibbler_genesis_sanity_check() { + let block = get_dibbler_genesis_block(); + assert_eq!(block.block().body.outputs().len(), 1); + + let factories = CryptoFactories::default(); + let coinbase = block.block().body.outputs().first().unwrap(); + assert!(coinbase.is_coinbase()); + coinbase.verify_range_proof(&factories.range_proof).unwrap(); + assert_eq!(block.block().body.kernels().len(), 1); + for kernel in block.block().body.kernels() { + kernel.verify_signature().unwrap(); + } + + let coinbase_kernel = block.block().body.kernels().first().unwrap(); + assert!(coinbase_kernel.features.contains(KernelFeatures::COINBASE_KERNEL)); + } } diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 4c607a5119..dd5d485749 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -388,6 +388,38 @@ impl ConsensusConstants { }] } + pub fn dibbler() -> Vec { + let mut algos = HashMap::new(); + // seting sha3/monero to 40/60 split + algos.insert(PowAlgorithm::Sha3, PowAlgorithmConstants { + max_target_time: 1800, + min_difficulty: 60_000_000.into(), + max_difficulty: u64::MAX.into(), + target_time: 300, + }); + algos.insert(PowAlgorithm::Monero, PowAlgorithmConstants { + max_target_time: 1200, + min_difficulty: 60_000.into(), + max_difficulty: u64::MAX.into(), + target_time: 200, + }); + vec![ConsensusConstants { + effective_from_height: 0, + coinbase_lock_height: 6, + blockchain_version: 1, + future_time_limit: 540, + difficulty_block_window: 90, + max_block_transaction_weight: 19500, + median_timestamp_count: 11, + emission_initial: 5_538_846_115 * uT, + emission_decay: &EMISSION_DECAY, + emission_tail: 100.into(), + max_randomx_seed_height: std::u64::MAX, + proof_of_work: algos, + faucet_value: (5000 * 4000) * T, + }] + } + pub fn mainnet() -> Vec { // Note these values are all placeholders for final values let difficulty_block_window = 90; diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs index 984a88a430..c57c74e8fc 100644 --- a/base_layer/core/src/consensus/consensus_manager.rs +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -21,15 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - blocks::{ - genesis_block::{ - get_igor_genesis_block, - get_mainnet_genesis_block, - get_ridcully_genesis_block, - get_weatherwax_genesis_block, - }, - Block, - }, + blocks::{genesis_block::*, Block}, chain_storage::{ChainBlock, ChainStorageError}, consensus::{ chain_strength_comparer::{strongest_chain, ChainStrengthComparer}, @@ -83,6 +75,7 @@ impl ConsensusManager { .clone() .unwrap_or_else(get_weatherwax_genesis_block), Network::Igor => get_igor_genesis_block(), + Network::Dibbler => get_dibbler_genesis_block(), } } diff --git a/base_layer/core/src/consensus/network.rs b/base_layer/core/src/consensus/network.rs index 0e2e598a0a..78faea6585 100644 --- a/base_layer/core/src/consensus/network.rs +++ b/base_layer/core/src/consensus/network.rs @@ -37,6 +37,7 @@ impl NetworkConsensus { Weatherwax => ConsensusConstants::weatherwax(), LocalNet => ConsensusConstants::localnet(), Igor => ConsensusConstants::igor(), + Dibbler => ConsensusConstants::dibbler(), } } diff --git a/base_layer/core/src/transactions/crypto_factories.rs b/base_layer/core/src/transactions/crypto_factories.rs index 86270dc42d..19050de957 100644 --- a/base_layer/core/src/transactions/crypto_factories.rs +++ b/base_layer/core/src/transactions/crypto_factories.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use tari_common_types::types::{CommitmentFactory, RangeProofService, MAX_RANGE_PROOF_RANGE}; -/// A convenience struct wrapping cryptographic factories that are used through-out the rest of the code base +/// A convenience struct wrapping cryptographic factories that are used throughout the rest of the code base /// Uses Arcs internally so calling clone on this is cheap, no need to wrap this in an Arc pub struct CryptoFactories { pub commitment: Arc, diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 12d4659d62..50b9c5a3ba 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -108,10 +108,17 @@ fn genesis_template( (block, output) } -// This is a helper function to generate and print out a block that can be used as the genesis block. -// #[test] -pub fn _create_act_gen_block() { - let network = Network::Weatherwax; +#[test] +#[ignore = "used to generate a new genesis block"] +/// This is a helper function to generate and print out a block that can be used as the genesis block. +/// 1. Pick a network +/// 1. Run `cargo test --package tari_core --test mempool -- helpers::block_builders::print_new_genesis_block --exact +/// --nocapture --ignored` +/// 1. The block and range proof will be printed +/// 1. Profit! +fn print_new_genesis_block() { + let network = Network::Dibbler; + let consensus_manager: ConsensusManager = ConsensusManagerBuilder::new(network).build(); let factories = CryptoFactories::default(); let mut header = BlockHeader::new(consensus_manager.consensus_constants(0).blockchain_version()); @@ -126,17 +133,14 @@ pub fn _create_act_gen_block() { .build() .unwrap(); - let utxo_hash = utxo.hash(); - let witness_hash = utxo.witness_hash(); - let kern = kernel.hash(); - header.kernel_mr = kern; - header.output_mr = utxo_hash; - header.witness_mr = witness_hash; + header.kernel_mr = kernel.hash(); + header.output_mr = utxo.hash(); + header.witness_mr = utxo.witness_hash(); + let block = header.into_builder().with_coinbase_utxo(utxo, kernel).build(); println!("{}", &block); - dbg!(&key.to_hex()); - dbg!(&block.body.outputs()[0].proof.to_hex()); - panic!(); // this is so that the output is printed + println!("spending key: {}", &key.to_hex()); + println!("range proof: {}", &block.body.outputs()[0].proof.to_hex()); } /// Create a genesis block returning it with the spending key for the coinbase utxo diff --git a/common/src/configuration/network.rs b/common/src/configuration/network.rs index f9db42c923..4db25382f8 100644 --- a/common/src/configuration/network.rs +++ b/common/src/configuration/network.rs @@ -38,6 +38,7 @@ pub enum Network { Stibbons = 0x22, Weatherwax = 0xa3, Igor = 0x24, + Dibbler = 0x25, } impl Network { @@ -53,6 +54,7 @@ impl Network { Stibbons => "stibbons", Weatherwax => "weatherwax", Igor => "igor", + Dibbler => "dibbler", LocalNet => "localnet", } } @@ -76,6 +78,7 @@ impl FromStr for Network { "mainnet" => Ok(MainNet), "localnet" => Ok(LocalNet), "igor" => Ok(Igor), + "dibbler" => Ok(Dibbler), invalid => Err(ConfigurationError::new( "network", &format!("Invalid network option: {}", invalid), diff --git a/common/src/configuration/utils.rs b/common/src/configuration/utils.rs index 3814deb8d9..7aad9477a9 100644 --- a/common/src/configuration/utils.rs +++ b/common/src/configuration/utils.rs @@ -259,6 +259,33 @@ pub fn default_config(bootstrap: &ConfigBootstrap) -> Config { cfg.set_default("base_node.igor.dns_seeds_use_dnssec", true).unwrap(); cfg.set_default("base_node.igor.auto_ping_interval", 30).unwrap(); + //---------------------------------- Dibbler Defaults --------------------------------------------// + + cfg.set_default("base_node.dibbler.db_type", "lmdb").unwrap(); + cfg.set_default("base_node.dibbler.orphan_storage_capacity", 720) + .unwrap(); + cfg.set_default("base_node.dibbler.orphan_db_clean_out_threshold", 0) + .unwrap(); + cfg.set_default("base_node.dibbler.pruning_horizon", 0).unwrap(); + cfg.set_default("base_node.dibbler.pruned_mode_cleanup_interval", 50) + .unwrap(); + cfg.set_default("base_node.dibbler.flood_ban_max_msg_count", 1000) + .unwrap(); + cfg.set_default( + "base_node.dibbler.public_address", + format!("{}/tcp/18141", local_ip_addr), + ) + .unwrap(); + cfg.set_default("base_node.dibbler.grpc_enabled", false).unwrap(); + cfg.set_default("base_node.dibbler.grpc_base_node_address", "127.0.0.1:18142") + .unwrap(); + cfg.set_default("base_node.dibbler.grpc_console_wallet_address", "127.0.0.1:18143") + .unwrap(); + cfg.set_default("base_node.dibbler.dns_seeds_name_server", "1.1.1.1:53") + .unwrap(); + cfg.set_default("base_node.dibbler.dns_seeds_use_dnssec", true).unwrap(); + cfg.set_default("base_node.dibbler.auto_ping_interval", 30).unwrap(); + set_transport_defaults(&mut cfg).unwrap(); set_merge_mining_defaults(&mut cfg); set_mining_node_defaults(&mut cfg); @@ -277,6 +304,8 @@ fn set_stratum_transcoder_defaults(cfg: &mut Config) { .unwrap(); cfg.set_default("stratum_transcoder.igor.transcoder_host_address", "127.0.0.1:7879") .unwrap(); + cfg.set_default("stratum_transcoder.dibbler.transcoder_host_address", "127.0.0.1:7879") + .unwrap(); } fn set_merge_mining_defaults(cfg: &mut Config) { @@ -322,6 +351,18 @@ fn set_merge_mining_defaults(cfg: &mut Config) { cfg.set_default("merge_mining_proxy.igor.monerod_password", "").unwrap(); cfg.set_default("merge_mining_proxy.igor.wait_for_initial_sync_at_startup", true) .unwrap(); + cfg.set_default("merge_mining_proxy.dibbler.proxy_host_address", "127.0.0.1:7878") + .unwrap(); + cfg.set_default("merge_mining_proxy.dibbler.proxy_submit_to_origin", true) + .unwrap(); + cfg.set_default("merge_mining_proxy.dibbler.monerod_use_auth", "false") + .unwrap(); + cfg.set_default("merge_mining_proxy.dibbler.monerod_username", "") + .unwrap(); + cfg.set_default("merge_mining_proxy.dibbler.monerod_password", "") + .unwrap(); + cfg.set_default("merge_mining_proxy.dibbler.wait_for_initial_sync_at_startup", true) + .unwrap(); } fn set_mining_node_defaults(cfg: &mut Config) { @@ -417,6 +458,24 @@ fn set_transport_defaults(cfg: &mut Config) -> Result<(), config::ConfigError> { cfg.set_default(&format!("{}.igor.socks5_proxy_address", app), "/ip4/0.0.0.0/tcp/9150")?; cfg.set_default(&format!("{}.igor.socks5_auth", app), "none")?; + + // dibbler + cfg.set_default(&format!("{}.dibbler.transport", app), "tor")?; + + cfg.set_default( + &format!("{}.dibbler.tor_control_address", app), + "/ip4/127.0.0.1/tcp/9051", + )?; + cfg.set_default(&format!("{}.dibbler.tor_control_auth", app), "none")?; + cfg.set_default(&format!("{}.dibbler.tor_forward_address", app), "/ip4/127.0.0.1/tcp/0")?; + cfg.set_default(&format!("{}.dibbler.tor_onion_port", app), "18141")?; + + cfg.set_default( + &format!("{}.dibbler.socks5_proxy_address", app), + "/ip4/0.0.0.0/tcp/9150", + )?; + + cfg.set_default(&format!("{}.dibbler.socks5_auth", app), "none")?; } Ok(()) } From 6023923d0cc299742b0e466960e14f3d9d275105 Mon Sep 17 00:00:00 2001 From: striderDM <51991544+StriderDM@users.noreply.github.com> Date: Mon, 13 Sep 2021 09:37:12 +0200 Subject: [PATCH 05/77] feat: create initial asset checkpoint for console wallet Review comments --- .../src/automation/command_parser.rs | 2 + .../src/automation/commands.rs | 38 +++++++++++++++++++ .../core/src/transactions/transaction/mod.rs | 10 +++++ base_layer/wallet/src/assets/asset_manager.rs | 21 ++++++++++ .../wallet/src/assets/asset_manager_handle.rs | 21 ++++++++++ .../infrastructure/asset_manager_service.rs | 14 +++++++ .../wallet/src/assets/infrastructure/mod.rs | 5 +++ 7 files changed, 111 insertions(+) diff --git a/applications/tari_console_wallet/src/automation/command_parser.rs b/applications/tari_console_wallet/src/automation/command_parser.rs index b35cf31c0e..5106ca0e46 100644 --- a/applications/tari_console_wallet/src/automation/command_parser.rs +++ b/applications/tari_console_wallet/src/automation/command_parser.rs @@ -61,6 +61,7 @@ impl Display for ParsedCommand { ClearCustomBaseNode => "clear-custom-base-node", RegisterAsset => "register-asset", MintTokens => "mint-tokens", + CreateInitialCheckpoint => "create-initial-checkpoint", }; let args = self @@ -131,6 +132,7 @@ pub fn parse_command(command: &str) -> Result { RegisterAsset => parser_builder(args).text().build()?, // mint-tokens pub_key nft_id1 nft_id2 MintTokens => parser_builder(args).pub_key().text_array().build()?, + CreateInitialCheckpoint => parser_builder(args).pub_key().text().build()?, }; Ok(ParsedCommand { command, args }) diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index 1f667270ed..2bcbffdf64 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -85,6 +85,7 @@ pub enum WalletCommand { ClearCustomBaseNode, RegisterAsset, MintTokens, + CreateInitialCheckpoint, } #[derive(Debug, EnumString, PartialEq, Clone)] @@ -731,6 +732,43 @@ pub async fn command_runner( .submit_transaction(tx_id, transaction, fee, 0.into(), "test mint transaction".to_string()) .await?; }, + CreateInitialCheckpoint => { + println!("Creating Initial Checkpoint for Asset"); + let public_key = match parsed.args[0] { + ParsedArgument::PublicKey(ref key) => Ok(key.clone()), + _ => Err(CommandError::Argument), + }?; + + let merkle_root = match parsed.args[1] { + ParsedArgument::Text(ref root) => { + let s = root.to_string(); + match &s[0..2] { + "0x" => { + let s = s[2..].to_string(); + let r: Vec = Hex::from_hex(&s).unwrap(); + Ok(r) + }, + _ => Ok(s.into_bytes()), + } + }, + _ => Err(CommandError::Argument), + }?; + + let mut asset_manager = wallet.asset_manager.clone(); + let (tx_id, transaction) = asset_manager + .create_initial_asset_checkpoint(&public_key, &merkle_root) + .await?; + let fee = transaction.body.get_total_fee(); + let _result = transaction_service + .submit_transaction( + tx_id, + transaction, + fee, + 0.into(), + "test initial asset checkpoint transaction".to_string(), + ) + .await?; + }, } } diff --git a/base_layer/core/src/transactions/transaction/mod.rs b/base_layer/core/src/transactions/transaction/mod.rs index df1e708be5..1ed12abdac 100644 --- a/base_layer/core/src/transactions/transaction/mod.rs +++ b/base_layer/core/src/transactions/transaction/mod.rs @@ -183,6 +183,15 @@ impl OutputFeatures { ..Default::default() } } + + pub fn for_checkpoint(merkle_root: Vec) -> OutputFeatures { + Self { + flags: OutputFlags::SIDECHAIN_CHECKPOINT, + asset: None, + sidechain_checkpoint: Some(SideChainCheckpointFeatures { merkle_root }), + ..Default::default() + } + } } impl Default for OutputFeatures { @@ -229,6 +238,7 @@ bitflags! { // TODO: separate these flags const ASSET_REGISTRATION = 0b0000_1010; // Registration and also non-fungible const MINT_NON_FUNGIBLE = 0b0000_1100; // Mint and non-fungible + const SIDECHAIN_CHECKPOINT = 0b0000_1101; } } diff --git a/base_layer/wallet/src/assets/asset_manager.rs b/base_layer/wallet/src/assets/asset_manager.rs index 1cfbd02878..ab754fbb07 100644 --- a/base_layer/wallet/src/assets/asset_manager.rs +++ b/base_layer/wallet/src/assets/asset_manager.rs @@ -134,6 +134,27 @@ impl, + ) -> Result<(TxId, Transaction), WalletError> { + let output = self + .output_manager + .create_output_with_features( + 0.into(), + OutputFeatures::for_checkpoint(merkle_root), + Some([0u8; 64].to_vec()), + Some(asset.public_key().clone()), + ) + .await?; + let (tx_id, transaction) = self + .output_manager + .create_send_to_self_with_output(0.into(), vec![output], 0.into()) + .await?; + Ok((tx_id, transaction)) + } } fn convert_to_asset(unblinded_output: DbUnblindedOutput) -> Result { diff --git a/base_layer/wallet/src/assets/asset_manager_handle.rs b/base_layer/wallet/src/assets/asset_manager_handle.rs index fd2b755739..05ea149ff8 100644 --- a/base_layer/wallet/src/assets/asset_manager_handle.rs +++ b/base_layer/wallet/src/assets/asset_manager_handle.rs @@ -68,6 +68,27 @@ impl AssetManagerHandle { } } + pub async fn create_initial_asset_checkpoint( + &mut self, + public_key: &PublicKey, + merkle_root: &Vec, + ) -> Result<(TxId, Transaction), WalletError> { + match self + .handle + .call(AssetManagerRequest::CreateInitialCheckpoint { + asset_public_key: Box::new(public_key.clone()), + merkle_root: Box::new(merkle_root.clone()), + }) + .await?? + { + AssetManagerResponse::CreateInitialCheckpoint { transaction, tx_id } => Ok((tx_id, *transaction)), + _ => Err(WalletError::UnexpectedApiResponse { + method: "create_initial_asset_checkpoint".to_string(), + api: "AssetManagerService".to_string(), + }), + } + } + pub async fn create_registration_transaction(&mut self, name: String) -> Result<(TxId, Transaction), WalletError> { match self .handle diff --git a/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs b/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs index 82a1471a26..44cbad5d21 100644 --- a/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs +++ b/base_layer/wallet/src/assets/infrastructure/asset_manager_service.rs @@ -113,6 +113,20 @@ impl AssetManagerService { tx_id, }) }, + AssetManagerRequest::CreateInitialCheckpoint { + asset_public_key, + merkle_root, + } => { + let asset = self.manager.get_owned_asset_by_pub_key(*asset_public_key).await?; + let (tx_id, transaction) = self + .manager + .create_initial_asset_checkpoint(asset, *merkle_root) + .await?; + Ok(AssetManagerResponse::CreateInitialCheckpoint { + transaction: Box::new(transaction), + tx_id, + }) + }, } } } diff --git a/base_layer/wallet/src/assets/infrastructure/mod.rs b/base_layer/wallet/src/assets/infrastructure/mod.rs index 16fe057e7b..74131c00aa 100644 --- a/base_layer/wallet/src/assets/infrastructure/mod.rs +++ b/base_layer/wallet/src/assets/infrastructure/mod.rs @@ -42,6 +42,10 @@ pub enum AssetManagerRequest { asset_owner_commitment: Box, unique_ids: Vec>, }, + CreateInitialCheckpoint { + asset_public_key: Box, + merkle_root: Box>, + }, } pub enum AssetManagerResponse { @@ -49,4 +53,5 @@ pub enum AssetManagerResponse { GetOwnedAsset { asset: Box }, CreateRegistrationTransaction { transaction: Box, tx_id: TxId }, CreateMintingTransaction { transaction: Box, tx_id: TxId }, + CreateInitialCheckpoint { transaction: Box, tx_id: TxId }, } From b906339dedce2e3cdfb15900c302db3d6e85ffa7 Mon Sep 17 00:00:00 2001 From: David Main <51991544+StriderDM@users.noreply.github.com> Date: Tue, 14 Sep 2021 16:32:51 +0200 Subject: [PATCH 06/77] refactor!: move unique_id and parent_public_key to outputfeatures review comments --- Cargo.lock | 82 +++++++++---------- applications/tari_app_grpc/proto/types.proto | 18 ++-- .../src/conversions/output_features.rs | 10 +++ .../src/conversions/transaction_output.rs | 19 ----- .../src/conversions/unblinded_output.rs | 10 --- .../src/grpc/base_node_grpc_server.rs | 8 +- .../src/grpc/wallet_grpc_server.rs | 2 +- base_layer/core/src/blocks/genesis_block.rs | 8 -- base_layer/core/src/proto/transaction.proto | 16 ++-- base_layer/core/src/proto/transaction.rs | 29 +++---- .../core/src/transactions/coinbase_builder.rs | 2 - base_layer/core/src/transactions/helpers.rs | 6 -- .../core/src/transactions/transaction/mod.rs | 32 ++------ .../proto/transaction_sender.proto | 2 +- .../proto/transaction_sender.rs | 7 -- .../transaction_protocol/sender.rs | 3 - .../sender_transaction_protocol_builder.rs | 2 - .../transaction_protocol/single_receiver.rs | 2 - .../2021-07-07-121212_add_unique_id/up.sql | 4 +- .../recovery/standard_outputs_recoverer.rs | 14 +--- .../src/output_manager_service/service.rs | 12 +-- .../storage/sqlite_db/mod.rs | 8 +- .../storage/sqlite_db/new_output_sql.rs | 8 +- .../storage/sqlite_db/output_sql.rs | 4 +- base_layer/wallet/src/schema.rs | 7 +- base_layer/wallet/src/tokens/token_manager.rs | 8 +- .../protocols/transaction_receive_protocol.rs | 2 - .../protocols/transaction_send_protocol.rs | 2 - .../wallet/src/transaction_service/service.rs | 4 - .../transaction_service/storage/database.rs | 1 - .../src/transaction_service/storage/models.rs | 13 --- .../transaction_service/storage/sqlite_db.rs | 9 -- base_layer/wallet/src/wallet.rs | 2 - 33 files changed, 118 insertions(+), 238 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9907c8a623..5e2717df4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1759,7 +1759,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-util 0.6.7", "tracing", ] @@ -1926,7 +1926,7 @@ dependencies = [ "itoa", "pin-project-lite 0.2.7", "socket2 0.4.1", - "tokio 1.10.1", + "tokio 1.11.0", "tower-service", "tracing", "want", @@ -1940,7 +1940,7 @@ checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper 0.14.12", "pin-project-lite 0.2.7", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-io-timeout", ] @@ -1966,7 +1966,7 @@ dependencies = [ "bytes 1.1.0", "hyper 0.14.12", "native-tls", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-native-tls", ] @@ -2986,7 +2986,7 @@ dependencies = [ "pin-project 1.0.8", "rand 0.8.4", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", ] @@ -3002,7 +3002,7 @@ dependencies = [ "opentelemetry-semantic-conventions", "thiserror", "thrift", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -3761,7 +3761,7 @@ dependencies = [ "serde 1.0.130", "serde_json", "serde_urlencoded", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-native-tls", "url 2.2.2", "wasm-bindgen", @@ -4501,7 +4501,7 @@ dependencies = [ "tari_p2p", "tari_wallet", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tonic", ] @@ -4535,7 +4535,7 @@ dependencies = [ "tari_service_framework", "tari_shutdown", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tonic", "tracing", "tracing-opentelemetry", @@ -4601,7 +4601,7 @@ dependencies = [ "rand 0.8.4", "serde 1.0.130", "tari_crypto", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -4643,7 +4643,7 @@ dependencies = [ "tari_test_utils 0.9.6", "tempfile", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", "tokio-util 0.6.7", "tower 0.3.1", @@ -4692,7 +4692,7 @@ dependencies = [ "tari_utilities", "tempfile", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", "tokio-test 0.4.2", "tower 0.3.1", @@ -4711,7 +4711,7 @@ dependencies = [ "syn 1.0.75", "tari_comms", "tari_test_utils 0.9.6", - "tokio 1.10.1", + "tokio 1.11.0", "tower-service", ] @@ -4747,7 +4747,7 @@ dependencies = [ "tari_shutdown", "tari_wallet", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tonic", "tracing", "tracing-opentelemetry", @@ -4802,7 +4802,7 @@ dependencies = [ "tari_test_utils 0.9.6", "tempfile", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tracing", "tracing-attributes", "tracing-futures", @@ -4928,7 +4928,7 @@ dependencies = [ "tari_crypto", "tari_utilities", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tonic", "tracing", "tracing-futures", @@ -4962,7 +4962,7 @@ dependencies = [ "tari_crypto", "thiserror", "time", - "tokio 1.10.1", + "tokio 1.11.0", "tonic", ] @@ -5020,7 +5020,7 @@ dependencies = [ "tari_utilities", "tempfile", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", "tower 0.3.1", "tower-service", @@ -5039,7 +5039,7 @@ dependencies = [ "tari_shutdown", "tari_test_utils 0.9.6", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tower 0.3.1", "tower-service", ] @@ -5049,7 +5049,7 @@ name = "tari_shutdown" version = "0.9.6" dependencies = [ "futures 0.3.16", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5114,7 +5114,7 @@ dependencies = [ "tari_crypto", "tari_utilities", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "tonic", "tonic-build", "tracing", @@ -5147,7 +5147,7 @@ dependencies = [ "rand 0.8.4", "tari_shutdown", "tempfile", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5206,7 +5206,7 @@ dependencies = [ "tempfile", "thiserror", "time", - "tokio 1.10.1", + "tokio 1.11.0", "tower 0.3.1", ] @@ -5235,7 +5235,7 @@ dependencies = [ "tari_wallet", "tempfile", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5272,7 +5272,7 @@ dependencies = [ "tari_core", "tari_crypto", "tari_utilities", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5412,9 +5412,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5" +checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" dependencies = [ "autocfg 1.0.1", "bytes 1.1.0", @@ -5436,7 +5436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9" dependencies = [ "pin-project-lite 0.2.7", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5468,7 +5468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5478,7 +5478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls", - "tokio 1.10.1", + "tokio 1.11.0", "webpki", ] @@ -5490,7 +5490,7 @@ checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" dependencies = [ "futures-core", "pin-project-lite 0.2.7", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-util 0.6.7", ] @@ -5514,7 +5514,7 @@ dependencies = [ "async-stream", "bytes 1.1.0", "futures-core", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", ] @@ -5554,7 +5554,7 @@ dependencies = [ "futures-sink", "log 0.4.14", "pin-project-lite 0.2.7", - "tokio 1.10.1", + "tokio 1.11.0", ] [[package]] @@ -5596,7 +5596,7 @@ dependencies = [ "pin-project 1.0.8", "prost", "prost-derive", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", "tokio-util 0.6.7", "tower 0.4.8", @@ -5648,7 +5648,7 @@ dependencies = [ "pin-project 1.0.8", "rand 0.8.4", "slab", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-stream", "tokio-util 0.6.7", "tower-layer", @@ -5929,9 +5929,9 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] name = "trust-dns-client" -version = "0.21.0-alpha.1" +version = "0.21.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37532ce92c75c6174b1d51ed612e26c5fde66ef3f29aa10dbd84e7c5d9a0c27b" +checksum = "edaa01dcab6aff8dbd2efa666c5b656729d2c04728c50e16e30117be07c764ac" dependencies = [ "cfg-if 1.0.0", "chrono", @@ -5945,16 +5945,16 @@ dependencies = [ "ring", "rustls", "thiserror", - "tokio 1.10.1", + "tokio 1.11.0", "trust-dns-proto", "webpki", ] [[package]] name = "trust-dns-proto" -version = "0.21.0-alpha.1" +version = "0.21.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd23117e93ea0e776abfd8a07c9e389d7ecd3377827858f21bd795ebdfefa36" +checksum = "09637dee9c56a62b9acd8ca59ab2ed9459d8430b005100d9063ea326a0a3590a" dependencies = [ "async-trait", "cfg-if 1.0.0", @@ -5973,7 +5973,7 @@ dependencies = [ "smallvec", "thiserror", "tinyvec", - "tokio 1.10.1", + "tokio 1.11.0", "tokio-rustls", "url 2.2.2", "webpki", diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index 7f61b31399..17eaaabeaf 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -191,10 +191,6 @@ message TransactionOutput { // offset private key ComSignature metadata_signature = 7; // Tari script offset pubkey, K_O - - // Unique id, e.g. for NFTs - bytes unique_id = 8; - bytes parent_public_key = 9; } // Options for UTXO's @@ -207,9 +203,15 @@ message OutputFeatures { bytes metadata= 3; - AssetOutputFeatures asset = 16; - MintNonFungibleFeatures mint_non_fungible = 17; - SideChainCheckpointFeatures sidechain_checkpoint = 18; + AssetOutputFeatures asset = 4; + + optional bytes unique_id = 5; + + optional bytes parent_public_key = 6; + + MintNonFungibleFeatures mint_non_fungible = 7; + + SideChainCheckpointFeatures sidechain_checkpoint = 8; } message AssetOutputFeatures { @@ -313,8 +315,6 @@ message UnblindedOutput { bytes sender_offset_public_key = 8; // UTXO signature with the script offset private key, k_O ComSignature metadata_signature = 9; - // Unique Id - bytes unique_id = 10; } // ----------------------------- Network Types ----------------------------- // diff --git a/applications/tari_app_grpc/src/conversions/output_features.rs b/applications/tari_app_grpc/src/conversions/output_features.rs index f0bd39e7e4..2396016631 100644 --- a/applications/tari_app_grpc/src/conversions/output_features.rs +++ b/applications/tari_app_grpc/src/conversions/output_features.rs @@ -41,6 +41,11 @@ impl TryFrom for OutputFeatures { .ok_or_else(|| "Invalid or unrecognised output flags".to_string())?, maturity: features.maturity, metadata: features.metadata, + unique_id: features.unique_id, + parent_public_key: match features.parent_public_key { + Some(a) => Some(PublicKey::from_bytes(a.as_bytes()).map_err(|err| format!("{:?}", err))?), + None => None, + }, asset: features.asset.map(|a| a.try_into()).transpose()?, mint_non_fungible: features.mint_non_fungible.map(|m| m.try_into()).transpose()?, sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), @@ -54,6 +59,11 @@ impl From for grpc::OutputFeatures { flags: features.flags.bits() as u32, maturity: features.maturity, metadata: features.metadata, + unique_id: features.unique_id, + parent_public_key: match features.parent_public_key { + Some(a) => Some(a.as_bytes().to_vec()), + None => None, + }, asset: features.asset.map(|a| a.into()), mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), diff --git a/applications/tari_app_grpc/src/conversions/transaction_output.rs b/applications/tari_app_grpc/src/conversions/transaction_output.rs index a0689e138f..26efa4a60b 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_output.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_output.rs @@ -54,30 +54,13 @@ impl TryFrom for TransactionOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; - let unique_id = if output.unique_id.is_empty() { - None - } else { - Some(output.unique_id.clone()) - }; - - let parent_public_key = if output.parent_public_key.is_empty() { - None - } else { - Some( - PublicKey::from_bytes(output.parent_public_key.as_bytes()) - .map_err(|err| format!("parent_public_key {:?}", err))?, - ) - }; - Ok(Self { features, - unique_id, commitment, proof: BulletRangeProof(output.range_proof), script, sender_offset_public_key, metadata_signature, - parent_public_key, }) } } @@ -97,8 +80,6 @@ impl From for grpc::TransactionOutput { signature_u: Vec::from(output.metadata_signature.u().as_bytes()), signature_v: Vec::from(output.metadata_signature.v().as_bytes()), }), - unique_id: output.unique_id.unwrap_or_default(), - parent_public_key: output.parent_public_key.map(|b| b.to_vec()).unwrap_or_default(), } } } diff --git a/applications/tari_app_grpc/src/conversions/unblinded_output.rs b/applications/tari_app_grpc/src/conversions/unblinded_output.rs index 8a62661c1a..4e84054b6f 100644 --- a/applications/tari_app_grpc/src/conversions/unblinded_output.rs +++ b/applications/tari_app_grpc/src/conversions/unblinded_output.rs @@ -46,7 +46,6 @@ impl From for grpc::UnblindedOutput { signature_u: Vec::from(output.metadata_signature.u().as_bytes()), signature_v: Vec::from(output.metadata_signature.v().as_bytes()), }), - unique_id: output.unique_id.unwrap_or_default(), } } } @@ -80,11 +79,6 @@ impl TryFrom for UnblindedOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; - let unique_id = if output.unique_id.is_empty() { - None - } else { - Some(output.unique_id.clone()) - }; Ok(Self { value: MicroTari::from(output.value), spending_key, @@ -94,10 +88,6 @@ impl TryFrom for UnblindedOutput { script_private_key, sender_offset_public_key, metadata_signature, - unique_id, - - // TODO: Remove this none - parent_public_key: None, }) } } diff --git a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs index 9839877281..aca7e0d839 100644 --- a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs +++ b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs @@ -433,8 +433,12 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { for token in tokens { match tx .send(Ok(tari_rpc::GetTokensResponse { - asset_public_key: token.parent_public_key.map(|pk| pk.to_vec()).unwrap_or_default(), - unique_id: token.unique_id.unwrap_or_default(), + asset_public_key: token + .features + .parent_public_key + .map(|pk| pk.to_vec()) + .unwrap_or_default(), + unique_id: token.features.unique_id.unwrap_or_default(), owner_commitment: token.commitment.to_vec(), mined_in_block: vec![], mined_height: 0, diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index c6db66831b..84856620a2 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -383,7 +383,7 @@ impl wallet_server::Wallet for WalletGrpcServer { .body .outputs() .iter() - .filter_map(|o| o.unique_id.as_ref().map(|_| o.commitment.to_vec())) + .filter_map(|o| o.features.unique_id.as_ref().map(|_| o.commitment.to_vec())) .collect(); let _result = transaction_service .submit_transaction(tx_id, transaction, fee, 0.into(), "test mint transaction".to_string()) diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index 990b3a5a25..aaad87cd89 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -104,8 +104,6 @@ fn get_weatherwax_genesis_block_raw() -> Block { sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked metadata_signature: Default::default(), - unique_id: None, - parent_public_key: None }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, @@ -211,8 +209,6 @@ fn get_ridcully_genesis_block_raw() -> Block { sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked metadata_signature: Default::default(), - unique_id: None, - parent_public_key: None }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, @@ -296,8 +292,6 @@ fn get_igor_genesis_block_raw() -> Block { sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked metadata_signature: Default::default(), - unique_id: None, - parent_public_key: None }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, @@ -384,8 +378,6 @@ fn get_dibbler_genesis_block_raw() -> Block { sender_offset_public_key: Default::default(), // For genesis block: Metadata signature will never be checked metadata_signature: Default::default(), - unique_id: None, - parent_public_key: None, }], vec![TransactionKernel { features: KernelFeatures::COINBASE_KERNEL, diff --git a/base_layer/core/src/proto/transaction.proto b/base_layer/core/src/proto/transaction.proto index 0a14d6b557..4b19d94467 100644 --- a/base_layer/core/src/proto/transaction.proto +++ b/base_layer/core/src/proto/transaction.proto @@ -60,10 +60,8 @@ message TransactionOutput { bytes sender_offset_public_key = 5; // UTXO signature with the script offset private key, k_O ComSignature metadata_signature = 6; - // A unique id field that can be used to ensure there is only one of a specific NFT or token in the Unspent set at a time - bytes unique_id = 7; + // Numbering 7-8 taken - bytes parent_public_key = 8; } // Options for UTXO's @@ -76,9 +74,15 @@ message OutputFeatures { bytes metadata = 3; - AssetOutputFeatures asset = 16; - MintNonFungibleFeatures mint_non_fungible = 17; - SideChainCheckpointFeatures sidechain_checkpoint = 18; + AssetOutputFeatures asset = 4; + + optional bytes unique_id = 5; + + optional bytes parent_public_key = 6; + + MintNonFungibleFeatures mint_non_fungible = 7; + + SideChainCheckpointFeatures sidechain_checkpoint = 8; } message AssetOutputFeatures { diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index 40812fe3bd..a11a329c2c 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -170,18 +170,6 @@ impl TryFrom for TransactionOutput { .try_into() .map_err(|_| "Metadata signature could not be converted".to_string())?; - let unique_id = if output.unique_id.is_empty() { - None - } else { - Some(output.unique_id.clone()) - }; - - let parent_public_key = if output.parent_public_key.is_empty() { - None - } else { - Some(PublicKey::from_bytes(output.parent_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?) - }; - Ok(Self { features, commitment, @@ -189,8 +177,6 @@ impl TryFrom for TransactionOutput { script, sender_offset_public_key, metadata_signature, - unique_id, - parent_public_key, }) } } @@ -204,11 +190,6 @@ impl From for proto::types::TransactionOutput { script: output.script.as_bytes(), sender_offset_public_key: output.sender_offset_public_key.as_bytes().to_vec(), metadata_signature: Some(output.metadata_signature.into()), - unique_id: output.unique_id.unwrap_or_default(), - parent_public_key: output - .parent_public_key - .map(|pp| pp.as_bytes().to_vec()) - .unwrap_or_default(), } } } @@ -224,6 +205,11 @@ impl TryFrom for OutputFeatures { .ok_or_else(|| "Invalid or unrecognised output flags".to_string())?, maturity: features.maturity, metadata: features.metadata, + unique_id: features.unique_id, + parent_public_key: match features.parent_public_key { + Some(a) => Some(PublicKey::from_bytes(a.as_bytes()).map_err(|err| format!("{:?}", err))?), + None => None, + }, asset: match features.asset { Some(a) => Some(a.try_into()?), None => None, @@ -246,6 +232,11 @@ impl From for proto::types::OutputFeatures { flags: features.flags.bits() as u32, maturity: features.maturity, metadata: features.metadata, + unique_id: features.unique_id, + parent_public_key: match features.parent_public_key { + Some(a) => Some(a.as_bytes().to_vec()), + None => None, + }, asset: features.asset.map(|a| a.into()), mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index dd78f16104..ffcb65c5c8 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -208,8 +208,6 @@ impl CoinbaseBuilder { script_private_key, sender_offset_public_key, metadata_sig, - None, - None, ); // TODO: Verify bullet proof? let output = if let Some(rewind_data) = self.rewind_data.as_ref() { diff --git a/base_layer/core/src/transactions/helpers.rs b/base_layer/core/src/transactions/helpers.rs index c3e5907adc..54f90cebb8 100644 --- a/base_layer/core/src/transactions/helpers.rs +++ b/base_layer/core/src/transactions/helpers.rs @@ -147,8 +147,6 @@ impl TestParams { self.script_private_key.clone(), self.sender_offset_public_key.clone(), metadata_signature, - None, - None, ) } @@ -508,8 +506,6 @@ pub fn spend_utxos(schema: TransactionSchema) -> (Transaction, Vec, + pub unique_id: Option>, + pub parent_public_key: Option, pub asset: Option, pub mint_non_fungible: Option, pub sidechain_checkpoint: Option, @@ -200,6 +202,8 @@ impl Default for OutputFeatures { flags: OutputFlags::empty(), maturity: 0, metadata: vec![], + unique_id: None, + parent_public_key: None, asset: None, mint_non_fungible: None, sidechain_checkpoint: None, @@ -291,8 +295,6 @@ pub struct UnblindedOutputBuilder { metadata_signature: Option, metadata_signed_by_receiver: bool, metadata_signed_by_sender: bool, - unique_id: Option>, - parent_public_key: Option, } impl UnblindedOutputBuilder { @@ -308,8 +310,6 @@ impl UnblindedOutputBuilder { metadata_signature: None, metadata_signed_by_receiver: false, metadata_signed_by_sender: false, - unique_id: None, - parent_public_key: None, } } @@ -380,8 +380,6 @@ impl UnblindedOutputBuilder { metadata_signature: self .metadata_signature .ok_or_else(|| TransactionError::ValidationError("metadata_signature must be set".to_string()))?, - unique_id: self.unique_id, - parent_public_key: self.parent_public_key, }; Ok(ub) } @@ -407,12 +405,12 @@ impl UnblindedOutputBuilder { } pub fn with_unique_id(mut self, unique_id: Option>) -> Self { - self.unique_id = unique_id; + self.features.unique_id = unique_id; self } pub fn with_parent_public_key(mut self, parent_public_key: Option) -> Self { - self.parent_public_key = parent_public_key; + self.features.parent_public_key = parent_public_key; self } } @@ -430,8 +428,6 @@ pub struct UnblindedOutput { pub script_private_key: PrivateKey, pub sender_offset_public_key: PublicKey, pub metadata_signature: ComSignature, - pub unique_id: Option>, - pub parent_public_key: Option, } impl UnblindedOutput { @@ -446,8 +442,6 @@ impl UnblindedOutput { script_private_key: PrivateKey, sender_offset_public_key: PublicKey, metadata_signature: ComSignature, - unique_id: Option>, - parent_public_key: Option, ) -> UnblindedOutput { UnblindedOutput { value, @@ -458,8 +452,6 @@ impl UnblindedOutput { script_private_key, sender_offset_public_key, metadata_signature, - unique_id, - parent_public_key, } } @@ -515,8 +507,6 @@ impl UnblindedOutput { script: self.script.clone(), sender_offset_public_key: self.sender_offset_public_key.clone(), metadata_signature: self.metadata_signature.clone(), - unique_id: self.unique_id.clone(), - parent_public_key: self.parent_public_key.clone(), }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. if verify_proof && !output.verify_range_proof(&factories.range_proof)? { @@ -553,8 +543,6 @@ impl UnblindedOutput { script: self.script.clone(), sender_offset_public_key: self.sender_offset_public_key.clone(), metadata_signature: self.metadata_signature.clone(), - unique_id: self.unique_id.clone(), - parent_public_key: self.parent_public_key.clone(), }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. if verify_proof && !output.verify_range_proof(&factories.range_proof)? { @@ -784,10 +772,6 @@ pub struct TransactionOutput { pub sender_offset_public_key: PublicKey, /// UTXO signature with the script offset private key, k_O pub metadata_signature: ComSignature, - /// Unique id. There can only be one UTXO at a time in the unspent set with this id - pub unique_id: Option>, - /// Public key if this has a parent (e.g. tokens or sub assets) - pub parent_public_key: Option, } /// An output for a transaction, includes a range proof and Tari script metadata @@ -800,8 +784,6 @@ impl TransactionOutput { script: TariScript, sender_offset_public_key: PublicKey, metadata_signature: ComSignature, - unique_id: Option>, - parent_public_key: Option, ) -> TransactionOutput { TransactionOutput { features, @@ -810,8 +792,6 @@ impl TransactionOutput { script, sender_offset_public_key, metadata_signature, - unique_id, - parent_public_key, } } diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto index eaa4625bd7..860317c3b5 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto @@ -28,7 +28,7 @@ message SingleRoundSenderData { // Output features tari.types.OutputFeatures features = 10; // Unique id for NFTs - bytes unique_id = 11; + // bytes unique_id = 11; } message TransactionSenderMessage { diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs index e2e16adb05..a7be789752 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs @@ -106,11 +106,6 @@ impl TryFrom for SingleRoundSenderData { .map(TryInto::try_into) .ok_or_else(|| "Transaction output features not provided".to_string())??; - let unique_id = if data.unique_id.is_empty() { - None - } else { - Some(data.unique_id.clone()) - }; Ok(Self { tx_id: data.tx_id.into(), amount: data.amount.into(), @@ -122,7 +117,6 @@ impl TryFrom for SingleRoundSenderData { script: TariScript::from_bytes(&data.script).map_err(|err| err.to_string())?, sender_offset_public_key, public_commitment_nonce, - unique_id, }) } } @@ -142,7 +136,6 @@ impl From for proto::SingleRoundSenderData { script: sender_data.script.as_bytes(), sender_offset_public_key: sender_data.sender_offset_public_key.to_vec(), public_commitment_nonce: sender_data.public_commitment_nonce.to_vec(), - unique_id: sender_data.unique_id.unwrap_or_default(), } } } diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 523408a086..063d4ec6ee 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -123,8 +123,6 @@ pub struct SingleRoundSenderData { pub sender_offset_public_key: PublicKey, /// The sender's portion of the public commitment nonce pub public_commitment_nonce: PublicKey, - /// Unique id on the blockchain, if present - pub unique_id: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -374,7 +372,6 @@ impl SenderTransactionProtocol { script: recipient_script, sender_offset_public_key: PublicKey::from_secret_key(recipient_script_offset_secret_key), public_commitment_nonce: PublicKey::from_secret_key(&private_commitment_nonce), - unique_id: info.unique_id.clone(), }) }, _ => Err(TPE::InvalidStateError), diff --git a/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs index c180817222..cc04ce1229 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs @@ -333,8 +333,6 @@ impl SenderTransactionProtocolBuilder { .clone(), PublicKey::from_secret_key(&change_sender_offset_private_key), metadata_signature, - None, - None, ); Ok((fee_with_change, v, Some(change_unblinded_output))) }, diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 07c86d2e38..7f6060fed6 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -128,8 +128,6 @@ impl SingleReceiverTransactionProtocol { sender_info.script.clone(), sender_info.sender_offset_public_key.clone(), partial_metadata_signature, - None, - None, ); Ok(output) } diff --git a/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql index 0c42be0c8b..416f507d5f 100644 --- a/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql +++ b/base_layer/wallet/migrations/2021-07-07-121212_add_unique_id/up.sql @@ -6,5 +6,5 @@ alter table outputs add features_asset_public_key blob; alter table outputs add features_mint_asset_public_key blob; alter table outputs add features_mint_asset_owner_commitment blob; alter table outputs add features_sidechain_checkpoint_merkle_root blob; -alter table outputs add parent_public_key blob; -alter table inbound_transactions add unique_id blob; +alter table outputs add features_parent_public_key blob; +alter table outputs add features_unique_id blob; diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index 2add294cd8..1da885d7f1 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -86,21 +86,11 @@ where TBackend: OutputManagerBackend + 'static output.script, output.sender_offset_public_key, output.metadata_signature, - output.unique_id, - output.parent_public_key, ) }) }) .map( - |( - output, - features, - script, - sender_offset_public_key, - metadata_signature, - unique_id, - parent_public_key, - )| { + |(output, features, script, sender_offset_public_key, metadata_signature)| { UnblindedOutput::new( output.committed_value, output.blinding_factor.clone(), @@ -110,8 +100,6 @@ where TBackend: OutputManagerBackend + 'static output.blinding_factor, sender_offset_public_key, metadata_signature, - unique_id, - parent_public_key, ) }, ) diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 444e765373..9376ce88c8 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -521,9 +521,6 @@ where TBackend: OutputManagerBackend + 'static &single_round_sender_data.sender_offset_public_key.clone(), &single_round_sender_data.public_commitment_nonce.clone(), )?, - single_round_sender_data.unique_id.clone(), - // TODO: put data in - None, ), &self.resources.factories, )?; @@ -934,7 +931,8 @@ where TBackend: OutputManagerBackend + 'static } let script = script!(Nop); - let output_features = OutputFeatures::default(); + let mut output_features = OutputFeatures::default(); + output_features.unique_id = unique_id.clone(); let (spending_key, script_private_key) = self .resources .master_key_manager @@ -957,8 +955,6 @@ where TBackend: OutputManagerBackend + 'static script_private_key, PublicKey::from_secret_key(&sender_offset_private_key), metadata_signature, - unique_id.clone(), - None, ), &self.resources.factories, )?; @@ -1373,8 +1369,6 @@ where TBackend: OutputManagerBackend + 'static script_private_key, sender_offset_public_key, metadata_signature, - None, - None, ), &self.resources.factories, )?; @@ -1457,8 +1451,6 @@ where TBackend: OutputManagerBackend + 'static known_one_sided_payment_scripts[i].private_key.clone(), output.sender_offset_public_key, output.metadata_signature, - output.unique_id, - output.parent_public_key, ); let db_output = DbUnblindedOutput::from_unblinded_output(rewound_output.clone(), &self.resources.factories)?; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 7685bc31ae..757ca4dc2c 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -944,10 +944,16 @@ impl TryFrom for DbUnblindedOutput { }), None => None, }; + let features = OutputFeatures { flags: OutputFlags::from_bits(o.flags as u8).ok_or(OutputManagerStorageError::ConversionError)?, maturity: o.maturity as u64, metadata: o.metadata.unwrap_or_default(), + unique_id: o.features_unique_id.clone(), + parent_public_key: o + .features_parent_public_key + .map(|p| PublicKey::from_bytes(&p)) + .transpose()?, asset: asset_features, mint_non_fungible, sidechain_checkpoint, @@ -1001,8 +1007,6 @@ impl TryFrom for DbUnblindedOutput { OutputManagerStorageError::ConversionError })?, ), - o.unique_id.clone(), - o.parent_public_key.map(|p| PublicKey::from_bytes(&p)).transpose()?, ); let hash = match o.hash { diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index 8ef669d227..2a6f7f302d 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -52,8 +52,8 @@ pub struct NewOutputSql { script_private_key: Vec, metadata: Option>, features_asset_public_key: Option>, - unique_id: Option>, - parent_public_key: Option>, + features_unique_id: Option>, + features_parent_public_key: Option>, sender_offset_public_key: Vec, metadata_signature_nonce: Vec, metadata_signature_u_key: Vec, @@ -76,8 +76,8 @@ impl NewOutputSql { script_private_key: output.unblinded_output.script_private_key.to_vec(), metadata: Some(output.unblinded_output.features.metadata), features_asset_public_key: output.unblinded_output.features.asset.map(|a| a.public_key.to_vec()), - unique_id: output.unblinded_output.unique_id, - parent_public_key: output.unblinded_output.parent_public_key.map(|a| a.to_vec()), + features_unique_id: output.unblinded_output.features.unique_id, + features_parent_public_key: output.unblinded_output.features.parent_public_key.map(|a| a.to_vec()), sender_offset_public_key: output.unblinded_output.sender_offset_public_key.to_vec(), metadata_signature_nonce: output.unblinded_output.metadata_signature.public_nonce().to_vec(), metadata_signature_u_key: output.unblinded_output.metadata_signature.u().to_vec(), diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 61e43a029f..e9c3e1b526 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -32,13 +32,13 @@ pub struct OutputSql { pub metadata_signature_nonce: Vec, pub metadata_signature_u_key: Vec, pub metadata_signature_v_key: Vec, - pub unique_id: Option>, pub metadata: Option>, pub features_asset_public_key: Option>, pub features_mint_asset_public_key: Option>, pub features_mint_asset_owner_commitment: Option>, pub features_sidechain_checkpoint_merkle_root: Option>, - pub parent_public_key: Option>, + pub features_unique_id: Option>, + pub features_parent_public_key: Option>, } impl OutputSql { diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index efd96802da..d2fb847abd 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -24,7 +24,6 @@ table! { valid -> Integer, confirmations -> Nullable, mined_height -> Nullable, - unique_id -> Nullable, } } @@ -47,7 +46,6 @@ table! { direct_send_success -> Integer, send_count -> Integer, last_send_timestamp -> Nullable, - unique_id -> Nullable, } } @@ -83,7 +81,6 @@ table! { direct_send_success -> Integer, send_count -> Integer, last_send_timestamp -> Nullable, - unique_id -> Nullable, } } @@ -105,13 +102,13 @@ table! { metadata_signature_nonce -> Binary, metadata_signature_u_key -> Binary, metadata_signature_v_key -> Binary, - unique_id -> Nullable, metadata -> Nullable, features_asset_public_key -> Nullable, features_mint_asset_public_key -> Nullable, features_mint_asset_owner_commitment -> Nullable, features_sidechain_checkpoint_merkle_root -> Nullable, - parent_public_key -> Nullable, + features_parent_public_key -> Nullable, + features_unique_id -> Nullable, } } diff --git a/base_layer/wallet/src/tokens/token_manager.rs b/base_layer/wallet/src/tokens/token_manager.rs index ca81c1b968..5c41cc7a46 100644 --- a/base_layer/wallet/src/tokens/token_manager.rs +++ b/base_layer/wallet/src/tokens/token_manager.rs @@ -65,7 +65,7 @@ impl TokenManager { .into_iter() .filter(|ub| { // Filter out asset registrations that don't have a parent pub key - ub.unblinded_output.parent_public_key.is_some() + ub.unblinded_output.features.parent_public_key.is_some() }) .map(|unblinded_output| convert_to_token(unblinded_output)) .collect::>()?; @@ -81,12 +81,13 @@ fn convert_to_token(unblinded_output: DbUnblindedOutput) -> Result Result>, pub receiver_protocol: ReceiverTransactionProtocol, pub status: TransactionStatus, pub message: String, @@ -117,7 +116,6 @@ impl InboundTransaction { tx_id: TxId, source_public_key: CommsPublicKey, amount: MicroTari, - unique_id: Option>, receiver_protocol: ReceiverTransactionProtocol, status: TransactionStatus, message: String, @@ -127,7 +125,6 @@ impl InboundTransaction { tx_id, source_public_key, amount, - unique_id, receiver_protocol, status, message, @@ -154,7 +151,6 @@ pub struct OutboundTransaction { pub direct_send_success: bool, pub send_count: u32, pub last_send_timestamp: Option, - pub unique_id: Option>, } impl OutboundTransaction { @@ -163,7 +159,6 @@ impl OutboundTransaction { tx_id: TxId, destination_public_key: CommsPublicKey, amount: MicroTari, - unique_id: Option>, fee: MicroTari, sender_protocol: SenderTransactionProtocol, status: TransactionStatus, @@ -175,7 +170,6 @@ impl OutboundTransaction { tx_id, destination_public_key, amount, - unique_id, fee, sender_protocol, status, @@ -195,7 +189,6 @@ pub struct CompletedTransaction { pub source_public_key: CommsPublicKey, pub destination_public_key: CommsPublicKey, pub amount: MicroTari, - pub unique_id: Option>, pub fee: MicroTari, pub transaction: Transaction, pub status: TransactionStatus, @@ -218,7 +211,6 @@ impl CompletedTransaction { source_public_key: CommsPublicKey, destination_public_key: CommsPublicKey, amount: MicroTari, - unique_id: Option>, fee: MicroTari, transaction: Transaction, status: TransactionStatus, @@ -232,7 +224,6 @@ impl CompletedTransaction { source_public_key, destination_public_key, amount, - unique_id, fee, transaction, status, @@ -289,7 +280,6 @@ impl From for InboundTransaction { tx_id: ct.tx_id, source_public_key: ct.source_public_key, amount: ct.amount, - unique_id: ct.unique_id, receiver_protocol: ReceiverTransactionProtocol::new_placeholder(), status: ct.status, message: ct.message, @@ -308,7 +298,6 @@ impl From for OutboundTransaction { tx_id: ct.tx_id, destination_public_key: ct.destination_public_key, amount: ct.amount, - unique_id: ct.unique_id.clone(), fee: ct.fee, sender_protocol: SenderTransactionProtocol::new_placeholder(), status: ct.status, @@ -329,7 +318,6 @@ impl From for CompletedTransaction { source_public_key: Default::default(), destination_public_key: tx.destination_public_key, amount: tx.amount, - unique_id: tx.unique_id, fee: tx.fee, status: tx.status, message: tx.message, @@ -354,7 +342,6 @@ impl From for CompletedTransaction { source_public_key: tx.source_public_key, destination_public_key: Default::default(), amount: tx.amount, - unique_id: tx.unique_id, fee: MicroTari::from(0), status: tx.status, message: tx.message, diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs index 4bd40b1145..ee2a49673b 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -928,7 +928,6 @@ struct InboundTransactionSql { direct_send_success: i32, send_count: i32, last_send_timestamp: Option, - unique_id: Option>, } impl InboundTransactionSql { @@ -1055,7 +1054,6 @@ impl TryFrom for InboundTransactionSql { tx_id: i.tx_id.as_u64() as i64, source_public_key: i.source_public_key.to_vec(), amount: u64::from(i.amount) as i64, - unique_id: i.unique_id, receiver_protocol: serde_json::to_string(&i.receiver_protocol)?, message: i.message, timestamp: i.timestamp, @@ -1076,7 +1074,6 @@ impl TryFrom for InboundTransaction { source_public_key: PublicKey::from_vec(&i.source_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid Source Publickey".to_string()))?, amount: MicroTari::from(i.amount as u64), - unique_id: i.unique_id, receiver_protocol: serde_json::from_str(&i.receiver_protocol)?, status: TransactionStatus::Pending, message: i.message, @@ -1114,7 +1111,6 @@ struct OutboundTransactionSql { direct_send_success: i32, send_count: i32, last_send_timestamp: Option, - unique_id: Option>, } impl OutboundTransactionSql { @@ -1249,7 +1245,6 @@ impl TryFrom for OutboundTransactionSql { direct_send_success: o.direct_send_success as i32, send_count: o.send_count as i32, last_send_timestamp: o.last_send_timestamp, - unique_id: o.unique_id, }) } } @@ -1263,7 +1258,6 @@ impl TryFrom for OutboundTransaction { destination_public_key: PublicKey::from_vec(&o.destination_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid destination PublicKey".to_string()))?, amount: MicroTari::from(o.amount as u64), - unique_id: o.unique_id, fee: MicroTari::from(o.fee as u64), sender_protocol: serde_json::from_str(&o.sender_protocol)?, status: TransactionStatus::Pending, @@ -1308,7 +1302,6 @@ struct CompletedTransactionSql { valid: i32, confirmations: Option, mined_height: Option, - unique_id: Option>, } impl CompletedTransactionSql { @@ -1568,7 +1561,6 @@ impl TryFrom for CompletedTransactionSql { source_public_key: c.source_public_key.to_vec(), destination_public_key: c.destination_public_key.to_vec(), amount: u64::from(c.amount) as i64, - unique_id: c.unique_id, fee: u64::from(c.fee) as i64, transaction_protocol: serde_json::to_string(&c.transaction)?, status: c.status as i32, @@ -1597,7 +1589,6 @@ impl TryFrom for CompletedTransaction { destination_public_key: PublicKey::from_vec(&c.destination_public_key) .map_err(|_| TransactionStorageError::ConversionError("Invalid destination PublicKey".to_string()))?, amount: MicroTari::from(c.amount as u64), - unique_id: c.unique_id, fee: MicroTari::from(c.fee as u64), transaction: serde_json::from_str(&c.transaction_protocol)?, status: TransactionStatus::try_from(c.status)?, diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 19cd0a9dfa..fbeeb9a25b 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -404,8 +404,6 @@ where sender_offset_public_key.clone(), metadata_signature, // TODO: Allow importing of unique ids - None, - None, ); let tx_id = self From 2184bfb3cc54265d17054d2f3fae9d47786a5b2f Mon Sep 17 00:00:00 2001 From: Martin Stefcek Date: Thu, 16 Sep 2021 10:15:02 +0200 Subject: [PATCH 07/77] fix: minting tokens --- .../tari_console_wallet/src/automation/commands.rs | 13 ++++++------- .../storage/sqlite_db/new_output_sql.rs | 4 ++-- .../storage/sqlite_db/output_sql.rs | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index 2bcbffdf64..17f743d117 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -709,13 +709,12 @@ pub async fn command_runner( .iter() .map(|arg| { let s = arg.to_string(); - match &s[0..2] { - "0x" => { - let s = s[2..].to_string(); - let r: Vec = Hex::from_hex(&s).unwrap(); - r - }, - _ => s.into_bytes(), + if s.starts_with("0x") { + let s = s[2..].to_string(); + let r: Vec = Hex::from_hex(&s).unwrap(); + r + } else { + s.into_bytes() } }) .collect(); diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index 2a6f7f302d..fa9f3fa60e 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -52,8 +52,8 @@ pub struct NewOutputSql { script_private_key: Vec, metadata: Option>, features_asset_public_key: Option>, - features_unique_id: Option>, features_parent_public_key: Option>, + features_unique_id: Option>, sender_offset_public_key: Vec, metadata_signature_nonce: Vec, metadata_signature_u_key: Vec, @@ -76,8 +76,8 @@ impl NewOutputSql { script_private_key: output.unblinded_output.script_private_key.to_vec(), metadata: Some(output.unblinded_output.features.metadata), features_asset_public_key: output.unblinded_output.features.asset.map(|a| a.public_key.to_vec()), - features_unique_id: output.unblinded_output.features.unique_id, features_parent_public_key: output.unblinded_output.features.parent_public_key.map(|a| a.to_vec()), + features_unique_id: output.unblinded_output.features.unique_id, sender_offset_public_key: output.unblinded_output.sender_offset_public_key.to_vec(), metadata_signature_nonce: output.unblinded_output.metadata_signature.public_nonce().to_vec(), metadata_signature_u_key: output.unblinded_output.metadata_signature.u().to_vec(), diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index e9c3e1b526..850e34097d 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -37,8 +37,8 @@ pub struct OutputSql { pub features_mint_asset_public_key: Option>, pub features_mint_asset_owner_commitment: Option>, pub features_sidechain_checkpoint_merkle_root: Option>, - pub features_unique_id: Option>, pub features_parent_public_key: Option>, + pub features_unique_id: Option>, } impl OutputSql { From 2997e0ec74aef81281ccc8451de0b612793e9c76 Mon Sep 17 00:00:00 2001 From: Martin Stefcek <35243812+Cifko@users.noreply.github.com> Date: Mon, 20 Sep 2021 11:39:16 +0200 Subject: [PATCH 08/77] feat: send token (#3346) --- .../src/ui/components/send_tab.rs | 191 ++++++++++++++---- .../src/ui/components/transactions_tab.rs | 55 ++++- .../src/ui/state/app_state.rs | 4 + .../tari_console_wallet/src/ui/state/tasks.rs | 6 +- .../transaction_protocol/sender.rs | 1 - .../sender_transaction_protocol_builder.rs | 17 +- .../transaction_protocol/single_receiver.rs | 2 +- .../src/output_manager_service/error.rs | 2 + .../src/output_manager_service/service.rs | 60 ++++-- .../storage/sqlite_db/new_output_sql.rs | 13 ++ .../protocols/transaction_send_protocol.rs | 5 +- .../wallet/src/transaction_service/service.rs | 2 + .../src/transaction_service/storage/models.rs | 32 ++- 13 files changed, 306 insertions(+), 84 deletions(-) diff --git a/applications/tari_console_wallet/src/ui/components/send_tab.rs b/applications/tari_console_wallet/src/ui/components/send_tab.rs index 2849ae00bb..c70864d57e 100644 --- a/applications/tari_console_wallet/src/ui/components/send_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/send_tab.rs @@ -1,21 +1,21 @@ use crate::{ ui::{ - components::{balance::Balance, Component, KeyHandled}, + components::{balance::Balance, styles, Component, KeyHandled}, state::{AppState, UiTransactionSendStatus}, widgets::{centered_rect_absolute, draw_dialog, MultiColumnList, WindowedListState}, MAX_WIDTH, }, utils::formatting::display_compressed_string, }; -use tari_core::transactions::tari_amount::MicroTari; -use tari_wallet::types::DEFAULT_FEE_PER_GRAM; +use tari_core::{tari_utilities::hex::Hex, transactions::tari_amount::MicroTari}; +use tari_wallet::{tokens::Token, types::DEFAULT_FEE_PER_GRAM}; use tokio::{runtime::Handle, sync::watch}; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, text::{Span, Spans}, - widgets::{Block, Borders, Clear, ListItem, Paragraph, Wrap}, + widgets::{Block, Borders, Clear, ListItem, Paragraph, Row, Table, TableState, Wrap}, Frame, }; use unicode_width::UnicodeWidthStr; @@ -37,6 +37,8 @@ pub struct SendTab { contacts_list_state: WindowedListState, send_result_watch: Option>, confirmation_dialog: Option, + selected_unique_id: Option>, + table_state: TableState, } impl SendTab { @@ -58,6 +60,8 @@ impl SendTab { contacts_list_state: WindowedListState::new(), send_result_watch: None, confirmation_dialog: None, + selected_unique_id: None, + table_state: TableState::default(), } } @@ -89,7 +93,7 @@ impl SendTab { Span::raw(" field, "), Span::styled("A", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" to edit "), - Span::styled("Amount", Style::default().add_modifier(Modifier::BOLD)), + Span::styled("Amount/Token", Style::default().add_modifier(Modifier::BOLD)), Span::raw(", "), Span::styled("F", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" to edit "), @@ -127,12 +131,19 @@ impl SendTab { .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(vert_chunks[2]); - let amount_input = Paragraph::new(self.amount_field.as_ref()) - .style(match self.send_input_mode { - SendInputMode::Amount => Style::default().fg(Color::Magenta), - _ => Style::default(), - }) - .block(Block::default().borders(Borders::ALL).title("(A)mount (uT or T):")); + let amount_input = Paragraph::new(match &self.selected_unique_id { + Some(token) => format!("Token selected : {}", token.to_hex()), + None => format!("{}", self.amount_field), + }) + .style(match self.send_input_mode { + SendInputMode::Amount => Style::default().fg(Color::Magenta), + _ => Style::default(), + }) + .block( + Block::default() + .borders(Borders::ALL) + .title("(A)mount (uT or T) or select Token:"), + ); f.render_widget(amount_input, amount_fee_layout[0]); let fee_input = Paragraph::new(self.fee_field.as_ref()) @@ -159,12 +170,16 @@ impl SendTab { // Move one line down, from the border to the input line vert_chunks[1].y + 1, ), - SendInputMode::Amount => f.set_cursor( - // Put cursor past the end of the input text - amount_fee_layout[0].x + self.amount_field.width() as u16 + 1, - // Move one line down, from the border to the input line - amount_fee_layout[0].y + 1, - ), + SendInputMode::Amount => { + if self.selected_unique_id.is_none() { + f.set_cursor( + // Put cursor past the end of the input text + amount_fee_layout[0].x + self.amount_field.width() as u16 + 1, + // Move one line down, from the border to the input line + amount_fee_layout[0].y + 1, + ) + } + }, SendInputMode::Fee => f.set_cursor( // Put cursor past the end of the input text amount_fee_layout[1].x + self.fee_field.width() as u16 + 1, @@ -301,6 +316,50 @@ impl SendTab { } } + fn draw_tokens(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) + where B: Backend { + let tokens = app_state.get_owned_tokens(); + + let tokens: Vec<_> = tokens + .iter() + .filter(|&token| token.output_status() == "Unspent") + .map(|r| { + ( + r.name().to_string(), + r.output_status().to_string(), + r.asset_public_key().to_hex(), + Vec::from(r.unique_id()).to_hex(), + r.owner_commitment().to_hex(), + ) + }) + .collect(); + let rows: Vec<_> = tokens + .iter() + .map(|v| { + Row::new(vec![ + v.0.as_str(), + v.1.as_str(), + v.2.as_str(), + v.3.as_str(), + v.4.as_str(), + ]) + }) + .collect(); + let table = Table::new(rows) + .header(Row::new(vec!["Name", "Status", "Asset Pub Key", "Unique ID", "Owner"]).style(styles::header_row())) + .block(Block::default().title("Tokens").borders(Borders::ALL)) + .widths(&[ + Constraint::Length(30), + Constraint::Length(20), + Constraint::Length(32), + Constraint::Length(32), + Constraint::Length(64), + ]) + .highlight_style(styles::highlight()) + .highlight_symbol(">>"); + f.render_stateful_widget(table, area, &mut self.table_state) + } + fn on_key_confirmation_dialog(&mut self, c: char, app_state: &mut AppState) -> KeyHandled { if self.confirmation_dialog.is_some() { if 'n' == c { @@ -319,9 +378,12 @@ impl SendTab { let amount = if let Ok(v) = self.amount_field.parse::() { v } else { - self.error_message = - Some("Amount should be an integer\nPress Enter to continue.".to_string()); - return KeyHandled::Handled; + if self.selected_unique_id.is_none() { + self.error_message = + Some("Amount should be an integer\nPress Enter to continue.".to_string()); + return KeyHandled::Handled; + } + MicroTari::from(0) }; let fee_per_gram = if let Ok(v) = self.fee_field.parse::() { @@ -339,6 +401,7 @@ impl SendTab { match Handle::current().block_on(app_state.send_one_sided_transaction( self.to_field.clone(), amount.into(), + self.selected_unique_id.clone(), fee_per_gram, self.message_field.clone(), tx, @@ -355,6 +418,7 @@ impl SendTab { match Handle::current().block_on(app_state.send_transaction( self.to_field.clone(), amount.into(), + self.selected_unique_id.clone(), fee_per_gram, self.message_field.clone(), tx, @@ -371,6 +435,7 @@ impl SendTab { if reset_fields { self.to_field = "".to_string(); self.amount_field = "".to_string(); + self.selected_unique_id = None; self.fee_field = u64::from(DEFAULT_FEE_PER_GRAM).to_string(); self.message_field = "".to_string(); self.send_input_mode = SendInputMode::None; @@ -409,20 +474,25 @@ impl SendTab { match self.send_input_mode { SendInputMode::None => (), SendInputMode::To => match c { - '\n' => { - self.send_input_mode = SendInputMode::Amount; - }, + '\n' => self.send_input_mode = SendInputMode::Amount, c => { self.to_field.push(c); return KeyHandled::Handled; }, }, SendInputMode::Amount => match c { - '\n' => self.send_input_mode = SendInputMode::Message, + '\n' => { + if self.selected_unique_id.is_some() { + self.amount_field = "".to_string(); + } + self.send_input_mode = SendInputMode::Message + }, c => { - let symbols = &['t', 'T', 'u', 'U']; - if c.is_numeric() || symbols.contains(&c) { - self.amount_field.push(c); + if self.selected_unique_id.is_none() { + let symbols = &['t', 'T', 'u', 'U']; + if c.is_numeric() || symbols.contains(&c) { + self.amount_field.push(c); + } } return KeyHandled::Handled; }, @@ -547,6 +617,10 @@ impl Component for SendTab { } }; + if self.send_input_mode == SendInputMode::Amount { + self.draw_tokens(f, areas[2], app_state); + } + let rx_option = self.send_result_watch.take(); if let Some(rx) = rx_option { let status = match (*rx.borrow()).clone() { @@ -672,7 +746,6 @@ impl Component for SendTab { self.send_input_mode = SendInputMode::None; } }, - 'e' => { if let Some(c) = self .contacts_list_state @@ -688,21 +761,25 @@ impl Component for SendTab { } }, 't' => self.send_input_mode = SendInputMode::To, - 'a' => self.send_input_mode = SendInputMode::Amount, + 'a' => { + self.send_input_mode = SendInputMode::Amount; + }, 'f' => self.send_input_mode = SendInputMode::Fee, 'm' => self.send_input_mode = SendInputMode::Message, 's' | 'o' => { - if self.amount_field.is_empty() || self.to_field.is_empty() { - self.error_message = Some( - "Destination Public Key/Emoji ID and Amount required\nPress Enter to continue.".to_string(), - ); + if self.to_field.is_empty() { + self.error_message = Some("Destination Public Key/Emoji ID\nPress Enter to continue.".to_string()); + return; + } + if self.amount_field.is_empty() && self.selected_unique_id.is_none() { + self.error_message = Some("Amount or token required\nPress Enter to continue.".to_string()); return; } - if self.amount_field.parse::().is_err() { + if self.amount_field.parse::().is_err() && self.selected_unique_id.is_none() { self.error_message = Some("Amount should be a valid amount of Tari\nPress Enter to continue.".to_string()); return; - }; + } if matches!(c, 'o') { self.confirmation_dialog = Some(ConfirmationDialogType::ConfirmOneSidedSend); @@ -715,13 +792,45 @@ impl Component for SendTab { } fn on_up(&mut self, app_state: &mut AppState) { - self.contacts_list_state.set_num_items(app_state.get_contacts().len()); - self.contacts_list_state.previous(); + if self.show_contacts { + self.contacts_list_state.set_num_items(app_state.get_contacts().len()); + self.contacts_list_state.previous(); + } else if self.send_input_mode == SendInputMode::Amount { + let index = self.table_state.selected().unwrap_or_default(); + if index == 0 { + self.table_state.select(None); + self.selected_unique_id = None; + } else { + let tokens: Vec<&Token> = app_state + .get_owned_tokens() + .into_iter() + .filter(|&token| token.output_status() == "Unspent") + .collect(); + self.selected_unique_id = Some(Vec::from(tokens[index - 1].unique_id())); + self.table_state.select(Some(index - 1)); + } + } } fn on_down(&mut self, app_state: &mut AppState) { - self.contacts_list_state.set_num_items(app_state.get_contacts().len()); - self.contacts_list_state.next(); + if self.show_contacts { + self.contacts_list_state.set_num_items(app_state.get_contacts().len()); + self.contacts_list_state.next(); + } else if self.send_input_mode == SendInputMode::Amount { + let index = self.table_state.selected().map(|s| s + 1).unwrap_or_default(); + let tokens: Vec<&Token> = app_state + .get_owned_tokens() + .into_iter() + .filter(|&token| token.output_status() == "Unspent") + .collect(); + if index > tokens.len().saturating_sub(1) { + self.table_state.select(None); + self.selected_unique_id = None; + } else { + self.selected_unique_id = Some(Vec::from(tokens[index].unique_id())); + self.table_state.select(Some(index)); + } + } } fn on_esc(&mut self, _: &mut AppState) { @@ -737,7 +846,9 @@ impl Component for SendTab { let _ = self.to_field.pop(); }, SendInputMode::Amount => { - let _ = self.amount_field.pop(); + if self.selected_unique_id.is_none() { + let _ = self.amount_field.pop(); + } }, SendInputMode::Fee => { let _ = self.fee_field.pop(); diff --git a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs index 8326ebe865..f7f3962a0a 100644 --- a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs @@ -106,7 +106,13 @@ impl TransactionsTab { } else { Style::default().fg(Color::Red) }; - column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); + match t.get_unique_id() { + Some(unique_id) => column1_items.push(ListItem::new(Span::styled( + format!("Token : {}", unique_id), + amount_style, + ))), + None => column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))), + } } else { column0_items.push(ListItem::new(Span::styled( app_state.get_alias(&t.source_public_key), @@ -117,7 +123,13 @@ impl TransactionsTab { } else { Style::default().fg(Color::Green) }; - column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); + match t.get_unique_id() { + Some(unique_id) => column1_items.push(ListItem::new(Span::styled( + format!("Token : {}", unique_id), + amount_style, + ))), + None => column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))), + } } let local_time = DateTime::::from_utc(t.timestamp, Local::now().offset().to_owned()); column2_items.push(ListItem::new(Span::styled( @@ -135,7 +147,7 @@ impl TransactionsTab { .heading_style(styles::header_row()) .max_width(MAX_WIDTH) .add_column(Some("Source/Destination Public Key"), Some(67), column0_items) - .add_column(Some("Amount"), Some(18), column1_items) + .add_column(Some("Amount/Token"), Some(18), column1_items) .add_column(Some("Local Date/Time"), Some(20), column2_items) .add_column(Some("Message"), None, column3_items); column_list.render(f, area, &mut pending_list_state); @@ -191,7 +203,13 @@ impl TransactionsTab { } else { Style::default().fg(Color::Red) }; - column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); + match t.get_unique_id() { + Some(unique_id) => column1_items.push(ListItem::new(Span::styled( + format!("Token : {}", unique_id), + amount_style, + ))), + None => column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))), + } } else { column0_items.push(ListItem::new(Span::styled( app_state.get_alias(&t.source_public_key), @@ -211,7 +229,13 @@ impl TransactionsTab { _ => Color::Green, }; let amount_style = Style::default().fg(color); - column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); + match t.get_unique_id() { + Some(unique_id) => column1_items.push(ListItem::new(Span::styled( + format!("Token : {}", unique_id), + amount_style, + ))), + None => column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))), + } } let local_time = DateTime::::from_utc(t.timestamp, Local::now().offset().to_owned()); column2_items.push(ListItem::new(Span::styled( @@ -235,7 +259,7 @@ impl TransactionsTab { .heading_style(Style::default().fg(Color::Magenta)) .max_width(MAX_WIDTH) .add_column(Some("Source/Destination Public Key"), Some(67), column0_items) - .add_column(Some("Amount"), Some(18), column1_items) + .add_column(Some("Amount/Token"), Some(18), column1_items) .add_column(Some("Local Date/Time"), Some(20), column2_items) .add_column(Some("Status"), None, column3_items); @@ -264,7 +288,16 @@ impl TransactionsTab { let source_public_key = Span::styled("Source Public Key:", Style::default().fg(Color::Magenta)); let destination_public_key = Span::styled("Destination Public Key:", Style::default().fg(Color::Magenta)); let direction = Span::styled("Direction:", Style::default().fg(Color::Magenta)); - let amount = Span::styled("Amount:", Style::default().fg(Color::Magenta)); + let amount = Span::styled( + match self.detailed_transaction.as_ref() { + Some(tx) => match tx.get_unique_id() { + Some(_unique_id) => "Token:", + None => "Amount", + }, + None => "Amount/Token:", + }, + Style::default().fg(Color::Magenta), + ); let fee = Span::styled("Fee:", Style::default().fg(Color::Magenta)); let status = Span::styled("Status:", Style::default().fg(Color::Magenta)); let message = Span::styled("Message:", Style::default().fg(Color::Magenta)); @@ -325,7 +358,13 @@ impl TransactionsTab { ) }; let direction = Span::styled(format!("{}", tx.direction), Style::default().fg(Color::White)); - let amount = Span::styled(format!("{}", tx.amount), Style::default().fg(Color::White)); + let amount = Span::styled( + format!("{}", match tx.get_unique_id() { + Some(unique_id) => unique_id, + None => tx.amount.to_string(), + }), + Style::default().fg(Color::White), + ); let fee = Span::styled(format!("{}", tx.fee), Style::default().fg(Color::White)); let status_msg = if tx.cancelled { "Cancelled".to_string() diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index 56ad5aff98..444c8dede9 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -238,6 +238,7 @@ impl AppState { &mut self, public_key: String, amount: u64, + unique_id: Option>, fee_per_gram: u64, message: String, result_tx: watch::Sender, @@ -253,6 +254,7 @@ impl AppState { tokio::spawn(send_transaction_task( public_key, MicroTari::from(amount), + unique_id, message, fee_per_gram, tx_service_handle, @@ -266,6 +268,7 @@ impl AppState { &mut self, public_key: String, amount: u64, + unique_id: Option>, fee_per_gram: u64, message: String, result_tx: watch::Sender, @@ -281,6 +284,7 @@ impl AppState { tokio::spawn(send_one_sided_transaction_task( public_key, MicroTari::from(amount), + unique_id, message, fee_per_gram, tx_service_handle, diff --git a/applications/tari_console_wallet/src/ui/state/tasks.rs b/applications/tari_console_wallet/src/ui/state/tasks.rs index 0d39599dde..c753a69f15 100644 --- a/applications/tari_console_wallet/src/ui/state/tasks.rs +++ b/applications/tari_console_wallet/src/ui/state/tasks.rs @@ -31,6 +31,7 @@ const LOG_TARGET: &str = "wallet::console_wallet::tasks "; pub async fn send_transaction_task( public_key: CommsPublicKey, amount: MicroTari, + unique_id: Option>, message: String, fee_per_gram: MicroTari, mut transaction_service_handle: TransactionServiceHandle, @@ -41,7 +42,7 @@ pub async fn send_transaction_task( let mut send_direct_received_result = (false, false); let mut send_saf_received_result = (false, false); match transaction_service_handle - .send_transaction(public_key, amount, None, fee_per_gram, message) + .send_transaction(public_key, amount, unique_id, fee_per_gram, message) .await { Err(e) => { @@ -106,6 +107,7 @@ pub async fn send_transaction_task( pub async fn send_one_sided_transaction_task( public_key: CommsPublicKey, amount: MicroTari, + unique_id: Option>, message: String, fee_per_gram: MicroTari, mut transaction_service_handle: TransactionServiceHandle, @@ -114,7 +116,7 @@ pub async fn send_one_sided_transaction_task( let _ = result_tx.send(UiTransactionSendStatus::Initiated); let mut event_stream = transaction_service_handle.get_event_stream(); match transaction_service_handle - .send_one_sided_transaction(public_key, amount, None, fee_per_gram, message) + .send_one_sided_transaction(public_key, amount, unique_id, fee_per_gram, message) .await { Err(e) => { diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 063d4ec6ee..f59f3d94e2 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -98,7 +98,6 @@ pub(super) struct RawTransactionInfo { pub recipient_info: RecipientInfo, pub signatures: Vec, pub message: String, - pub unique_id: Option>, } #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] diff --git a/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs index cc04ce1229..2a154ab755 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs @@ -94,7 +94,6 @@ pub struct SenderTransactionProtocolBuilder { recipient_scripts: FixedSet, recipient_sender_offset_private_keys: FixedSet, private_commitment_nonces: FixedSet, - unique_id: Option>, tx_id: Option, } @@ -135,7 +134,6 @@ impl SenderTransactionProtocolBuilder { recipient_scripts: FixedSet::new(num_recipients), recipient_sender_offset_private_keys: FixedSet::new(num_recipients), private_commitment_nonces: FixedSet::new(num_recipients), - unique_id: None, tx_id: None, } } @@ -364,11 +362,6 @@ impl SenderTransactionProtocolBuilder { self.amounts.clone().into_vec().iter().sum() } - pub fn with_unique_id(mut self, unique_id: Vec) -> Self { - self.unique_id = Some(unique_id); - self - } - /// Construct a `SenderTransactionProtocol` instance in and appropriate state. The data stored /// in the struct is _moved_ into the new struct. If any data is missing, the `self` instance is returned in the /// error (so that you can continue building) along with a string listing the missing fields. @@ -521,10 +514,15 @@ impl SenderTransactionProtocolBuilder { None => calculate_tx_id::(&public_nonce, 0), }; + let recipient_output_features = self.recipient_output_features.clone().into_vec(); // The fee should be less than the amount being sent. This isn't a protocol requirement, but it's what you want // 99.999% of the time, however, always preventing this will also prevent spending dust in some edge // cases. - if self.amounts.size() > 0 && total_fee > self.calculate_amount_to_others() { + // Don't care about the fees when we are sending token. + if self.amounts.size() > 0 && + total_fee > self.calculate_amount_to_others() && + recipient_output_features[0].unique_id.is_none() + { warn!( target: LOG_TARGET, "Fee ({}) is greater than amount ({}) being sent for Transaction (TxId: {}).", @@ -548,7 +546,7 @@ impl SenderTransactionProtocolBuilder { amount_to_self, tx_id, amounts: self.amounts.into_vec(), - recipient_output_features: self.recipient_output_features.into_vec(), + recipient_output_features, recipient_scripts: self.recipient_scripts.into_vec(), recipient_sender_offset_private_keys: self.recipient_sender_offset_private_keys.into_vec(), private_commitment_nonces: self.private_commitment_nonces.into_vec(), @@ -574,7 +572,6 @@ impl SenderTransactionProtocolBuilder { recipient_info, signatures: Vec::new(), message: self.message.unwrap_or_else(|| "".to_string()), - unique_id: self.unique_id, }; let state = SenderState::Initializing(Box::new(sender_info)); diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 7f6060fed6..45c6a89685 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -80,7 +80,7 @@ impl SingleReceiverTransactionProtocol { /// Validates the sender info fn validate_sender_data(sender_info: &SD) -> Result<(), TPE> { - if sender_info.amount == 0.into() { + if sender_info.amount == 0.into() && sender_info.features.unique_id.is_none() { return Err(TPE::ValidationError("Cannot send zero microTari".into())); } Ok(()) diff --git a/base_layer/wallet/src/output_manager_service/error.rs b/base_layer/wallet/src/output_manager_service/error.rs index e2a466d409..d3235c9d52 100644 --- a/base_layer/wallet/src/output_manager_service/error.rs +++ b/base_layer/wallet/src/output_manager_service/error.rs @@ -109,6 +109,8 @@ pub enum OutputManagerError { MasterSecretKeyMismatch, #[error("Private Key is not found in the current Key Chain")] KeyNotFoundInKeyChain, + #[error("Token with unique id not found")] + TokenUniqueIdNotFound, } #[derive(Debug, Error, PartialEq)] diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 9376ce88c8..b37af5f193 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -629,12 +629,27 @@ where TBackend: OutputManagerBackend + 'static ) -> Result { debug!( target: LOG_TARGET, - "Preparing to send transaction. Amount: {}. Fee per gram: {}. ", amount, fee_per_gram, + "Preparing to send transaction. Amount: {}. Unique id : {:?}. Fee per gram: {}. ", + amount, + unique_id, + fee_per_gram, ); let (outputs, _, total) = self .select_utxos(amount, fee_per_gram, 1, None, unique_id.as_ref()) .await?; + let output_features = match unique_id { + Some(ref _unique_id) => match outputs + .clone() + .into_iter() + .find(|output| output.unblinded_output.features.unique_id.is_some()) + { + Some(output) => output.unblinded_output.features, + _ => Default::default(), + }, + _ => Default::default(), + }; + let offset = PrivateKey::random(&mut OsRng); let nonce = PrivateKey::random(&mut OsRng); @@ -649,16 +664,13 @@ where TBackend: OutputManagerBackend + 'static 0, recipient_script, PrivateKey::random(&mut OsRng), - Default::default(), + output_features, PrivateKey::random(&mut OsRng), ) .with_message(message) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_tx_id(tx_id); - if let Some(ref unique_id) = unique_id { - builder = builder.with_unique_id(unique_id.clone()); - } for uo in outputs.iter() { builder.with_input( uo.unblinded_output @@ -918,10 +930,6 @@ where TBackend: OutputManagerBackend + 'static .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_tx_id(tx_id); - if let Some(ref unique_id) = unique_id { - builder = builder.with_unique_id(unique_id.clone()); - } - for uo in &inputs { builder.with_input( uo.unblinded_output @@ -1115,14 +1123,32 @@ where TBackend: OutputManagerBackend + 'static strategy: Option, unique_id: Option<&Vec>, ) -> Result<(Vec, bool, MicroTari), OutputManagerError> { - if unique_id.is_some() { - // Select UTXO that has this ID - todo!(); - } + let token = match unique_id { + Some(unique_id) => { + warn!(target: LOG_TARGET, "Looking for {:?}", unique_id); + let uo = self.resources.db.fetch_all_unspent_outputs().await?; + // for x in uo { + // warn!(target: LOG_TARGET, "{:?}", x.unblinded_output.unique_id); + // } + if let Some(token_id) = uo.into_iter().find(|x| match &x.unblinded_output.features.unique_id { + Some(token_unique_id) => { + warn!(target: LOG_TARGET, "Comparing with {:?}", token_unique_id); + token_unique_id == unique_id + }, + _ => false, + }) { + Some(token_id) + } else { + return Err(OutputManagerError::TokenUniqueIdNotFound); + } + }, + _ => None, + }; debug!( target: LOG_TARGET, - "select_utxos amount: {}, fee_per_gram: {}, output_count: {}, strategy: {:?}", + "select_utxos amount: {}, token : {:?}, fee_per_gram: {}, output_count: {}, strategy: {:?}", amount, + token, fee_per_gram, output_count, strategy @@ -1132,6 +1158,10 @@ where TBackend: OutputManagerBackend + 'static let mut fee_without_change = MicroTari::from(0); let mut fee_with_change = MicroTari::from(0); + if let Some(unblinded_token) = token { + utxos.push(unblinded_token); + } + let uo = self.resources.db.fetch_spendable_outputs()?; // Attempt to get the chain tip height @@ -1182,7 +1212,7 @@ where TBackend: OutputManagerBackend + 'static } }, }; - debug!(target: LOG_TARGET, "select_utxos selection strategy: {}", strategy); + warn!(target: LOG_TARGET, "select_utxos selection strategy: {}", strategy); let uo = match strategy { UTXOSelectionStrategy::Smallest => uo, diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index fa9f3fa60e..d3d0461f4f 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -52,6 +52,8 @@ pub struct NewOutputSql { script_private_key: Vec, metadata: Option>, features_asset_public_key: Option>, + features_mint_asset_public_key: Option>, + features_mint_asset_owner_commitment: Option>, features_parent_public_key: Option>, features_unique_id: Option>, sender_offset_public_key: Vec, @@ -76,6 +78,17 @@ impl NewOutputSql { script_private_key: output.unblinded_output.script_private_key.to_vec(), metadata: Some(output.unblinded_output.features.metadata), features_asset_public_key: output.unblinded_output.features.asset.map(|a| a.public_key.to_vec()), + features_mint_asset_public_key: output + .unblinded_output + .features + .mint_non_fungible + .clone() + .map(|a| a.asset_public_key.to_vec()), + features_mint_asset_owner_commitment: output + .unblinded_output + .features + .mint_non_fungible + .map(|a| a.asset_owner_commitment.to_vec()), features_parent_public_key: output.unblinded_output.features.parent_public_key.map(|a| a.to_vec()), features_unique_id: output.unblinded_output.features.unique_id, sender_offset_public_key: output.unblinded_output.sender_offset_public_key.to_vec(), diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs index 0f5e2fe94a..2e4330d217 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs @@ -72,6 +72,7 @@ where TBackend: TransactionBackend + 'static id: TxId, dest_pubkey: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, message: String, service_request_reply_channel: Option>>, @@ -92,6 +93,7 @@ where TBackend: TransactionBackend + 'static cancellation_receiver: oneshot::Receiver<()>, dest_pubkey: CommsPublicKey, amount: MicroTari, + unique_id: Option>, fee_per_gram: MicroTari, message: String, service_request_reply_channel: Option< @@ -106,6 +108,7 @@ where TBackend: TransactionBackend + 'static cancellation_receiver: Some(cancellation_receiver), dest_pubkey, amount, + unique_id, fee_per_gram, message, service_request_reply_channel, @@ -155,7 +158,7 @@ where TBackend: TransactionBackend + 'static .prepare_transaction_to_send( self.id, self.amount, - None, // TODO: is this supposed to be populated? + self.unique_id.clone(), self.fee_per_gram, None, self.message.clone(), diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 77192fdb90..a61c76f7af 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -742,6 +742,7 @@ where cancellation_receiver, dest_pubkey, amount, + unique_id, fee_per_gram, message, Some(reply_channel), @@ -1175,6 +1176,7 @@ where cancellation_receiver, tx.destination_public_key, tx.amount, + None, // TODO: Fill this ? tx.fee, tx.message, None, diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index 749dc91bb6..7df7db12ea 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -29,12 +29,15 @@ use std::{ }; use tari_common_types::types::PrivateKey; use tari_comms::types::CommsPublicKey; -use tari_core::transactions::{ - tari_amount::MicroTari, - transaction::Transaction, - transaction_protocol::TxId, - ReceiverTransactionProtocol, - SenderTransactionProtocol, +use tari_core::{ + tari_utilities::hex::Hex, + transactions::{ + tari_amount::MicroTari, + transaction::Transaction, + transaction_protocol::TxId, + ReceiverTransactionProtocol, + SenderTransactionProtocol, + }, }; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -239,6 +242,23 @@ impl CompletedTransaction { mined_height: None, } } + + pub fn get_unique_id(&self) -> Option { + let body = self.transaction.get_body(); + for tx_input in body.inputs() { + match tx_input.features.unique_id { + Some(ref unique_id) => return Some(unique_id.to_hex()), + _ => {}, + } + } + for tx_output in body.outputs() { + match tx_output.features.unique_id { + Some(ref unique_id) => return Some(unique_id.to_hex()), + _ => {}, + } + } + None + } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] From 2af7d0145befa5bc6f4f065029b0434b5585f5ad Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Tue, 28 Sep 2021 11:59:38 +0200 Subject: [PATCH 09/77] fix: refresh assets and tokens state when tx state changes (#3386) --- .../tari_console_wallet/src/automation/commands.rs | 8 +++++--- .../tari_console_wallet/src/ui/state/app_state.rs | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index 17f743d117..c88228ac08 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -691,11 +691,12 @@ pub async fn command_runner( RegisterAsset => { println!("Registering asset."); let name = parsed.args[0].to_string(); + let message = format!("Register asset: {}", name); let mut manager = wallet.asset_manager.clone(); let (tx_id, transaction) = manager.create_registration_transaction(name).await?; let fee = transaction.body.get_total_fee(); let _result = transaction_service - .submit_transaction(tx_id, transaction, fee, 0.into(), "test o ramam".to_string()) + .submit_transaction(tx_id, transaction, fee, 0.into(), message) .await?; }, MintTokens => { @@ -721,14 +722,15 @@ pub async fn command_runner( let mut asset_manager = wallet.asset_manager.clone(); let asset = asset_manager.get_owned_asset_by_pub_key(&public_key).await?; - println!("Found asset:{}", asset.name()); + println!("Asset name: {}", asset.name()); + let message = format!("Minting {} tokens for asset {}", unique_ids.len(), asset.name()); let (tx_id, transaction) = asset_manager .create_minting_transaction(&public_key, asset.owner_commitment(), unique_ids) .await?; let fee = transaction.body.get_total_fee(); let _result = transaction_service - .submit_transaction(tx_id, transaction, fee, 0.into(), "test mint transaction".to_string()) + .submit_transaction(tx_id, transaction, fee, 0.into(), message) .await?; }, CreateInitialCheckpoint => { diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index 444c8dede9..ba947f13e7 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -598,7 +598,7 @@ impl AppStateInner { match found { None => { - // In its not in the backend then make sure it is not left behind in the AppState + // If it's not in the backend then remove it from AppState let _: Option = self .data .pending_txs @@ -652,6 +652,8 @@ impl AppStateInner { }, } self.refresh_balance().await?; + self.refresh_assets_state().await?; + self.refresh_tokens_state().await?; self.updated = true; Ok(()) } From 82e246f187126ac3f152ec386cb0c047a20499a1 Mon Sep 17 00:00:00 2001 From: Martin Stefcek <35243812+Cifko@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:57:28 +0200 Subject: [PATCH 10/77] chore: update tokio to 1.10 and add config section (#3388) * chore: update tokio to 1.10 and add config section * test: Add cucumber for validator node --- Cargo.lock | 17 +- applications/tari_dan_node/Cargo.toml | 3 +- .../tari_dan_node/proto/dan_node.proto | 2 +- .../dan_layer/workers/states/commit_state.rs | 4 +- .../dan_layer/workers/states/decide_state.rs | 4 +- .../workers/states/pre_commit_state.rs | 4 +- .../src/dan_layer/workers/states/prepare.rs | 6 +- .../tari_dan_node/src/grpc/dan_grpc_server.rs | 2 +- applications/tari_dan_node/src/main.rs | 11 +- .../config/presets/tari_config_example.toml | 9 + common/src/configuration/dan_config.rs | 16 ++ common/src/configuration/global.rs | 18 +- .../features/ValidatorNode.feature | 5 + integration_tests/features/support/steps.js | 68 ++++++ integration_tests/features/support/world.js | 24 +- integration_tests/helpers/config.js | 10 +- integration_tests/helpers/danNodeClient.js | 62 +++++ integration_tests/helpers/danNodeProcess.js | 225 ++++++++++++++++++ 18 files changed, 452 insertions(+), 38 deletions(-) create mode 100644 integration_tests/features/ValidatorNode.feature create mode 100644 integration_tests/helpers/danNodeClient.js create mode 100644 integration_tests/helpers/danNodeProcess.js diff --git a/Cargo.lock b/Cargo.lock index 5e2717df4b..c303205be4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4861,7 +4861,8 @@ dependencies = [ "tari_shutdown", "tari_test_utils 0.8.1", "thiserror", - "tokio 0.2.25", + "tokio 1.11.0", + "tokio-stream", "tonic", "tonic-build", ] @@ -5407,7 +5408,6 @@ dependencies = [ "num_cpus", "pin-project-lite 0.1.12", "slab", - "tokio-macros 0.2.6", ] [[package]] @@ -5425,7 +5425,7 @@ dependencies = [ "once_cell", "pin-project-lite 0.2.7", "signal-hook-registry", - "tokio-macros 1.3.0", + "tokio-macros", "winapi 0.3.9", ] @@ -5439,17 +5439,6 @@ dependencies = [ "tokio 1.11.0", ] -[[package]] -name = "tokio-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" -dependencies = [ - "proc-macro2 1.0.28", - "quote 1.0.9", - "syn 1.0.75", -] - [[package]] name = "tokio-macros" version = "1.3.0" diff --git a/applications/tari_dan_node/Cargo.toml b/applications/tari_dan_node/Cargo.toml index 32ad408437..4be659ea04 100644 --- a/applications/tari_dan_node/Cargo.toml +++ b/applications/tari_dan_node/Cargo.toml @@ -22,7 +22,8 @@ tari_mmr = {path = "../../base_layer/mmr"} thiserror = "^1.0.20" log = { version = "0.4.8", features = ["std"] } anyhow = "1.0.32" -tokio = { version="0.2.10", features = ["macros", "sync", "time"]} +tokio = { version="1.10", features = ["macros", "time"]} +tokio-stream = { version = "0.1.7", features = ["sync"] } tonic = "0.5.2" prost = "0.8" prost-types = "0.8" diff --git a/applications/tari_dan_node/proto/dan_node.proto b/applications/tari_dan_node/proto/dan_node.proto index 0c52320429..f5fddb9064 100644 --- a/applications/tari_dan_node/proto/dan_node.proto +++ b/applications/tari_dan_node/proto/dan_node.proto @@ -41,7 +41,7 @@ message ExecuteInstructionRequest{ bytes asset_public_key =1; string method =2; repeated bytes args = 3; - bytes from = 4; + bytes token_id = 4; bytes signature = 5; uint64 id = 6; } diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs b/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs index d0d851ecb5..789039df2a 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs +++ b/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs @@ -41,7 +41,7 @@ use crate::{ digital_assets_error::DigitalAssetError, }; use std::{any::Any, collections::HashMap, marker::PhantomData, time::Instant}; -use tokio::time::{delay_for, Duration}; +use tokio::time::{sleep, Duration}; // TODO: This is very similar to pre-commit state pub struct CommitState @@ -121,7 +121,7 @@ where } } - _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs b/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs index 76297ac264..0dba82c906 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs +++ b/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs @@ -42,7 +42,7 @@ use crate::{ digital_assets_error::DigitalAssetError, }; use std::{any::Any, collections::HashMap, marker::PhantomData, time::Instant}; -use tokio::time::{delay_for, Duration}; +use tokio::time::{sleep, Duration}; // TODO: This is very similar to pre-commit, and commit state pub struct DecideState @@ -127,7 +127,7 @@ where } } - _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs b/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs index db7450a7b0..65fa68b45e 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs +++ b/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs @@ -41,7 +41,7 @@ use crate::{ digital_assets_error::DigitalAssetError, }; use std::{any::Any, collections::HashMap, marker::PhantomData, time::Instant}; -use tokio::time::{delay_for, Duration}; +use tokio::time::{sleep, Duration}; pub struct PreCommitState where @@ -118,7 +118,7 @@ where } } - _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs b/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs index 2fdb06c1d5..ef8d5b2f62 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs +++ b/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs @@ -56,7 +56,7 @@ use std::{ time::Instant, }; use tari_shutdown::{Shutdown, ShutdownSignal}; -use tokio::time::{delay_for, Duration}; +use tokio::time::{sleep, Duration}; pub struct Prepare where @@ -135,7 +135,7 @@ where } }, - _ = delay_for(timeout.saturating_sub(Instant::now() - started)) => { + _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; @@ -265,7 +265,7 @@ where payload_provider: &TPayloadProvider, ) -> Result, DigitalAssetError> { // TODO: Artificial delay here to set the block time - delay_for(Duration::from_secs(3)).await; + sleep(Duration::from_secs(3)).await; let payload = payload_provider.create_payload()?; dbg!(&payload); diff --git a/applications/tari_dan_node/src/grpc/dan_grpc_server.rs b/applications/tari_dan_node/src/grpc/dan_grpc_server.rs index f3fe9697c7..3d66aa24ac 100644 --- a/applications/tari_dan_node/src/grpc/dan_grpc_server.rs +++ b/applications/tari_dan_node/src/grpc/dan_grpc_server.rs @@ -65,7 +65,7 @@ impl dan_rpc::d .map_err(|err| Status::invalid_argument("asset_public_key was not a valid public key"))?, request.method.clone(), request.args.clone(), - TokenId(request.from.clone()), + TokenId(request.token_id.clone()), // TODO: put signature in here ComSig::default() // create_com_sig_from_bytes(&request.signature) diff --git a/applications/tari_dan_node/src/main.rs b/applications/tari_dan_node/src/main.rs index 7bad9a91e9..f42f19b04e 100644 --- a/applications/tari_dan_node/src/main.rs +++ b/applications/tari_dan_node/src/main.rs @@ -37,7 +37,8 @@ use std::{ }; use tari_shutdown::{Shutdown, ShutdownSignal}; use thiserror::Error; -use tokio::{runtime, stream::StreamExt, task}; +use tokio::{runtime, task}; +use tokio_stream::StreamExt; use tonic::transport::Server; use crate::{ @@ -97,12 +98,8 @@ async fn run_node(config: GlobalConfig) -> Result<(), ExitCodes> { } fn build_runtime() -> Result { - let mut builder = runtime::Builder::new(); - builder - .threaded_scheduler() - .enable_all() - .build() - .map_err(|e| ExitCodes::UnknownError) + let mut builder = runtime::Builder::new_multi_thread(); + builder.enable_all().build().map_err(|e| ExitCodes::UnknownError) } async fn run_dan_node( diff --git a/common/config/presets/tari_config_example.toml b/common/config/presets/tari_config_example.toml index 8805575cc2..e78500798f 100644 --- a/common/config/presets/tari_config_example.toml +++ b/common/config/presets/tari_config_example.toml @@ -550,3 +550,12 @@ transcoder_host_address = "127.0.0.1:7879" # mining_pool_address = "miningcore.tarilabs.com:3052" # mining_wallet_address = "YOUR_WALLET_PUBLIC_KEY" # mining_worker_name = "worker1" + +[dan_node] +# Override common.network for base node +# network = "weatherwax" + +[dan_node.weatherwax] +# committee = ["pubkey_1", "pubkey_2"] # cannot be of zero size +# phase_timeout = 30 +# template_id = "EditableMetadata" \ No newline at end of file diff --git a/common/src/configuration/dan_config.rs b/common/src/configuration/dan_config.rs index abb19b28d5..9fa3d75b51 100644 --- a/common/src/configuration/dan_config.rs +++ b/common/src/configuration/dan_config.rs @@ -32,6 +32,22 @@ pub struct DanNodeConfig { } impl DanNodeConfig { + pub fn new( + committee: Vec, + phase_timeout: u64, + template_id: String, + ) -> Result, ConfigurationError> { + if committee.len() == 0 { + Ok(None) + } else { + Ok(Some(DanNodeConfig { + committee, + phase_timeout, + template_id, + })) + } + } + pub fn convert_if_present(cfg: Config) -> Result, ConfigurationError> { let section: DanNodeConfig = match cfg.get("dan_node") { Ok(s) => s, diff --git a/common/src/configuration/global.rs b/common/src/configuration/global.rs index 31e557aff7..6669df72fa 100644 --- a/common/src/configuration/global.rs +++ b/common/src/configuration/global.rs @@ -694,6 +694,22 @@ fn convert_node_config( .filter(|c| c.is_alphanumeric()) .collect::(); + // Dan config + let key = config_string("dan_node", &net_str, "committee"); + let committee = match cfg.get_array(&key) { + Ok(committee) => committee.into_iter().map(|v| v.into_str().unwrap()).collect(), + Err(..) => match cfg.get_str(&key) { + Ok(s) => s.split(',').map(|v| v.to_string()).collect(), + Err(..) => vec![], + }, + }; + + let key = config_string("dan_node", &net_str, "phase_timeout"); + let phase_timeout = optional(cfg.get_int(&key))?.unwrap_or(30) as u64; + + let key = config_string("dan_node", &net_str, "template_id"); + let template_id = optional(cfg.get_str(&key))?.unwrap_or_else(|| "EditableMetadata".to_string()); + Ok(GlobalConfig { autoupdate_check_interval, autoupdate_dns_hosts, @@ -776,7 +792,7 @@ fn convert_node_config( flood_ban_max_msg_count, mine_on_tip_only, validate_tip_timeout_sec, - dan_node: DanNodeConfig::convert_if_present(cfg)?, + dan_node: DanNodeConfig::new(committee, phase_timeout, template_id).unwrap(), mining_pool_address, mining_wallet_address, mining_worker_name, diff --git a/integration_tests/features/ValidatorNode.feature b/integration_tests/features/ValidatorNode.feature new file mode 100644 index 0000000000..12c84efed6 --- /dev/null +++ b/integration_tests/features/ValidatorNode.feature @@ -0,0 +1,5 @@ +Feature: Validator Node + Scenario: Test committee + Given I have committee from 4 validator nodes connected + Then I send instruction successfully with metadata {"issuer" : {"num_clicks" : 1}} + Then At least 3 out of 4 validator nodes have filled asset data \ No newline at end of file diff --git a/integration_tests/features/support/steps.js b/integration_tests/features/support/steps.js index f67aa45f08..b43c8e0282 100644 --- a/integration_tests/features/support/steps.js +++ b/integration_tests/features/support/steps.js @@ -4096,4 +4096,72 @@ Then( } } ); + +Given( + "I have committee from {int} validator nodes connected", + { timeout: 20 * 1000 }, + async function (nodes_cnt) { + console.log(`Starting ${nodes_cnt} validator nodes`); + const promises = []; + for (let i = 0; i < nodes_cnt; i++) { + promises.push(this.createAndAddDanNode(`DanNode${i}`)); + } + await Promise.all(promises); + let committee = Array(nodes_cnt) + .fill() + .map((_, i) => this.getNode(`DanNode${i}`).getPubKey()); + let peers = Array(nodes_cnt) + .fill() + .map((_, i) => this.getNode(`DanNode${i}`).peerAddress()); + for (let i = 0; i < nodes_cnt; ++i) { + let dan_node = this.getNode(`DanNode${i}`); + dan_node.setCommittee(committee); + dan_node.setPeerSeeds(peers.filter((_, j) => i != j)); + promises.push(dan_node.start()); + } + await Promise.all(promises); + } +); + +Then( + /I send instruction successfully with metadata (.*)/, + { timeout: 20 * 1000 }, + async function (metadata) { + console.log("metadata", metadata); + let dan_node = this.getNode("DanNode0"); // Only the first node has GRPC + let grpc_dan_node = await dan_node.createGrpcClient(); + let response = await grpc_dan_node.executeInstruction( + "f665775dbbf4e428e5c8c2bb1c5e7d2e508e93c83250c495ac617a0a1fb2d76d", // asset + "update", // method + metadata, + "eee280877ef836f1026d8a848a5da3eb6364cd0343372235e6ca10e2a697fc6f" // token + ); + expect(response?.status).to.be.equal("Accepted"); + } +); + +Then( + "At least {int} out of {int} validator nodes have filled asset data", + { timeout: 1200 * 1000 }, + async function (at_least, total) { + let retries = 1; + let success = false; + let retries_limit = 239; + while (retries < retries_limit) { + let count = 0; + for (let i = 0; i < total; ++i) { + let node = this.getNode(`DanNode${i}`); + if (node.hasAssetData()) { + count += 1; + } + } + success = count >= at_least; + if (success) break; + ++retries; + await sleep(5000); + } + expect(success).to.be.true; + } +); + //endregion diff --git a/integration_tests/features/support/world.js b/integration_tests/features/support/world.js index aa2233cf20..2d5ea9caec 100644 --- a/integration_tests/features/support/world.js +++ b/integration_tests/features/support/world.js @@ -1,6 +1,7 @@ const { setWorldConstructor, After, BeforeAll } = require("cucumber"); const BaseNodeProcess = require("../../helpers/baseNodeProcess"); +const DanNodeProcess = require("../../helpers/danNodeProcess"); const MergeMiningProxyProcess = require("../../helpers/mergeMiningProxyProcess"); const WalletProcess = require("../../helpers/walletProcess"); const WalletFFIClient = require("../../helpers/walletFFIClient"); @@ -18,6 +19,7 @@ class CustomWorld { this.checkAutoTransactions = true; this.seeds = {}; this.nodes = {}; + this.dan_nodes = {}; this.proxies = {}; this.miners = {}; this.wallets = {}; @@ -69,6 +71,16 @@ class CustomWorld { return new BaseNodeProcess(name, false, options, this.logFilePathBaseNode); } + createDanNode(name, options) { + return new DanNodeProcess(name, false, options, this.logFilePathBaseNode); + } + + async createAndAddDanNode(name) { + const node = this.createDanNode(name); + await node.init(); + await this.addDanNode(name, node); + } + async createAndAddNode(name, addresses) { const node = this.createNode(name); if (Array.isArray(addresses)) { @@ -80,6 +92,11 @@ class CustomWorld { await this.addNode(name, node); } + async addDanNode(name, process) { + this.dan_nodes[name] = process; + // this.clients[name] = await process.createGrpcClient(); + } + async addNode(name, process) { this.nodes[name] = process; this.clients[name] = await process.createGrpcClient(); @@ -233,7 +250,7 @@ class CustomWorld { } getNode(name) { - const node = this.nodes[name] || this.seeds[name]; + const node = this.nodes[name] || this.seeds[name] || this.dan_nodes[name]; if (!node) { throw new Error(`Node not found with name '${name}'`); } @@ -356,6 +373,11 @@ BeforeAll({ timeout: 1200000 }, async function () { await baseNode.init(); await baseNode.compile(); + const danNode = new DanNodeProcess("compile"); + console.log("Compiling dan node..."); + await danNode.init(); + await danNode.compile(); + const wallet = new WalletProcess("compile"); console.log("Compiling wallet..."); await wallet.init(); diff --git a/integration_tests/helpers/config.js b/integration_tests/helpers/config.js index ef06a75449..8bd3a954cc 100644 --- a/integration_tests/helpers/config.js +++ b/integration_tests/helpers/config.js @@ -57,7 +57,7 @@ function mapEnvs(options) { return res; } -function baseEnvs(peerSeeds = [], forceSyncPeers = []) { +function baseEnvs(peerSeeds = [], forceSyncPeers = [], committee = []) { const envs = { RUST_BACKTRACE: 1, TARI_BASE_NODE__NETWORK: "localnet", @@ -112,6 +112,9 @@ function baseEnvs(peerSeeds = [], forceSyncPeers = []) { "5cfcf17f41b01980eb4fa03cec5ea12edbd3783496a2b5dabf99e4bf6410f460::/ip4/10.0.0.50/tcp/1", ]; } + if (committee.length != 0) { + envs.TARI_DAN_NODE__LOCALNET__COMMITTEE = committee; + } return envs; } @@ -131,9 +134,10 @@ function createEnv( options, peerSeeds = [], _txnSendingMechanism = "DirectAndStoreAndForward", - forceSyncPeers = [] + forceSyncPeers = [], + committee = [] ) { - const envs = baseEnvs(peerSeeds, forceSyncPeers); + const envs = baseEnvs(peerSeeds, forceSyncPeers, committee); const network = options && options.network ? options.network.toUpperCase() : "LOCALNET"; diff --git a/integration_tests/helpers/danNodeClient.js b/integration_tests/helpers/danNodeClient.js new file mode 100644 index 0000000000..e0361d31cb --- /dev/null +++ b/integration_tests/helpers/danNodeClient.js @@ -0,0 +1,62 @@ +const grpc = require("@grpc/grpc-js"); +const protoLoader = require("@grpc/proto-loader"); +const { tryConnect } = require("./util"); +const grpcPromise = require("grpc-promise"); + +class DanNodeClient { + constructor() { + this.client = null; + this.blockTemplates = {}; + } + + async connect(port) { + const PROTO_PATH = + __dirname + "/../../applications/tari_dan_node/proto/dan_node.proto"; + const packageDefinition = protoLoader.loadSync(PROTO_PATH, { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, + }); + const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); + const tari = protoDescriptor.tari.dan.rpc; + this.client = await tryConnect( + () => + new tari.DanNode("127.0.0.1:" + port, grpc.credentials.createInsecure()) + ); + + grpcPromise.promisifyAll(this.client, { + metadata: new grpc.Metadata(), + }); + } + + static async create(port) { + const client = new DanNodeClient(); + await client.connect(port); + return client; + } + + executeInstruction(asset_public_key, method, metadata, token, signature, id) { + let convertHexStringToVec = (string) => + string.match(/.{2}/g).map((x) => parseInt(x, 16)); + let convertStringToVec = (string) => + Array(string.length) + .fill() + .map((_, i) => string.charCodeAt(i)); + + console.log( + `Executing instruction for asset ${asset_public_key} / token ${token} via method ${method} with metadata ${metadata} ` + ); + return this.client.executeInstruction().sendMessage({ + asset_public_key: convertHexStringToVec(asset_public_key), + method, + args: [convertStringToVec(metadata)], + token_id: convertHexStringToVec(token), + signature, + id, + }); + } +} + +module.exports = DanNodeClient; diff --git a/integration_tests/helpers/danNodeProcess.js b/integration_tests/helpers/danNodeProcess.js new file mode 100644 index 0000000000..b059f781ac --- /dev/null +++ b/integration_tests/helpers/danNodeProcess.js @@ -0,0 +1,225 @@ +const { spawn } = require("child_process"); +const { expect } = require("chai"); +const fs = require("fs"); +const path = require("path"); +const DanNodeClient = require("./danNodeClient"); +const { sleep, getFreePort } = require("./util"); +const dateFormat = require("dateformat"); +const { createEnv } = require("./config"); + +let outputProcess; +class DanNodeProcess { + constructor(name, excludeTestEnvars, options, logFilePath, nodeFile) { + this.name = name; + this.logFilePath = logFilePath ? path.resolve(logFilePath) : logFilePath; + this.nodeFile = nodeFile; + this.options = Object.assign( + { + baseDir: "./temp/base_nodes", + }, + options || {} + ); + this.excludeTestEnvars = excludeTestEnvars; + } + + async init() { + this.port = await getFreePort(); + this.grpcPort = 18080; // Currently it's constant + this.name = `DanNode${this.port}-${this.name}`; + this.nodeFile = this.nodeFile || "nodeid.json"; + + do { + this.baseDir = `${this.options.baseDir}/${dateFormat( + new Date(), + "yyyymmddHHMM" + )}/${this.name}`; + // Some tests failed during testing because the next base node process started in the previous process + // directory therefore using the previous blockchain database + if (fs.existsSync(this.baseDir)) { + sleep(1000); + } + } while (fs.existsSync(this.baseDir)); + const args = ["--base-path", ".", "--init", "--create-id"]; + if (this.logFilePath) { + args.push("--log-config", this.logFilePath); + } + + await this.run(await this.compile(), args); + } + + async compile() { + if (!outputProcess) { + await this.run("cargo", [ + "build", + "--release", + "--bin", + "tari_dan_node", + "-Z", + "unstable-options", + "--out-dir", + process.cwd() + "/temp/out", + ]); + outputProcess = process.cwd() + "/temp/out/tari_dan_node"; + } + console.log(outputProcess); + return outputProcess; + } + + hasAssetData() { + return fs.existsSync(this.baseDir + "/localnet/asset_data"); + } + + ensureNodeInfo() { + for (;;) { + if (fs.existsSync(this.baseDir + "/" + this.nodeFile)) { + break; + } + } + + this.nodeInfo = JSON.parse( + fs.readFileSync(this.baseDir + "/" + this.nodeFile, "utf8") + ); + } + + peerAddress() { + this.ensureNodeInfo(); + const addr = this.nodeInfo.public_key + "::" + this.nodeInfo.public_address; + return addr; + } + + getPubKey() { + this.ensureNodeInfo(); + return this.nodeInfo.public_key; + } + + setPeerSeeds(addresses) { + this.peerSeeds = addresses.join(","); + } + + setForceSyncPeers(addresses) { + this.forceSyncPeers = addresses.join(","); + } + + setCommittee(committee) { + this.committee = committee.join(","); + } + + getGrpcAddress() { + const address = "127.0.0.1:" + this.grpcPort; + // console.log("Base Node GRPC Address:",address); + return address; + } + + run(cmd, args) { + return new Promise((resolve, reject) => { + if (!fs.existsSync(this.baseDir)) { + fs.mkdirSync(this.baseDir, { recursive: true }); + fs.mkdirSync(this.baseDir + "/log", { recursive: true }); + } + + let envs = []; + if (!this.excludeTestEnvars) { + envs = createEnv( + this.name, + false, + this.nodeFile, + "127.0.0.1", + "8082", + "8081", + "127.0.0.1", + this.grpcPort, + this.port, + "127.0.0.1:8080", + "127.0.0.1:8085", + this.options, + this.peerSeeds, + "DirectAndStoreAndForward", + this.forceSyncPeers, + this.committee + ); + } + const ps = spawn(cmd, args, { + cwd: this.baseDir, + // shell: true, + env: { ...process.env, ...envs }, + }); + + ps.stdout.on("data", (data) => { + //console.log(`stdout: ${data}`); + fs.appendFileSync(`${this.baseDir}/log/stdout.log`, data.toString()); + if ( + data + .toString() + .toUpperCase() + .match(/STATE CHANGED FROM STARTING TO PREPARE/) + ) { + resolve(ps); + } + }); + + ps.stderr.on("data", (data) => { + // console.error(`stderr: ${data}`); + fs.appendFileSync(`${this.baseDir}/log/stderr.log`, data.toString()); + }); + + ps.on("close", (code) => { + const ps = this.ps; + this.ps = null; + if (code) { + if (code == 101) { + resolve(ps); // Validator node will fail, because of the missing committee section, but that's okay. + } else { + console.log(`child process exited with code ${code}`); + reject(`child process exited with code ${code}`); + } + } else { + resolve(ps); + } + }); + + expect(ps.error).to.be.an("undefined"); + this.ps = ps; + }); + } + + async startNew() { + await this.init(); + const start = await this.start(); + return start; + } + + async startAndConnect() { + await this.startNew(); + return await this.createGrpcClient(); + } + + async start(opts = []) { + const args = ["--base-path", "."]; + if (this.logFilePath) { + args.push("--log-config", this.logFilePath); + } + args.push(...opts); + return await this.run(await this.compile(), args); + } + + stop() { + return new Promise((resolve) => { + if (!this.ps) { + return resolve(); + } + this.ps.on("close", (code) => { + if (code) { + console.log(`child process exited with code ${code}`); + } + resolve(); + }); + this.ps.kill("SIGINT"); + }); + } + + async createGrpcClient() { + return await DanNodeClient.create(this.grpcPort); + } +} + +module.exports = DanNodeProcess; From 80f29decbc6e9d103f2de7eccb5283b8bbca6a65 Mon Sep 17 00:00:00 2001 From: Martin Stefcek <35243812+Cifko@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:16:48 +0200 Subject: [PATCH 11/77] chore: remove fee from submittransaction (#3397) --- .../src/automation/commands.rs | 12 ++++------ .../src/grpc/wallet_grpc_server.rs | 3 +-- .../src/output_manager_service/handle.rs | 4 ++-- .../src/output_manager_service/service.rs | 4 ++-- .../wallet/src/transaction_service/handle.rs | 2 +- base_layer/wallet/src/wallet.rs | 4 ++-- .../tests/output_manager_service/service.rs | 22 ++++++++++++------- 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index c88228ac08..566f6576a1 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -170,11 +170,11 @@ pub async fn coin_split( _ => Err(CommandError::Argument), }?; - let (tx_id, tx, fee, amount) = output_service + let (tx_id, tx, amount) = output_service .create_coin_split(amount_per_split, num_splits as usize, MicroTari(100), None) .await?; transaction_service - .submit_transaction(tx_id, tx, fee, amount, "Coin split".into()) + .submit_transaction(tx_id, tx, amount, "Coin split".into()) .await?; Ok(tx_id) @@ -694,9 +694,8 @@ pub async fn command_runner( let message = format!("Register asset: {}", name); let mut manager = wallet.asset_manager.clone(); let (tx_id, transaction) = manager.create_registration_transaction(name).await?; - let fee = transaction.body.get_total_fee(); let _result = transaction_service - .submit_transaction(tx_id, transaction, fee, 0.into(), message) + .submit_transaction(tx_id, transaction, 0.into(), message) .await?; }, MintTokens => { @@ -728,9 +727,8 @@ pub async fn command_runner( let (tx_id, transaction) = asset_manager .create_minting_transaction(&public_key, asset.owner_commitment(), unique_ids) .await?; - let fee = transaction.body.get_total_fee(); let _result = transaction_service - .submit_transaction(tx_id, transaction, fee, 0.into(), message) + .submit_transaction(tx_id, transaction, 0.into(), message) .await?; }, CreateInitialCheckpoint => { @@ -759,12 +757,10 @@ pub async fn command_runner( let (tx_id, transaction) = asset_manager .create_initial_asset_checkpoint(&public_key, &merkle_root) .await?; - let fee = transaction.body.get_total_fee(); let _result = transaction_service .submit_transaction( tx_id, transaction, - fee, 0.into(), "test initial asset checkpoint transaction".to_string(), ) diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index 84856620a2..d291d3e689 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -377,7 +377,6 @@ impl wallet_server::Wallet for WalletGrpcServer { .create_minting_transaction(&asset_public_key, asset.owner_commitment(), message.unique_ids) .await .map_err(|e| Status::internal(e.to_string()))?; - let fee = transaction.body.get_total_fee(); let owner_commitments = transaction .body @@ -386,7 +385,7 @@ impl wallet_server::Wallet for WalletGrpcServer { .filter_map(|o| o.features.unique_id.as_ref().map(|_| o.commitment.to_vec())) .collect(); let _result = transaction_service - .submit_transaction(tx_id, transaction, fee, 0.into(), "test mint transaction".to_string()) + .submit_transaction(tx_id, transaction, 0.into(), "test mint transaction".to_string()) .await .map_err(|e| Status::internal(e.to_string()))?; diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index 2e53d4e77c..2412a6eb31 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -189,7 +189,7 @@ pub enum OutputManagerResponse { SeedWords(Vec), BaseNodePublicKeySet, UtxoValidationStarted(u64), - Transaction((TxId, Transaction, MicroTari, MicroTari)), + Transaction((TxId, Transaction, MicroTari)), EncryptionApplied, EncryptionRemoved, PublicRewindKeys(Box), @@ -557,7 +557,7 @@ impl OutputManagerHandle { split_count: usize, fee_per_gram: MicroTari, lock_height: Option, - ) -> Result<(TxId, Transaction, MicroTari, MicroTari), OutputManagerError> { + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { match self .handle .call(OutputManagerRequest::CreateCoinSplit(( diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index b37af5f193..36a2959621 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1316,7 +1316,7 @@ where TBackend: OutputManagerBackend + 'static split_count: usize, fee_per_gram: MicroTari, lock_height: Option, - ) -> Result<(TxId, Transaction, MicroTari, MicroTari), OutputManagerError> { + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { trace!( target: LOG_TARGET, "Select UTXOs and estimate coin split transaction fee." @@ -1425,7 +1425,7 @@ where TBackend: OutputManagerBackend + 'static trace!(target: LOG_TARGET, "Finalize coin split transaction ({}).", tx_id); stp.finalize(KernelFeatures::empty(), &factories)?; let tx = stp.take_transaction()?; - Ok((tx_id, tx, fee, utxos_total_value)) + Ok((tx_id, tx, utxos_total_value)) } /// Persist a one-sided payment script for a Comms Public/Private key. These are the scripts that this wallet knows diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index a8352374f8..4f34b77cd6 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -493,10 +493,10 @@ impl TransactionServiceHandle { &mut self, tx_id: TxId, tx: Transaction, - fee: MicroTari, amount: MicroTari, message: String, ) -> Result<(), TransactionServiceError> { + let fee = tx.body.get_total_fee(); match self .handle .call(TransactionServiceRequest::SubmitSelfSendTransaction( diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index fbeeb9a25b..3eec705e0d 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -499,10 +499,10 @@ where .await; match coin_split_tx { - Ok((tx_id, split_tx, amount, fee)) => { + Ok((tx_id, split_tx, amount)) => { let coin_tx = self .transaction_service - .submit_transaction(tx_id, split_tx, fee, amount, message) + .submit_transaction(tx_id, split_tx, amount, message) .await; match coin_tx { Ok(_) => Ok(tx_id), diff --git a/base_layer/wallet/tests/output_manager_service/service.rs b/base_layer/wallet/tests/output_manager_service/service.rs index ceb4ed4c8b..78d10ddb94 100644 --- a/base_layer/wallet/tests/output_manager_service/service.rs +++ b/base_layer/wallet/tests/output_manager_service/service.rs @@ -428,8 +428,8 @@ async fn test_utxo_selection_no_chain_metadata() { assert!(matches!(err, OutputManagerError::NotEnoughFunds)); // coin split uses the "Largest" selection strategy - let (_, _, fee, utxos_total_value) = oms.create_coin_split(amount, 5, fee_per_gram, None).await.unwrap(); - assert_eq!(fee, MicroTari::from(820)); + let (_, tx, utxos_total_value) = oms.create_coin_split(amount, 5, fee_per_gram, None).await.unwrap(); + assert_eq!(tx.body.get_total_fee(), MicroTari::from(820)); assert_eq!(utxos_total_value, MicroTari::from(10_000)); // test that largest utxo was encumbered @@ -496,9 +496,9 @@ async fn test_utxo_selection_with_chain_metadata() { assert!(matches!(err, OutputManagerError::NotEnoughFunds)); // test coin split is maturity aware - let (_, _, fee, utxos_total_value) = oms.create_coin_split(amount, 5, fee_per_gram, None).await.unwrap(); + let (_, tx, utxos_total_value) = oms.create_coin_split(amount, 5, fee_per_gram, None).await.unwrap(); assert_eq!(utxos_total_value, MicroTari::from(6_000)); - assert_eq!(fee, MicroTari::from(820)); + assert_eq!(tx.get_total_fee(), MicroTari::from(820)); // test that largest spendable utxo was encumbered let utxos = oms.get_unspent_outputs().await.unwrap(); @@ -1164,13 +1164,16 @@ async fn coin_split_with_change() { let fee_per_gram = MicroTari::from(25); let split_count = 8; - let (_tx_id, coin_split_tx, fee, amount) = oms + let (_tx_id, coin_split_tx, amount) = oms .create_coin_split(1000.into(), split_count, fee_per_gram, None) .await .unwrap(); assert_eq!(coin_split_tx.body.inputs().len(), 2); assert_eq!(coin_split_tx.body.outputs().len(), split_count + 1); - assert_eq!(fee, Fee::calculate(fee_per_gram, 1, 2, split_count + 1)); + assert_eq!( + coin_split_tx.body.get_total_fee(), + Fee::calculate(fee_per_gram, 1, 2, split_count + 1) + ); assert_eq!(amount, val2 + val3); } @@ -1194,13 +1197,16 @@ async fn coin_split_no_change() { assert!(oms.add_output(uo2).await.is_ok()); assert!(oms.add_output(uo3).await.is_ok()); - let (_tx_id, coin_split_tx, fee, amount) = oms + let (_tx_id, coin_split_tx, amount) = oms .create_coin_split(1000.into(), split_count, fee_per_gram, None) .await .unwrap(); assert_eq!(coin_split_tx.body.inputs().len(), 3); assert_eq!(coin_split_tx.body.outputs().len(), split_count); - assert_eq!(fee, Fee::calculate(fee_per_gram, 1, 3, split_count)); + assert_eq!( + coin_split_tx.body.get_total_fee(), + Fee::calculate(fee_per_gram, 1, 3, split_count) + ); assert_eq!(amount, val1 + val2 + val3); } From cf3ad4e7f3a3f05bc4a2b55aff9aa70483aa09b4 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Sat, 2 Oct 2021 16:18:21 +0200 Subject: [PATCH 12/77] chore: merge development --- Cargo.lock | 516 ++++++++++++++++-- .../src/ui/state/app_state.rs | 2 +- .../tari_dan_node/src/dan_layer/dan_node.rs | 6 +- .../src/dan_layer/models/instruction.rs | 2 + .../src/dan_layer/models/instruction_set.rs | 1 + .../wallet/src/transaction_service/service.rs | 3 +- rust-toolchain | 2 +- 7 files changed, 475 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b75f0a2920..5cc0232ab6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,18 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +[[package]] +name = "bytecodec" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf4c9d0bbf32eea58d7c0f812058138ee8edaf0f2802b6d03561b504729a325" +dependencies = [ + "byteorder", + "serde 1.0.130", + "serde_json", + "trackable 0.2.24", +] + [[package]] name = "bytemuck" version = "1.7.2" @@ -898,15 +910,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" dependencies = [ "bitflags 1.3.2", - "crossterm_winapi", + "crossterm_winapi 0.6.2", "lazy_static 1.4.0", "libc", - "mio", + "mio 0.7.13", "parking_lot 0.10.2", "signal-hook", "winapi 0.3.9", ] +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi 0.7.0", + "lazy_static 1.4.0", + "libc", + "mio 0.7.13", + "parking_lot 0.11.2", + "signal-hook", + "winapi 0.3.9", +] + [[package]] name = "crossterm_winapi" version = "0.6.2" @@ -916,6 +944,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -1362,6 +1399,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "futures" version = "0.1.31" @@ -1659,6 +1712,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log 0.4.14", + "regex", +] + +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio 0.2.25", + "tokio-util 0.3.1", + "tracing", + "tracing-futures", +] + [[package]] name = "h2" version = "0.3.4" @@ -1674,7 +1760,7 @@ dependencies = [ "indexmap", "slab", "tokio 1.11.0", - "tokio-util", + "tokio-util 0.6.7", "tracing", ] @@ -1739,6 +1825,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes 0.5.6", + "http", +] + [[package]] name = "http-body" version = "0.4.3" @@ -1756,6 +1852,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + [[package]] name = "httpdate" version = "1.0.1" @@ -1792,10 +1894,34 @@ dependencies = [ "time", "traitobject", "typeable", - "unicase", + "unicase 1.4.2", "url 1.7.2", ] +[[package]] +name = "hyper" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" +dependencies = [ + "bytes 0.5.6", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.2.7", + "http", + "http-body 0.3.1", + "httparse", + "httpdate 0.3.2", + "itoa", + "pin-project 1.0.8", + "socket2 0.3.19", + "tokio 0.2.25", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "0.14.12" @@ -1806,14 +1932,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.4", "http", - "http-body", + "http-body 0.4.3", "httparse", - "httpdate", + "httpdate 1.0.1", "itoa", "pin-project-lite 0.2.7", - "socket2", + "socket2 0.4.1", "tokio 1.11.0", "tower-service", "tracing", @@ -1912,6 +2038,15 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48dc51180a9b377fd75814d0cc02199c20f8e99433d6762f650d39cdbbd3b56f" +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "ipnet" version = "2.3.1" @@ -1963,6 +2098,54 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpc-core" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4467ab6dfa369b69e52bd0692e480c4d117410538526a57a304a0f2250fd95e" +dependencies = [ + "futures 0.3.16", + "futures-executor", + "futures-util", + "log 0.4.14", + "serde 1.0.130", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-http-server" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522a047cac0958097ee71d047dd71cb84979fd2fa21c7a68fbe12736bef870a2" +dependencies = [ + "futures 0.3.16", + "hyper 0.13.10", + "jsonrpc-core", + "jsonrpc-server-utils", + "log 0.4.14", + "net2", + "parking_lot 0.11.2", + "unicase 2.6.0", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bce68fa279a2822b3619369cd024f8a4f8e5ce485468834f8679a3c7919aae2d" +dependencies = [ + "bytes 0.5.6", + "futures 0.3.16", + "globset", + "jsonrpc-core", + "lazy_static 1.4.0", + "log 0.4.14", + "tokio 0.2.25", + "tokio-util 0.3.1", + "unicase 2.6.0", +] + [[package]] name = "keccak" version = "0.1.0" @@ -2316,6 +2499,25 @@ dependencies = [ "autocfg 1.0.1", ] +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log 0.4.14", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + [[package]] name = "mio" version = "0.7.13" @@ -2324,11 +2526,23 @@ checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" dependencies = [ "libc", "log 0.4.14", - "miow", + "miow 0.3.7", "ntapi", "winapi 0.3.9", ] +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + [[package]] name = "miow" version = "0.3.7" @@ -2422,6 +2636,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "newtype-ops" version = "0.1.4" @@ -2817,6 +3042,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" +[[package]] +name = "patricia_tree" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c4b8ef84caee22395fa083b7d8ee9351e71cdf69a46c832528acdcac402117" +dependencies = [ + "bitflags 1.3.2", + "bytecodec", + "trackable 0.2.24", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -3421,7 +3657,7 @@ dependencies = [ "futures-core", "futures-util", "http", - "http-body", + "http-body 0.4.3", "hyper 0.14.12", "hyper-tls", "ipnet", @@ -3885,7 +4121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" dependencies = [ "libc", - "mio", + "mio 0.7.13", "signal-hook-registry", ] @@ -3944,6 +4180,17 @@ dependencies = [ "x25519-dalek", ] +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + [[package]] name = "socket2" version = "0.4.1" @@ -4135,7 +4382,7 @@ dependencies = [ "tari_common_types", "tari_comms", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_wallet", "tonic", "tonic-build", @@ -4159,7 +4406,7 @@ dependencies = [ "tari_common_types", "tari_comms", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_p2p", "tari_wallet", "thiserror", @@ -4192,7 +4439,7 @@ dependencies = [ "tari_comms", "tari_comms_dht", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_mmr", "tari_p2p", "tari_service_framework", @@ -4205,6 +4452,26 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tari_bulletproofs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c04fe42cf122b2b5a77f1f4e20562e05c422efddb2ede470d7d8988cc2bf3a7" +dependencies = [ + "byteorder", + "clear_on_drop", + "curve25519-dalek-ng", + "digest", + "merlin", + "rand 0.8.4", + "rand_core 0.6.3", + "serde 1.0.130", + "serde_derive", + "sha3", + "subtle-ng", + "thiserror", +] + [[package]] name = "tari_bulletproofs" version = "4.1.0" @@ -4245,7 +4512,7 @@ dependencies = [ "sha2", "structopt", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.10.1", "tempfile", "toml 0.5.8", "tracing", @@ -4262,7 +4529,7 @@ dependencies = [ "lazy_static 1.4.0", "rand 0.8.4", "serde 1.0.130", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tokio 1.11.0", ] @@ -4299,15 +4566,15 @@ dependencies = [ "snow", "tari_common", "tari_comms_rpc_macros", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.10.1", "tempfile", "thiserror", "tokio 1.11.0", "tokio-stream", - "tokio-util", + "tokio-util 0.6.7", "tower 0.3.1", "tower-make", "tracing", @@ -4347,10 +4614,10 @@ dependencies = [ "tari_common", "tari_comms", "tari_comms_rpc_macros", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.10.1", "tari_utilities", "tempfile", "thiserror", @@ -4372,7 +4639,7 @@ dependencies = [ "quote 1.0.9", "syn 1.0.75", "tari_comms", - "tari_test_utils", + "tari_test_utils 0.10.1", "tokio 1.11.0", "tower-service", ] @@ -4384,7 +4651,7 @@ dependencies = [ "bitflags 1.3.2", "chrono", "chrono-english", - "crossterm", + "crossterm 0.17.7", "futures 0.3.16", "log 0.4.14", "opentelemetry", @@ -4403,7 +4670,7 @@ dependencies = [ "tari_comms", "tari_comms_dht", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_key_manager", "tari_p2p", "tari_shutdown", @@ -4456,13 +4723,13 @@ dependencies = [ "tari_comms", "tari_comms_dht", "tari_comms_rpc_macros", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_mmr", "tari_p2p", "tari_service_framework", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.10.1", "tempfile", "thiserror", "tokio 1.11.0", @@ -4473,6 +4740,31 @@ dependencies = [ "uint", ] +[[package]] +name = "tari_crypto" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdb1f6ef0afe1aae900cd1b7558e84be17047677b9e184f3f2600e268ff3fa3" +dependencies = [ + "base64 0.10.1", + "blake2", + "cbindgen", + "clear_on_drop", + "curve25519-dalek-ng", + "digest", + "lazy_static 1.4.0", + "merlin", + "rand 0.8.4", + "rmp-serde", + "serde 1.0.130", + "serde_json", + "sha2", + "sha3", + "tari_bulletproofs 4.0.0", + "tari_utilities", + "thiserror", +] + [[package]] name = "tari_crypto" version = "0.11.2" @@ -4492,11 +4784,43 @@ dependencies = [ "serde_json", "sha2", "sha3", - "tari_bulletproofs", + "tari_bulletproofs 4.1.0", "tari_utilities", "thiserror", ] +[[package]] +name = "tari_dan_node" +version = "0.8.11" +dependencies = [ + "anyhow", + "async-trait", + "bytecodec", + "clap", + "digest", + "futures 0.3.16", + "log 0.4.14", + "patricia_tree", + "prost", + "prost-types", + "serde_json", + "tari_app_utilities", + "tari_common", + "tari_comms", + "tari_comms_dht", + "tari_crypto 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tari_mmr", + "tari_p2p", + "tari_service_framework", + "tari_shutdown", + "tari_test_utils 0.8.1", + "thiserror", + "tokio 1.11.0", + "tokio-stream", + "tonic", + "tonic-build", +] + [[package]] name = "tari_infra_derive" version = "0.10.1" @@ -4507,6 +4831,15 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "tari_json_rpc_proxy" +version = "0.1.0" +dependencies = [ + "jsonrpc-http-server", + "tari_app_grpc", + "tari_utilities", +] + [[package]] name = "tari_key_manager" version = "0.10.1" @@ -4517,7 +4850,7 @@ dependencies = [ "serde_derive", "serde_json", "sha2", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "thiserror", ] @@ -4547,7 +4880,7 @@ dependencies = [ "tari_app_utilities", "tari_common", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_utilities", "thiserror", "tokio 1.11.0", @@ -4581,7 +4914,7 @@ dependencies = [ "tari_app_utilities", "tari_common", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "thiserror", "time", "tokio 1.11.0", @@ -4601,7 +4934,7 @@ dependencies = [ "rand 0.8.4", "serde 1.0.130", "serde_json", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_infra_derive", "tari_utilities", "thiserror", @@ -4634,11 +4967,11 @@ dependencies = [ "tari_common", "tari_comms", "tari_comms_dht", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_service_framework", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.10.1", "tari_utilities", "tempfile", "thiserror", @@ -4659,7 +4992,7 @@ dependencies = [ "futures-test", "log 0.4.14", "tari_shutdown", - "tari_test_utils", + "tari_test_utils 0.10.1", "thiserror", "tokio 1.11.0", "tower 0.3.1", @@ -4704,7 +5037,7 @@ dependencies = [ "tari_common", "tari_comms", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_utilities", "thiserror", ] @@ -4733,7 +5066,7 @@ dependencies = [ "tari_app_grpc", "tari_common", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_utilities", "thiserror", "tokio 1.11.0", @@ -4745,6 +5078,20 @@ dependencies = [ "url 2.2.2", ] +[[package]] +name = "tari_test_utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f11c0804a3f136ad0f821981411215886f0039bf1519c4ec0ec0af8098a8def" +dependencies = [ + "futures 0.3.16", + "futures-test", + "lazy_static 1.4.0", + "rand 0.7.3", + "tempfile", + "tokio 0.2.25", +] + [[package]] name = "tari_test_utils" version = "0.10.1" @@ -4804,13 +5151,13 @@ dependencies = [ "tari_comms", "tari_comms_dht", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_key_manager", "tari_p2p", "tari_service_framework", "tari_shutdown", "tari_storage", - "tari_test_utils", + "tari_test_utils 0.10.1", "tempfile", "thiserror", "time", @@ -4820,7 +5167,7 @@ dependencies = [ [[package]] name = "tari_wallet_ffi" -version = "0.18.7" +version = "0.18.8" dependencies = [ "chrono", "env_logger 0.7.1", @@ -4835,11 +5182,11 @@ dependencies = [ "tari_comms", "tari_comms_dht", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_key_manager", "tari_p2p", "tari_shutdown", - "tari_test_utils", + "tari_test_utils 0.10.1", "tari_utilities", "tari_wallet", "tempfile", @@ -4879,7 +5226,7 @@ dependencies = [ "serde_json", "tari_common_types", "tari_core", - "tari_crypto", + "tari_crypto 0.11.2 (git+https://github.com/tari-project/tari-crypto.git?branch=main)", "tari_utilities", "tokio 1.11.0", ] @@ -5009,6 +5356,11 @@ dependencies = [ "bytes 0.5.6", "fnv", "futures-core", + "iovec", + "lazy_static 1.4.0", + "memchr", + "mio 0.6.23", + "num_cpus", "pin-project-lite 0.1.12", "slab", ] @@ -5023,7 +5375,7 @@ dependencies = [ "bytes 1.1.0", "libc", "memchr", - "mio", + "mio 0.7.13", "num_cpus", "once_cell", "pin-project-lite 0.2.7", @@ -5083,7 +5435,7 @@ dependencies = [ "futures-core", "pin-project-lite 0.2.7", "tokio 1.11.0", - "tokio-util", + "tokio-util 0.6.7", ] [[package]] @@ -5110,6 +5462,20 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log 0.4.14", + "pin-project-lite 0.1.12", + "tokio 0.2.25", +] + [[package]] name = "tokio-util" version = "0.6.7" @@ -5155,9 +5521,9 @@ dependencies = [ "bytes 1.1.0", "futures-core", "futures-util", - "h2", + "h2 0.3.4", "http", - "http-body", + "http-body 0.4.3", "hyper 0.14.12", "hyper-timeout", "percent-encoding 2.1.0", @@ -5166,7 +5532,7 @@ dependencies = [ "prost-derive", "tokio 1.11.0", "tokio-stream", - "tokio-util", + "tokio-util 0.6.7", "tower 0.4.8", "tower-layer", "tower-service", @@ -5219,7 +5585,7 @@ dependencies = [ "slab", "tokio 1.11.0", "tokio-stream", - "tokio-util", + "tokio-util 0.6.7", "tower-layer", "tower-service", "tracing", @@ -5461,6 +5827,35 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trackable" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98abb9e7300b9ac902cc04920945a874c1973e08c310627cc4458c04b70dd32" +dependencies = [ + "trackable 1.2.0", + "trackable_derive", +] + +[[package]] +name = "trackable" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "017e2a1a93718e4e8386d037cfb8add78f1d690467f4350fb582f55af1203167" +dependencies = [ + "trackable_derive", +] + +[[package]] +name = "trackable_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" +dependencies = [ + "quote 1.0.9", + "syn 1.0.75", +] + [[package]] name = "traitobject" version = "0.1.0" @@ -5545,13 +5940,13 @@ dependencies = [ [[package]] name = "tui" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2eaeee894a1e9b90f80aa466fe59154fdb471980b5e104d8836fcea309ae17e" +checksum = "861d8f3ad314ede6219bcb2ab844054b1de279ee37a9bc38e3d606f9d3fb2a71" dependencies = [ "bitflags 1.3.2", "cassowary", - "crossterm", + "crossterm 0.19.0", "unicode-segmentation", "unicode-width", ] @@ -5615,6 +6010,15 @@ dependencies = [ "version_check 0.1.5", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check 0.9.3", +] + [[package]] name = "unicode-bidi" version = "0.3.6" @@ -5937,6 +6341,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "x25519-dalek" version = "1.1.1" diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index 697d8acf5b..596929650a 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -77,7 +77,7 @@ use crate::{ }; use std::collections::VecDeque; use tari_core::transactions::transaction_protocol::TxId; -use tari_wallet::{assets::Asset, tokens::Token}; +use tari_wallet::{assets::Asset, output_manager_service::handle::OutputManagerHandle, tokens::Token}; const LOG_TARGET: &str = "wallet::console_wallet::app_state"; diff --git a/applications/tari_dan_node/src/dan_layer/dan_node.rs b/applications/tari_dan_node/src/dan_layer/dan_node.rs index 0d03f5f21f..8fd157cc9a 100644 --- a/applications/tari_dan_node/src/dan_layer/dan_node.rs +++ b/applications/tari_dan_node/src/dan_layer/dan_node.rs @@ -70,7 +70,7 @@ use tari_comms_dht::{DbConnectionUrl, Dht, DhtConfig}; use tari_crypto::tari_utilities::hex::{Hex, HexError}; use tari_p2p::{ comms_connector::{pubsub_connector, PubsubDomainConnector, SubscriptionFactory}, - initialization::{spawn_comms_using_transport, CommsConfig, P2pInitializer}, + initialization::{spawn_comms_using_transport, P2pConfig, P2pInitializer}, tari_message::TariMessageType, transport::{TorConfig, TransportType}, }; @@ -199,8 +199,8 @@ impl DanNode { Ok((handles, peer_message_subscriptions)) } - fn create_comms_config(&self, node_identity: Arc) -> CommsConfig { - CommsConfig { + fn create_comms_config(&self, node_identity: Arc) -> P2pConfig { + P2pConfig { network: self.config.network, node_identity: node_identity.clone(), transport_type: self.create_transport_type(), diff --git a/applications/tari_dan_node/src/dan_layer/models/instruction.rs b/applications/tari_dan_node/src/dan_layer/models/instruction.rs index a115b28edd..acfc617511 100644 --- a/applications/tari_dan_node/src/dan_layer/models/instruction.rs +++ b/applications/tari_dan_node/src/dan_layer/models/instruction.rs @@ -27,6 +27,8 @@ use crate::{ use digest::Digest; use tari_crypto::{common::Blake256, tari_utilities::ByteArray}; +// TODO: fix hash derive +#[allow(clippy::derive_hash_xor_eq)] #[derive(Clone, Debug, Hash)] pub struct Instruction { asset_id: PublicKey, diff --git a/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs b/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs index 3b6d748757..fd22a5eabc 100644 --- a/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs +++ b/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs @@ -35,6 +35,7 @@ impl InstructionSetHash { } // TODO: Implement hash properly +#[allow(clippy::derive_hash_xor_eq)] #[derive(Clone, Debug, Hash)] pub struct InstructionSet { hash: InstructionSetHash, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 6f398f6a9c..3499986871 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - output_manager_service::{handle::OutputManagerHandle, TxId}, + output_manager_service::handle::OutputManagerHandle, storage::database::{WalletBackend, WalletDatabase}, transaction_service::{ config::TransactionServiceConfig, @@ -46,6 +46,7 @@ use crate::{ }, types::{HashDigest, ValidationRetryStrategy}, utxo_scanner_service::utxo_scanning::RECOVERY_KEY, + OperationId, }; use chrono::{NaiveDateTime, Utc}; use digest::Digest; diff --git a/rust-toolchain b/rust-toolchain index 378367865c..63dc7f53b2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2021-08-17 +nightly-2021-08-18 From b5d973948a05ead175dc12e4667c6d8eb07812e9 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Tue, 5 Oct 2021 12:56:44 +0200 Subject: [PATCH 13/77] refactor: rename validator node (#3406) * refactor: rename to tari_validator_node * refactor: rename dan node to validator node * fix clippy errors * autofix clippy errors --- Cargo.lock | 256 +++--------------- Cargo.toml | 3 +- .../tari_app_utilities/src/initialization.rs | 9 +- applications/tari_json_rpc_proxy/Cargo.toml | 12 - applications/tari_json_rpc_proxy/src/main.rs | 126 --------- .../tari_merge_mining_proxy/src/main.rs | 10 +- .../tari_merge_mining_proxy/src/proxy.rs | 24 +- .../Cargo.toml | 8 +- .../build.rs | 2 +- .../proto/validator_node.proto} | 4 +- .../src/cmd_args.rs | 0 .../src/dan_layer/dan_node.rs | 2 +- .../src/dan_layer/mod.rs | 0 .../src/dan_layer/models/block.rs | 0 .../src/dan_layer/models/committee.rs | 0 .../consensus_worker_domain_event.rs | 0 .../src/dan_layer/models/domain_events/mod.rs | 0 .../src/dan_layer/models/hot_stuff_message.rs | 0 .../dan_layer/models/hot_stuff_tree_node.rs | 0 .../src/dan_layer/models/instruction.rs | 0 .../src/dan_layer/models/instruction_set.rs | 0 .../src/dan_layer/models/mod.rs | 0 .../dan_layer/models/quorum_certificate.rs | 0 .../src/dan_layer/models/replica_info.rs | 0 .../src/dan_layer/models/view.rs | 0 .../src/dan_layer/models/view_id.rs | 0 .../dan_layer/services/bft_replica_service.rs | 0 .../dan_layer/services/events_publisher.rs | 0 .../inbound_connection_service.rs | 3 +- .../infrastructure_services/mocks/mod.rs | 0 .../services/infrastructure_services/mod.rs | 0 .../node_addressable.rs | 0 .../outbound_service.rs | 1 + .../src/dan_layer/services/mempool_service.rs | 0 .../src/dan_layer/services/mocks/mod.rs | 0 .../src/dan_layer/services/mod.rs | 0 .../dan_layer/services/payload_processor.rs | 0 .../dan_layer/services/payload_provider.rs | 0 .../src/dan_layer/services/signing_service.rs | 0 .../dan_layer/services/template_service.rs | 0 .../src/dan_layer/storage/asset_data_store.rs | 0 .../src/dan_layer/storage/mod.rs | 0 .../src/dan_layer/template_command.rs | 0 .../templates/editable_metadata_template.rs | 0 .../src/dan_layer/templates/mod.rs | 0 .../src/dan_layer/workers/consensus_worker.rs | 0 .../src/dan_layer/workers/mod.rs | 0 .../dan_layer/workers/states/commit_state.rs | 0 .../dan_layer/workers/states/decide_state.rs | 4 - .../src/dan_layer/workers/states/mod.rs | 0 .../src/dan_layer/workers/states/next_view.rs | 0 .../workers/states/pre_commit_state.rs | 4 - .../src/dan_layer/workers/states/prepare.rs | 2 - .../src/dan_layer/workers/states/starting.rs | 0 .../src/dan_node_config.rs | 0 .../src/digital_assets_error.rs | 0 .../src/grpc/mod.rs | 6 +- .../src/grpc/validator_node_grpc_server.rs} | 22 +- .../src/main.rs | 10 +- .../src/p2p/mod.rs | 0 .../p2p/proto/dan_consensus_messages.proto | 0 .../src/types.rs | 0 .../core/src/consensus/consensus_manager.rs | 2 +- base_layer/core/src/proto/transaction.rs | 10 +- .../core/src/transactions/coinbase_builder.rs | 4 +- .../core/src/transactions/transaction/mod.rs | 9 +- .../sender_transaction_protocol_builder.rs | 10 +- base_layer/wallet/src/assets/asset_manager.rs | 5 +- .../wallet/src/assets/asset_manager_handle.rs | 4 +- .../src/output_manager_service/service.rs | 4 +- .../output_manager_service/storage/models.rs | 4 +- .../storage/sqlite_db/mod.rs | 9 +- .../storage/sqlite_db/new_output_sql.rs | 8 +- .../storage/sqlite_db/output_sql.rs | 8 +- base_layer/wallet/src/tokens/token_manager.rs | 8 +- .../config/presets/tari_config_example.toml | 50 ++-- common/src/configuration/global.rs | 132 ++++----- .../src/configuration/merge_mining_config.rs | 32 +++ common/src/configuration/mod.rs | 7 +- ...dan_config.rs => validator_node_config.rs} | 24 +- comms/Cargo.toml | 2 +- scripts/update_crate_metadata.sh | 1 + 82 files changed, 245 insertions(+), 596 deletions(-) delete mode 100644 applications/tari_json_rpc_proxy/Cargo.toml delete mode 100644 applications/tari_json_rpc_proxy/src/main.rs rename applications/{tari_dan_node => tari_validator_node}/Cargo.toml (89%) rename applications/{tari_dan_node => tari_validator_node}/build.rs (96%) rename applications/{tari_dan_node/proto/dan_node.proto => tari_validator_node/proto/validator_node.proto} (97%) rename applications/{tari_dan_node => tari_validator_node}/src/cmd_args.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/dan_node.rs (99%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/block.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/committee.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/domain_events/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/hot_stuff_message.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/hot_stuff_tree_node.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/instruction.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/instruction_set.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/quorum_certificate.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/replica_info.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/view.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/models/view_id.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/bft_replica_service.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/events_publisher.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs (98%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/infrastructure_services/mocks/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/infrastructure_services/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/infrastructure_services/node_addressable.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/infrastructure_services/outbound_service.rs (98%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/mempool_service.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/mocks/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/payload_processor.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/payload_provider.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/signing_service.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/services/template_service.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/storage/asset_data_store.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/storage/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/template_command.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/templates/editable_metadata_template.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/templates/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/consensus_worker.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/commit_state.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/decide_state.rs (97%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/next_view.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/pre_commit_state.rs (97%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/prepare.rs (98%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_layer/workers/states/starting.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/dan_node_config.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/digital_assets_error.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/grpc/mod.rs (92%) rename applications/{tari_dan_node/src/grpc/dan_grpc_server.rs => tari_validator_node/src/grpc/validator_node_grpc_server.rs} (82%) rename applications/{tari_dan_node => tari_validator_node}/src/main.rs (93%) rename applications/{tari_dan_node => tari_validator_node}/src/p2p/mod.rs (100%) rename applications/{tari_dan_node => tari_validator_node}/src/p2p/proto/dan_consensus_messages.proto (100%) rename applications/{tari_dan_node => tari_validator_node}/src/types.rs (100%) create mode 100644 common/src/configuration/merge_mining_config.rs rename common/src/configuration/{dan_config.rs => validator_node_config.rs} (80%) diff --git a/Cargo.lock b/Cargo.lock index 5cc0232ab6..90d423c2b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1712,39 +1712,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -[[package]] -name = "globset" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log 0.4.14", - "regex", -] - -[[package]] -name = "h2" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio 0.2.25", - "tokio-util 0.3.1", - "tracing", - "tracing-futures", -] - [[package]] name = "h2" version = "0.3.4" @@ -1760,7 +1727,7 @@ dependencies = [ "indexmap", "slab", "tokio 1.11.0", - "tokio-util 0.6.7", + "tokio-util", "tracing", ] @@ -1825,16 +1792,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" -dependencies = [ - "bytes 0.5.6", - "http", -] - [[package]] name = "http-body" version = "0.4.3" @@ -1852,12 +1809,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" -[[package]] -name = "httpdate" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" - [[package]] name = "httpdate" version = "1.0.1" @@ -1894,34 +1845,10 @@ dependencies = [ "time", "traitobject", "typeable", - "unicase 1.4.2", + "unicase", "url 1.7.2", ] -[[package]] -name = "hyper" -version = "0.13.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" -dependencies = [ - "bytes 0.5.6", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.2.7", - "http", - "http-body 0.3.1", - "httparse", - "httpdate 0.3.2", - "itoa", - "pin-project 1.0.8", - "socket2 0.3.19", - "tokio 0.2.25", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "0.14.12" @@ -1932,14 +1859,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.4", + "h2", "http", - "http-body 0.4.3", + "http-body", "httparse", - "httpdate 1.0.1", + "httpdate", "itoa", "pin-project-lite 0.2.7", - "socket2 0.4.1", + "socket2", "tokio 1.11.0", "tower-service", "tracing", @@ -2098,54 +2025,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "jsonrpc-core" -version = "17.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4467ab6dfa369b69e52bd0692e480c4d117410538526a57a304a0f2250fd95e" -dependencies = [ - "futures 0.3.16", - "futures-executor", - "futures-util", - "log 0.4.14", - "serde 1.0.130", - "serde_derive", - "serde_json", -] - -[[package]] -name = "jsonrpc-http-server" -version = "17.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522a047cac0958097ee71d047dd71cb84979fd2fa21c7a68fbe12736bef870a2" -dependencies = [ - "futures 0.3.16", - "hyper 0.13.10", - "jsonrpc-core", - "jsonrpc-server-utils", - "log 0.4.14", - "net2", - "parking_lot 0.11.2", - "unicase 2.6.0", -] - -[[package]] -name = "jsonrpc-server-utils" -version = "17.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce68fa279a2822b3619369cd024f8a4f8e5ce485468834f8679a3c7919aae2d" -dependencies = [ - "bytes 0.5.6", - "futures 0.3.16", - "globset", - "jsonrpc-core", - "lazy_static 1.4.0", - "log 0.4.14", - "tokio 0.2.25", - "tokio-util 0.3.1", - "unicase 2.6.0", -] - [[package]] name = "keccak" version = "0.1.0" @@ -3657,7 +3536,7 @@ dependencies = [ "futures-core", "futures-util", "http", - "http-body 0.4.3", + "http-body", "hyper 0.14.12", "hyper-tls", "ipnet", @@ -4180,17 +4059,6 @@ dependencies = [ "x25519-dalek", ] -[[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi 0.3.9", -] - [[package]] name = "socket2" version = "0.4.1" @@ -4574,7 +4442,7 @@ dependencies = [ "thiserror", "tokio 1.11.0", "tokio-stream", - "tokio-util 0.6.7", + "tokio-util", "tower 0.3.1", "tower-make", "tracing", @@ -4789,38 +4657,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "tari_dan_node" -version = "0.8.11" -dependencies = [ - "anyhow", - "async-trait", - "bytecodec", - "clap", - "digest", - "futures 0.3.16", - "log 0.4.14", - "patricia_tree", - "prost", - "prost-types", - "serde_json", - "tari_app_utilities", - "tari_common", - "tari_comms", - "tari_comms_dht", - "tari_crypto 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tari_mmr", - "tari_p2p", - "tari_service_framework", - "tari_shutdown", - "tari_test_utils 0.8.1", - "thiserror", - "tokio 1.11.0", - "tokio-stream", - "tonic", - "tonic-build", -] - [[package]] name = "tari_infra_derive" version = "0.10.1" @@ -4831,15 +4667,6 @@ dependencies = [ "syn 0.15.44", ] -[[package]] -name = "tari_json_rpc_proxy" -version = "0.1.0" -dependencies = [ - "jsonrpc-http-server", - "tari_app_grpc", - "tari_utilities", -] - [[package]] name = "tari_key_manager" version = "0.10.1" @@ -5123,6 +4950,38 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tari_validator_node" +version = "0.10.1" +dependencies = [ + "anyhow", + "async-trait", + "bytecodec", + "clap", + "digest", + "futures 0.3.16", + "log 0.4.14", + "patricia_tree", + "prost", + "prost-types", + "serde_json", + "tari_app_utilities", + "tari_common", + "tari_comms", + "tari_comms_dht", + "tari_crypto 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tari_mmr", + "tari_p2p", + "tari_service_framework", + "tari_shutdown", + "tari_test_utils 0.8.1", + "thiserror", + "tokio 1.11.0", + "tokio-stream", + "tonic", + "tonic-build", +] + [[package]] name = "tari_wallet" version = "0.10.1" @@ -5356,9 +5215,7 @@ dependencies = [ "bytes 0.5.6", "fnv", "futures-core", - "iovec", "lazy_static 1.4.0", - "memchr", "mio 0.6.23", "num_cpus", "pin-project-lite 0.1.12", @@ -5435,7 +5292,7 @@ dependencies = [ "futures-core", "pin-project-lite 0.2.7", "tokio 1.11.0", - "tokio-util 0.6.7", + "tokio-util", ] [[package]] @@ -5462,20 +5319,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "tokio-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log 0.4.14", - "pin-project-lite 0.1.12", - "tokio 0.2.25", -] - [[package]] name = "tokio-util" version = "0.6.7" @@ -5521,9 +5364,9 @@ dependencies = [ "bytes 1.1.0", "futures-core", "futures-util", - "h2 0.3.4", + "h2", "http", - "http-body 0.4.3", + "http-body", "hyper 0.14.12", "hyper-timeout", "percent-encoding 2.1.0", @@ -5532,7 +5375,7 @@ dependencies = [ "prost-derive", "tokio 1.11.0", "tokio-stream", - "tokio-util 0.6.7", + "tokio-util", "tower 0.4.8", "tower-layer", "tower-service", @@ -5585,7 +5428,7 @@ dependencies = [ "slab", "tokio 1.11.0", "tokio-stream", - "tokio-util 0.6.7", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -6010,15 +5853,6 @@ dependencies = [ "version_check 0.1.5", ] -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check 0.9.3", -] - [[package]] name = "unicode-bidi" version = "0.3.6" diff --git a/Cargo.toml b/Cargo.toml index 4432e38b34..bfe740e8ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,7 @@ members = [ "applications/tari_merge_mining_proxy", "applications/tari_stratum_transcoder", "applications/tari_mining_node", - "applications/tari_dan_node", - "applications/tari_json_rpc_proxy" + "applications/tari_validator_node", ] # #[profile.release] diff --git a/applications/tari_app_utilities/src/initialization.rs b/applications/tari_app_utilities/src/initialization.rs index 2497788307..602ae89911 100644 --- a/applications/tari_app_utilities/src/initialization.rs +++ b/applications/tari_app_utilities/src/initialization.rs @@ -84,14 +84,7 @@ fn check_file_paths(config: &mut GlobalConfig, bootstrap: &ConfigBootstrap) { config.console_wallet_peer_db_path = concatenate_paths_normalized(prepend.clone(), config.console_wallet_peer_db_path.clone()); } - if !config.console_wallet_identity_file.is_absolute() { - config.console_wallet_identity_file = - concatenate_paths_normalized(prepend.clone(), config.console_wallet_identity_file.clone()); - } - if !config.console_wallet_tor_identity_file.is_absolute() { - config.console_wallet_tor_identity_file = - concatenate_paths_normalized(prepend.clone(), config.console_wallet_tor_identity_file.clone()); - } + if !config.wallet_db_file.is_absolute() { config.wallet_db_file = concatenate_paths_normalized(prepend.clone(), config.wallet_db_file.clone()); } diff --git a/applications/tari_json_rpc_proxy/Cargo.toml b/applications/tari_json_rpc_proxy/Cargo.toml deleted file mode 100644 index 284f38e003..0000000000 --- a/applications/tari_json_rpc_proxy/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "tari_json_rpc_proxy" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tari_app_grpc = {path="../tari_app_grpc"} -tari_utilities = "^0.3" - -jsonrpc-http-server = "17.1.0" diff --git a/applications/tari_json_rpc_proxy/src/main.rs b/applications/tari_json_rpc_proxy/src/main.rs deleted file mode 100644 index 3dd12c012f..0000000000 --- a/applications/tari_json_rpc_proxy/src/main.rs +++ /dev/null @@ -1,126 +0,0 @@ -use jsonrpc_http_server::{ - jsonrpc_core::{IoHandler, Params, Value}, - ServerBuilder, -}; - -use tari_app_grpc::tari_rpc as grpc; -use tari_utilities::hex::Hex; - -fn main() { - let mut io = IoHandler::default(); - io.add_method("say_hello", |_params: Params| async { - dbg!("Hello called"); - Ok(Value::String("hello".to_owned())) - }); - - io.add_method("eth_chainId", |_params: Params| async { - dbg!("eth_chainId called"); - Ok(Value::String("0x88".to_owned())) - }); - - io.add_method("eth_blockNumber", |_params: Params| async { - dbg!("eth_blockNumber called"); - Ok(Value::String("0x7777".to_owned())) - }); - - io.add_method("net_version", |_params: Params| async { - dbg!("net_version called"); - Ok(Value::String("Test".to_owned())) - }); - - io.add_method("eth_call", |params: Params| async { - dbg!("eth_call called"); - dbg!(¶ms); - - struct CallParams { - data: String, - to: String, - } - - let call_data = match params { - Params::Array(values) => { - let v = values.first().unwrap(); - CallParams { - data: v["data"].as_str().unwrap().to_string(), - to: v["to"].as_str().unwrap().to_string(), - } - }, - _ => return Ok(Value::String("Unexpected".to_owned())), - }; - - match &call_data.data.as_str()[0..10] { - "0x313ce567" => { - // decimals - Ok(Value::String("0x00".to_owned())) - }, - "0x95d89b41" => - // symbol - { - Ok(Value::String("TXTR2".to_owned())) - }, - "0x70a08231" => - // balance - { - Ok(Value::String("0x17".to_owned())) - }, - _ => Ok(Value::String("don't know".to_owned())), - } - }); - - io.add_method("eth_estimateGas", |_params: Params| async { - dbg!("eth_estimateGas called"); - Ok(Value::String("0x1".to_owned())) - }); - - io.add_method("eth_gasPrice", |_params: Params| async { - dbg!("eth_gasPrice called"); - Ok(Value::String("0x1".to_owned())) - }); - - io.add_method("eth_getTransactionCount", |_params: Params| async { - dbg!("eth_getTransactionCount called"); - Ok(Value::String("0x00".to_owned())) - }); - - io.add_method("eth_sendRawTransaction", |params: Params| async { - dbg!("eth_sendRawTransaction called"); - Ok(Value::String("not yet impl".to_owned())) - }); - - io.add_method("eth_getBalance", |params: Params| async { - dbg!("eth_getBalance called"); - dbg!(params); - Ok(Value::String("0x00".to_owned())) - }); - - io.add_method("eth_accounts", |_params: Params| async { - dbg!("eth_accounts called"); - - let accounts = get_wallet_accounts().await.unwrap(); - Ok(Value::Array( - accounts.into_iter().map(|s| Value::String(s.to_string())).collect(), - )) - }); - - let server = ServerBuilder::new(io) - .threads(3) - .start_http(&"127.0.0.1:3030".parse().unwrap()) - .unwrap(); - - server.wait(); -} - -async fn get_wallet_accounts() -> Result, String> { - let mut wallet_client = grpc::wallet_client::WalletClient::connect(format!("http://{}", "127.0.0.1:18144")) - .await - .unwrap(); - let owned = wallet_client - .get_owned_tokens(grpc::GetOwnedTokensRequest { - asset_public_key: Hex::from_hex("d458e49c9fe023e6706db830c7520ce520ffbbfe22edb26f5c3379d2d4a9b838") - .unwrap(), - }) - .await - .unwrap() - .into_inner(); - Ok(owned.tokens.into_iter().map(|o| o.unique_id.to_hex()).collect()) -} diff --git a/applications/tari_merge_mining_proxy/src/main.rs b/applications/tari_merge_mining_proxy/src/main.rs index 0df27490bb..22328f0ee9 100644 --- a/applications/tari_merge_mining_proxy/src/main.rs +++ b/applications/tari_merge_mining_proxy/src/main.rs @@ -40,7 +40,7 @@ use crate::{block_template_data::BlockTemplateRepository, error::MmProxyError}; use futures::future; use hyper::{service::make_service_fn, Server}; use proxy::{MergeMiningProxyConfig, MergeMiningProxyService}; -use std::convert::Infallible; +use std::convert::{Infallible, TryFrom}; use tari_app_grpc::tari_rpc as grpc; use tari_app_utilities::initialization::init_configuration; use tari_common::configuration::bootstrap::ApplicationType; @@ -50,7 +50,13 @@ use tokio::time::Duration; async fn main() -> Result<(), anyhow::Error> { let (_, config, _) = init_configuration(ApplicationType::MergeMiningProxy)?; - let config = MergeMiningProxyConfig::from(config); + let config = match MergeMiningProxyConfig::try_from(config) { + Ok(c) => c, + Err(msg) => { + eprintln!("Invalid config: {}", msg); + return Ok(()); + }, + }; let addr = config.proxy_host_address; let client = reqwest::Client::builder() .connect_timeout(Duration::from_secs(5)) diff --git a/applications/tari_merge_mining_proxy/src/proxy.rs b/applications/tari_merge_mining_proxy/src/proxy.rs index ff31d8e8f9..2db8e83666 100644 --- a/applications/tari_merge_mining_proxy/src/proxy.rs +++ b/applications/tari_merge_mining_proxy/src/proxy.rs @@ -34,6 +34,7 @@ use reqwest::{ResponseBuilderExt, Url}; use serde_json as json; use std::{ cmp, + convert::TryFrom, future::Future, net::SocketAddr, pin::Pin, @@ -70,20 +71,25 @@ pub struct MergeMiningProxyConfig { pub wait_for_initial_sync_at_startup: bool, } -impl From for MergeMiningProxyConfig { - fn from(config: GlobalConfig) -> Self { - Self { +impl TryFrom for MergeMiningProxyConfig { + type Error = String; + + fn try_from(config: GlobalConfig) -> Result { + let inner = config + .merge_mining_config + .ok_or_else(|| "Merge mining config was missing".to_string())?; + Ok(Self { network: config.network, - monerod_url: config.monerod_url, - monerod_username: config.monerod_username, - monerod_password: config.monerod_password, - monerod_use_auth: config.monerod_use_auth, + monerod_url: inner.monerod_url, + monerod_username: inner.monerod_username, + monerod_password: inner.monerod_password, + monerod_use_auth: inner.monerod_use_auth, grpc_base_node_address: config.grpc_base_node_address, grpc_console_wallet_address: config.grpc_console_wallet_address, - proxy_host_address: config.proxy_host_address, + proxy_host_address: inner.proxy_host_address, proxy_submit_to_origin: config.proxy_submit_to_origin, wait_for_initial_sync_at_startup: config.wait_for_initial_sync_at_startup, - } + }) } } diff --git a/applications/tari_dan_node/Cargo.toml b/applications/tari_validator_node/Cargo.toml similarity index 89% rename from applications/tari_dan_node/Cargo.toml rename to applications/tari_validator_node/Cargo.toml index 4be659ea04..c205432d2e 100644 --- a/applications/tari_dan_node/Cargo.toml +++ b/applications/tari_validator_node/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "tari_dan_node" +name = "tari_validator_node" authors = ["The Tari Development Community"] -description = "The tari digital assets network node implementation" +description = "The Tari validator node implementation" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.8.11" +version = "0.10.1" edition = "2018" [dependencies] @@ -13,7 +13,7 @@ tari_crypto = "0.11.1" tari_test_utils = "0.8.1" tari_app_utilities = {path ="../tari_app_utilities"} tari_common = {path = "../../common"} -tari_comms = {path = "../../comms"} +tari_comms = {path = "../../comms" } tari_service_framework = { path= "../../base_layer/service_framework"} tari_p2p = {path = "../../base_layer/p2p"} tari_comms_dht = {path= "../../comms/dht"} diff --git a/applications/tari_dan_node/build.rs b/applications/tari_validator_node/build.rs similarity index 96% rename from applications/tari_dan_node/build.rs rename to applications/tari_validator_node/build.rs index ab5de164b3..895b75c404 100644 --- a/applications/tari_dan_node/build.rs +++ b/applications/tari_validator_node/build.rs @@ -24,7 +24,7 @@ fn main() -> Result<(), Box> { tonic_build::configure() .build_server(true) .format(false) - .compile(&["proto/dan_node.proto"], &["proto"])?; + .compile(&["proto/validator_node.proto"], &["proto"])?; tari_common::build::ProtobufCompiler::new() .proto_paths(&["src/p2p/proto"]) diff --git a/applications/tari_dan_node/proto/dan_node.proto b/applications/tari_validator_node/proto/validator_node.proto similarity index 97% rename from applications/tari_dan_node/proto/dan_node.proto rename to applications/tari_validator_node/proto/validator_node.proto index f5fddb9064..ac1d9ead42 100644 --- a/applications/tari_dan_node/proto/dan_node.proto +++ b/applications/tari_validator_node/proto/validator_node.proto @@ -21,9 +21,9 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; -package tari.dan.rpc; +package tari.validator_node.rpc; -service DanNode { +service ValidatorNode { rpc GetTokenData(GetTokenDataRequest) returns (GetTokenDataResponse); rpc ExecuteInstruction(ExecuteInstructionRequest) returns (ExecuteInstructionResponse); } diff --git a/applications/tari_dan_node/src/cmd_args.rs b/applications/tari_validator_node/src/cmd_args.rs similarity index 100% rename from applications/tari_dan_node/src/cmd_args.rs rename to applications/tari_validator_node/src/cmd_args.rs diff --git a/applications/tari_dan_node/src/dan_layer/dan_node.rs b/applications/tari_validator_node/src/dan_layer/dan_node.rs similarity index 99% rename from applications/tari_dan_node/src/dan_layer/dan_node.rs rename to applications/tari_validator_node/src/dan_layer/dan_node.rs index 8fd157cc9a..398afc7dd3 100644 --- a/applications/tari_dan_node/src/dan_layer/dan_node.rs +++ b/applications/tari_validator_node/src/dan_layer/dan_node.rs @@ -125,7 +125,7 @@ impl DanNode { let dan_config = self .config - .dan_node + .validator_node .as_ref() .ok_or(ExitCodes::ConfigError("Missing dan section".to_string()))?; diff --git a/applications/tari_dan_node/src/dan_layer/mod.rs b/applications/tari_validator_node/src/dan_layer/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/mod.rs rename to applications/tari_validator_node/src/dan_layer/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/block.rs b/applications/tari_validator_node/src/dan_layer/models/block.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/block.rs rename to applications/tari_validator_node/src/dan_layer/models/block.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/committee.rs b/applications/tari_validator_node/src/dan_layer/models/committee.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/committee.rs rename to applications/tari_validator_node/src/dan_layer/models/committee.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs b/applications/tari_validator_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs rename to applications/tari_validator_node/src/dan_layer/models/domain_events/consensus_worker_domain_event.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/domain_events/mod.rs b/applications/tari_validator_node/src/dan_layer/models/domain_events/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/domain_events/mod.rs rename to applications/tari_validator_node/src/dan_layer/models/domain_events/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/hot_stuff_message.rs b/applications/tari_validator_node/src/dan_layer/models/hot_stuff_message.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/hot_stuff_message.rs rename to applications/tari_validator_node/src/dan_layer/models/hot_stuff_message.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/hot_stuff_tree_node.rs b/applications/tari_validator_node/src/dan_layer/models/hot_stuff_tree_node.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/hot_stuff_tree_node.rs rename to applications/tari_validator_node/src/dan_layer/models/hot_stuff_tree_node.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/instruction.rs b/applications/tari_validator_node/src/dan_layer/models/instruction.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/instruction.rs rename to applications/tari_validator_node/src/dan_layer/models/instruction.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/instruction_set.rs b/applications/tari_validator_node/src/dan_layer/models/instruction_set.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/instruction_set.rs rename to applications/tari_validator_node/src/dan_layer/models/instruction_set.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/mod.rs b/applications/tari_validator_node/src/dan_layer/models/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/mod.rs rename to applications/tari_validator_node/src/dan_layer/models/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/quorum_certificate.rs b/applications/tari_validator_node/src/dan_layer/models/quorum_certificate.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/quorum_certificate.rs rename to applications/tari_validator_node/src/dan_layer/models/quorum_certificate.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/replica_info.rs b/applications/tari_validator_node/src/dan_layer/models/replica_info.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/replica_info.rs rename to applications/tari_validator_node/src/dan_layer/models/replica_info.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/view.rs b/applications/tari_validator_node/src/dan_layer/models/view.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/view.rs rename to applications/tari_validator_node/src/dan_layer/models/view.rs diff --git a/applications/tari_dan_node/src/dan_layer/models/view_id.rs b/applications/tari_validator_node/src/dan_layer/models/view_id.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/models/view_id.rs rename to applications/tari_validator_node/src/dan_layer/models/view_id.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs b/applications/tari_validator_node/src/dan_layer/services/bft_replica_service.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/bft_replica_service.rs rename to applications/tari_validator_node/src/dan_layer/services/bft_replica_service.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/events_publisher.rs b/applications/tari_validator_node/src/dan_layer/services/events_publisher.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/events_publisher.rs rename to applications/tari_validator_node/src/dan_layer/services/events_publisher.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs similarity index 98% rename from applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs rename to applications/tari_validator_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs index ddde53dadf..7caf82bbf7 100644 --- a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs +++ b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/inbound_connection_service.rs @@ -41,7 +41,7 @@ pub trait InboundConnectionService { } pub struct TariCommsInboundConnectionService { - // TODO: remove + // TODO: remove option receiver: Option>, sender: Sender<(CommsPublicKey, HotStuffMessage)>, } @@ -95,7 +95,6 @@ impl TariCommsInboundConnectionService { async fn forward_message(&mut self, message: Arc) -> Result<(), DigitalAssetError> { // let from = message.authenticated_origin.as_ref().unwrap().clone(); let from = message.source_peer.public_key.clone(); - // TODO: Convert hotstuff let proto_message: dan_p2p::HotStuffMessage = message.decode_message().unwrap(); let hot_stuff_message = proto_message .try_into() diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs rename to applications/tari_validator_node/src/dan_layer/services/infrastructure_services/mocks/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mod.rs b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/infrastructure_services/mod.rs rename to applications/tari_validator_node/src/dan_layer/services/infrastructure_services/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/node_addressable.rs b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/node_addressable.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/infrastructure_services/node_addressable.rs rename to applications/tari_validator_node/src/dan_layer/services/infrastructure_services/node_addressable.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/outbound_service.rs similarity index 98% rename from applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs rename to applications/tari_validator_node/src/dan_layer/services/infrastructure_services/outbound_service.rs index 98b6e7f6af..3cb6d227d2 100644 --- a/applications/tari_dan_node/src/dan_layer/services/infrastructure_services/outbound_service.rs +++ b/applications/tari_validator_node/src/dan_layer/services/infrastructure_services/outbound_service.rs @@ -29,6 +29,7 @@ use crate::{ p2p, }; use async_trait::async_trait; +use futures::{future::try_join_all, stream::FuturesUnordered}; use std::marker::PhantomData; use tari_comms::types::CommsPublicKey; use tari_comms_dht::{domain_message::OutboundDomainMessage, outbound::OutboundMessageRequester}; diff --git a/applications/tari_dan_node/src/dan_layer/services/mempool_service.rs b/applications/tari_validator_node/src/dan_layer/services/mempool_service.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/mempool_service.rs rename to applications/tari_validator_node/src/dan_layer/services/mempool_service.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/mocks/mod.rs b/applications/tari_validator_node/src/dan_layer/services/mocks/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/mocks/mod.rs rename to applications/tari_validator_node/src/dan_layer/services/mocks/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/mod.rs b/applications/tari_validator_node/src/dan_layer/services/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/mod.rs rename to applications/tari_validator_node/src/dan_layer/services/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/payload_processor.rs b/applications/tari_validator_node/src/dan_layer/services/payload_processor.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/payload_processor.rs rename to applications/tari_validator_node/src/dan_layer/services/payload_processor.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/payload_provider.rs b/applications/tari_validator_node/src/dan_layer/services/payload_provider.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/payload_provider.rs rename to applications/tari_validator_node/src/dan_layer/services/payload_provider.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/signing_service.rs b/applications/tari_validator_node/src/dan_layer/services/signing_service.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/signing_service.rs rename to applications/tari_validator_node/src/dan_layer/services/signing_service.rs diff --git a/applications/tari_dan_node/src/dan_layer/services/template_service.rs b/applications/tari_validator_node/src/dan_layer/services/template_service.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/services/template_service.rs rename to applications/tari_validator_node/src/dan_layer/services/template_service.rs diff --git a/applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs b/applications/tari_validator_node/src/dan_layer/storage/asset_data_store.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/storage/asset_data_store.rs rename to applications/tari_validator_node/src/dan_layer/storage/asset_data_store.rs diff --git a/applications/tari_dan_node/src/dan_layer/storage/mod.rs b/applications/tari_validator_node/src/dan_layer/storage/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/storage/mod.rs rename to applications/tari_validator_node/src/dan_layer/storage/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/template_command.rs b/applications/tari_validator_node/src/dan_layer/template_command.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/template_command.rs rename to applications/tari_validator_node/src/dan_layer/template_command.rs diff --git a/applications/tari_dan_node/src/dan_layer/templates/editable_metadata_template.rs b/applications/tari_validator_node/src/dan_layer/templates/editable_metadata_template.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/templates/editable_metadata_template.rs rename to applications/tari_validator_node/src/dan_layer/templates/editable_metadata_template.rs diff --git a/applications/tari_dan_node/src/dan_layer/templates/mod.rs b/applications/tari_validator_node/src/dan_layer/templates/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/templates/mod.rs rename to applications/tari_validator_node/src/dan_layer/templates/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/workers/consensus_worker.rs b/applications/tari_validator_node/src/dan_layer/workers/consensus_worker.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/workers/consensus_worker.rs rename to applications/tari_validator_node/src/dan_layer/workers/consensus_worker.rs diff --git a/applications/tari_dan_node/src/dan_layer/workers/mod.rs b/applications/tari_validator_node/src/dan_layer/workers/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/workers/mod.rs rename to applications/tari_validator_node/src/dan_layer/workers/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs b/applications/tari_validator_node/src/dan_layer/workers/states/commit_state.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/workers/states/commit_state.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/commit_state.rs diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs b/applications/tari_validator_node/src/dan_layer/workers/states/decide_state.rs similarity index 97% rename from applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/decide_state.rs index 0dba82c906..1837c20c04 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/decide_state.rs +++ b/applications/tari_validator_node/src/dan_layer/workers/states/decide_state.rs @@ -107,7 +107,6 @@ where }; self.received_new_view_messages.clear(); - // TODO: rather change the loop below to inside the wait for message let started = Instant::now(); loop { tokio::select! { @@ -128,7 +127,6 @@ where } _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { - // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; } @@ -155,7 +153,6 @@ where return Ok(None); } - // TODO: This might need to be checked in the QC rather if self.received_new_view_messages.contains_key(&sender) { dbg!("Already received message from {:?}", &sender); return Ok(None); @@ -207,7 +204,6 @@ where } fn create_qc(&self, current_view: &View) -> Option> { - // TODO: This can be done in one loop instead of two let mut node = None; for message in self.received_new_view_messages.values() { node = match node { diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/mod.rs b/applications/tari_validator_node/src/dan_layer/workers/states/mod.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/workers/states/mod.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/mod.rs diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/next_view.rs b/applications/tari_validator_node/src/dan_layer/workers/states/next_view.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/workers/states/next_view.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/next_view.rs diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs b/applications/tari_validator_node/src/dan_layer/workers/states/pre_commit_state.rs similarity index 97% rename from applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/pre_commit_state.rs index 65fa68b45e..857698b32c 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/pre_commit_state.rs +++ b/applications/tari_validator_node/src/dan_layer/workers/states/pre_commit_state.rs @@ -98,7 +98,6 @@ where }; self.received_new_view_messages.clear(); - // TODO: rather change the loop below to inside the wait for message let started = Instant::now(); loop { tokio::select! { @@ -119,7 +118,6 @@ where } _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { - // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; } @@ -146,7 +144,6 @@ where return Ok(None); } - // TODO: This might need to be checked in the QC rather if self.received_new_view_messages.contains_key(&sender) { dbg!("Already received message from {:?}", &sender); return Ok(None); @@ -200,7 +197,6 @@ where } fn create_qc(&self, current_view: &View) -> Option> { - // TODO: This can be done in one loop instead of two let mut node = None; for message in self.received_new_view_messages.values() { node = match node { diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs b/applications/tari_validator_node/src/dan_layer/workers/states/prepare.rs similarity index 98% rename from applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/prepare.rs index ef8d5b2f62..18f87e175f 100644 --- a/applications/tari_dan_node/src/dan_layer/workers/states/prepare.rs +++ b/applications/tari_validator_node/src/dan_layer/workers/states/prepare.rs @@ -116,7 +116,6 @@ where reason: "loop ended without setting this event".to_string(), }; - // TODO: rather change the loop below to inside the wait for message let started = Instant::now(); loop { @@ -136,7 +135,6 @@ where }, _ = sleep(timeout.saturating_sub(Instant::now() - started)) => { - // TODO: perhaps this should be from the time the state was entered next_event_result = ConsensusWorkerStateEvent::TimedOut; break; } diff --git a/applications/tari_dan_node/src/dan_layer/workers/states/starting.rs b/applications/tari_validator_node/src/dan_layer/workers/states/starting.rs similarity index 100% rename from applications/tari_dan_node/src/dan_layer/workers/states/starting.rs rename to applications/tari_validator_node/src/dan_layer/workers/states/starting.rs diff --git a/applications/tari_dan_node/src/dan_node_config.rs b/applications/tari_validator_node/src/dan_node_config.rs similarity index 100% rename from applications/tari_dan_node/src/dan_node_config.rs rename to applications/tari_validator_node/src/dan_node_config.rs diff --git a/applications/tari_dan_node/src/digital_assets_error.rs b/applications/tari_validator_node/src/digital_assets_error.rs similarity index 100% rename from applications/tari_dan_node/src/digital_assets_error.rs rename to applications/tari_validator_node/src/digital_assets_error.rs diff --git a/applications/tari_dan_node/src/grpc/mod.rs b/applications/tari_validator_node/src/grpc/mod.rs similarity index 92% rename from applications/tari_dan_node/src/grpc/mod.rs rename to applications/tari_validator_node/src/grpc/mod.rs index bfcbf97e63..ea674747da 100644 --- a/applications/tari_dan_node/src/grpc/mod.rs +++ b/applications/tari_validator_node/src/grpc/mod.rs @@ -19,8 +19,8 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub(crate) mod dan_grpc_server; +pub(crate) mod validator_node_grpc_server; -pub mod dan_rpc { - tonic::include_proto!("tari.dan.rpc"); +pub mod validator_node_rpc { + tonic::include_proto!("tari.validator_node.rpc"); } diff --git a/applications/tari_dan_node/src/grpc/dan_grpc_server.rs b/applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs similarity index 82% rename from applications/tari_dan_node/src/grpc/dan_grpc_server.rs rename to applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs index 3d66aa24ac..0a320c7cae 100644 --- a/applications/tari_dan_node/src/grpc/dan_grpc_server.rs +++ b/applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs @@ -24,7 +24,7 @@ use crate::{ models::{Instruction, TokenId}, services::{ConcreteMempoolService, MempoolService}, }, - grpc::dan_rpc, + grpc::validator_node_rpc as rpc, types::{create_com_sig_from_bytes, ComSig, PublicKey}, }; use std::sync::{Arc, Mutex}; @@ -32,32 +32,32 @@ use tari_crypto::tari_utilities::ByteArray; use tokio::sync::RwLock; use tonic::{Request, Response, Status}; -pub struct DanGrpcServer { +pub struct ValidatorNodeGrpcServer { mempool_service: TMempoolService, } -impl DanGrpcServer { +impl ValidatorNodeGrpcServer { pub fn new(mempool_service: TMempoolService) -> Self { Self { mempool_service } } } #[tonic::async_trait] -impl dan_rpc::dan_node_server::DanNode - for DanGrpcServer +impl rpc::validator_node_server::ValidatorNode + for ValidatorNodeGrpcServer { async fn get_token_data( &self, - request: tonic::Request, - ) -> Result, tonic::Status> { + request: tonic::Request, + ) -> Result, tonic::Status> { dbg!(&request); Err(Status::internal("Oh noes")) } async fn execute_instruction( &self, - request: Request, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { dbg!(&request); let request = request.into_inner(); let instruction = Instruction::new( @@ -76,12 +76,12 @@ impl dan_rpc::d let mut mempool_service = self.mempool_service.clone(); match mempool_service.submit_instruction(instruction) { Ok(_) => { - return Ok(Response::new(dan_rpc::ExecuteInstructionResponse { + return Ok(Response::new(rpc::ExecuteInstructionResponse { status: "Accepted".to_string(), })) }, Err(_) => { - return Ok(Response::new(dan_rpc::ExecuteInstructionResponse { + return Ok(Response::new(rpc::ExecuteInstructionResponse { status: "Errored".to_string(), })) }, diff --git a/applications/tari_dan_node/src/main.rs b/applications/tari_validator_node/src/main.rs similarity index 93% rename from applications/tari_dan_node/src/main.rs rename to applications/tari_validator_node/src/main.rs index f42f19b04e..79fe508732 100644 --- a/applications/tari_dan_node/src/main.rs +++ b/applications/tari_validator_node/src/main.rs @@ -27,7 +27,7 @@ mod grpc; mod p2p; mod types; -use crate::grpc::dan_grpc_server::DanGrpcServer; +use crate::grpc::validator_node_grpc_server::ValidatorNodeGrpcServer; use anyhow; use futures::FutureExt; use log::*; @@ -47,7 +47,7 @@ use crate::{ dan_node::DanNode, services::{ConcreteMempoolService, MempoolService, MempoolServiceHandle}, }, - grpc::dan_rpc::dan_node_server::DanNodeServer, + grpc::validator_node_rpc::validator_node_server::ValidatorNodeServer, }; use std::sync::{Arc, Mutex}; use tari_app_utilities::{initialization::init_configuration, utilities::ExitCodes}; @@ -88,7 +88,7 @@ async fn run_node(config: GlobalConfig) -> Result<(), ExitCodes> { let mempool_service = MempoolServiceHandle::new(Arc::new(Mutex::new(ConcreteMempoolService::new()))); - let grpc_server = DanGrpcServer::new(mempool_service.clone()); + let grpc_server = ValidatorNodeGrpcServer::new(mempool_service.clone()); let grpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 18080); // task::spawn(run_grpc(grpc_server, grpc_addr, shutdown.to_signal())); @@ -112,14 +112,14 @@ async fn run_dan_node( } async fn run_grpc( - grpc_server: DanGrpcServer, + grpc_server: ValidatorNodeGrpcServer, grpc_address: SocketAddr, shutdown_signal: ShutdownSignal, ) -> Result<(), anyhow::Error> { info!(target: LOG_TARGET, "Starting GRPC on {}", grpc_address); Server::builder() - .add_service(DanNodeServer::new(grpc_server)) + .add_service(ValidatorNodeServer::new(grpc_server)) .serve_with_shutdown(grpc_address, shutdown_signal.map(|_| ())) .await .map_err(|err| { diff --git a/applications/tari_dan_node/src/p2p/mod.rs b/applications/tari_validator_node/src/p2p/mod.rs similarity index 100% rename from applications/tari_dan_node/src/p2p/mod.rs rename to applications/tari_validator_node/src/p2p/mod.rs diff --git a/applications/tari_dan_node/src/p2p/proto/dan_consensus_messages.proto b/applications/tari_validator_node/src/p2p/proto/dan_consensus_messages.proto similarity index 100% rename from applications/tari_dan_node/src/p2p/proto/dan_consensus_messages.proto rename to applications/tari_validator_node/src/p2p/proto/dan_consensus_messages.proto diff --git a/applications/tari_dan_node/src/types.rs b/applications/tari_validator_node/src/types.rs similarity index 100% rename from applications/tari_dan_node/src/types.rs rename to applications/tari_validator_node/src/types.rs diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs index 8ee0fdfd74..64891796bc 100644 --- a/base_layer/core/src/consensus/consensus_manager.rs +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - blocks::{genesis_block::*, Block}, + blocks::genesis_block::*, chain_storage::{ChainBlock, ChainStorageError}, consensus::{ chain_strength_comparer::{strongest_chain, ChainStrengthComparer}, diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index 679709be42..f91ed8c0a0 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -218,10 +218,7 @@ impl TryFrom for OutputFeatures { Some(m) => Some(m.try_into()?), None => None, }, - sidechain_checkpoint: match features.sidechain_checkpoint { - Some(a) => Some(a.into()), - None => None, - }, + sidechain_checkpoint: features.sidechain_checkpoint.map(|a| a.into()), }) } } @@ -233,10 +230,7 @@ impl From for proto::types::OutputFeatures { maturity: features.maturity, metadata: features.metadata, unique_id: features.unique_id, - parent_public_key: match features.parent_public_key { - Some(a) => Some(a.as_bytes().to_vec()), - None => None, - }, + parent_public_key: features.parent_public_key.map(|a| a.as_bytes().to_vec()), asset: features.asset.map(|a| a.into()), mint_non_fungible: features.mint_non_fungible.map(|m| m.into()), sidechain_checkpoint: features.sidechain_checkpoint.map(|m| m.into()), diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index ffcb65c5c8..6a505be113 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -212,11 +212,11 @@ impl CoinbaseBuilder { // TODO: Verify bullet proof? let output = if let Some(rewind_data) = self.rewind_data.as_ref() { unblinded_output - .as_rewindable_transaction_output(&self.factories, rewind_data, false) + .as_rewindable_transaction_output(&self.factories, rewind_data) .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))? } else { unblinded_output - .as_transaction_output(&self.factories, false) + .as_transaction_output(&self.factories) .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))? }; let kernel = KernelBuilder::new() diff --git a/base_layer/core/src/transactions/transaction/mod.rs b/base_layer/core/src/transactions/transaction/mod.rs index 3b287b2a57..41193d3209 100644 --- a/base_layer/core/src/transactions/transaction/mod.rs +++ b/base_layer/core/src/transactions/transaction/mod.rs @@ -344,7 +344,7 @@ impl UnblindedOutputBuilder { .as_ref() .ok_or_else(|| TransactionError::ValidationError("script must be set".to_string()))?, &self.features, - &sender_offset_private_key, + sender_offset_private_key, )?; self.metadata_signature = Some(metadata_sig); self.metadata_signed_by_sender = true; @@ -490,11 +490,7 @@ impl UnblindedOutput { }) } - pub fn as_transaction_output( - &self, - factories: &CryptoFactories, - verify_proof: bool, - ) -> Result { + pub fn as_transaction_output(&self, factories: &CryptoFactories) -> Result { if factories.range_proof.range() < 64 && self.value >= MicroTari::from(1u64.shl(&factories.range_proof.range())) { return Err(TransactionError::ValidationError( @@ -523,7 +519,6 @@ impl UnblindedOutput { &self, factories: &CryptoFactories, rewind_data: &RewindData, - verify_proof: bool, ) -> Result { if factories.range_proof.range() < 64 && self.value >= MicroTari::from(1u64.shl(&factories.range_proof.range())) { diff --git a/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs index 11f8d866bd..1b5b7b5339 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender_transaction_protocol_builder.rs @@ -424,11 +424,9 @@ impl SenderTransactionProtocolBuilder { .iter() .map(|o| { if let Some(rewind_data) = self.rewind_data.as_ref() { - // TODO: Should proof be verified? - o.as_rewindable_transaction_output(factories, rewind_data, false) + o.as_rewindable_transaction_output(factories, rewind_data) } else { - // TODO: Should proof be verified - o.as_transaction_output(factories, false) + o.as_transaction_output(factories) } }) .collect::, _>>() @@ -450,7 +448,7 @@ impl SenderTransactionProtocolBuilder { // If rewind data is present we produce a rewindable output, else a standard output let change_output = if let Some(rewind_data) = self.rewind_data.as_ref() { // TODO: Should proof be verified? - match change_unblinded_output.as_rewindable_transaction_output(factories, rewind_data, false) { + match change_unblinded_output.as_rewindable_transaction_output(factories, rewind_data) { Ok(o) => o, Err(e) => { return self.build_err(e.to_string().as_str()); @@ -458,7 +456,7 @@ impl SenderTransactionProtocolBuilder { } } else { // TODO: Should proof be verified? - match change_unblinded_output.as_transaction_output(factories, false) { + match change_unblinded_output.as_transaction_output(factories) { Ok(o) => o, Err(e) => { return self.build_err(e.to_string().as_str()); diff --git a/base_layer/wallet/src/assets/asset_manager.rs b/base_layer/wallet/src/assets/asset_manager.rs index ab754fbb07..d93b4ca0d4 100644 --- a/base_layer/wallet/src/assets/asset_manager.rs +++ b/base_layer/wallet/src/assets/asset_manager.rs @@ -66,10 +66,7 @@ impl = outputs - .into_iter() - .map(|unblinded_output| convert_to_asset(unblinded_output)) - .collect::>()?; + let assets: Vec = outputs.into_iter().map(convert_to_asset).collect::>()?; Ok(assets) } diff --git a/base_layer/wallet/src/assets/asset_manager_handle.rs b/base_layer/wallet/src/assets/asset_manager_handle.rs index 05ea149ff8..69f5199b35 100644 --- a/base_layer/wallet/src/assets/asset_manager_handle.rs +++ b/base_layer/wallet/src/assets/asset_manager_handle.rs @@ -71,13 +71,13 @@ impl AssetManagerHandle { pub async fn create_initial_asset_checkpoint( &mut self, public_key: &PublicKey, - merkle_root: &Vec, + merkle_root: &[u8], ) -> Result<(TxId, Transaction), WalletError> { match self .handle .call(AssetManagerRequest::CreateInitialCheckpoint { asset_public_key: Box::new(public_key.clone()), - merkle_root: Box::new(merkle_root.clone()), + merkle_root: Box::new(merkle_root.to_vec()), }) .await?? { diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 95fbfccd2c..18d0b42531 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -466,9 +466,9 @@ where TBackend: OutputManagerBackend + 'static let input_data = inputs!(PublicKey::from_secret_key(&script_private_key)); let script = script!(Nop); - Ok(UnblindedOutputBuilder::new(value, spending_key.clone()) + Ok(UnblindedOutputBuilder::new(value, spending_key) .with_features(features) - .with_script(script.clone()) + .with_script(script) .with_input_data(input_data) .with_script_private_key(script_private_key) .with_unique_id(unique_id) diff --git a/base_layer/wallet/src/output_manager_service/storage/models.rs b/base_layer/wallet/src/output_manager_service/storage/models.rs index 4e87c09fa5..37ccf0c2ad 100644 --- a/base_layer/wallet/src/output_manager_service/storage/models.rs +++ b/base_layer/wallet/src/output_manager_service/storage/models.rs @@ -45,7 +45,7 @@ impl DbUnblindedOutput { output: UnblindedOutput, factory: &CryptoFactories, ) -> Result { - let tx_out = output.as_transaction_output(factory, false)?; + let tx_out = output.as_transaction_output(factory)?; Ok(DbUnblindedOutput { hash: tx_out.hash(), commitment: tx_out.commitment, @@ -59,7 +59,7 @@ impl DbUnblindedOutput { factory: &CryptoFactories, rewind_data: &RewindData, ) -> Result { - let tx_out = output.as_rewindable_transaction_output(factory, rewind_data, false)?; + let tx_out = output.as_rewindable_transaction_output(factory, rewind_data)?; Ok(DbUnblindedOutput { hash: tx_out.hash(), commitment: tx_out.commitment, diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 1fc707f95d..ff5d3a3de1 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -938,12 +938,9 @@ impl TryFrom for DbUnblindedOutput { }), None => None, }; - let sidechain_checkpoint = match o.features_sidechain_checkpoint_merkle_root { - Some(ref merkle_root) => Some(SideChainCheckpointFeatures { + let sidechain_checkpoint = o.features_sidechain_checkpoint_merkle_root.as_ref().map(|merkle_root| SideChainCheckpointFeatures { merkle_root: merkle_root.to_owned(), - }), - None => None, - }; + }); let features = OutputFeatures { flags: OutputFlags::from_bits(o.flags as u8).ok_or(OutputManagerStorageError::ConversionError)?, @@ -1012,7 +1009,7 @@ impl TryFrom for DbUnblindedOutput { let hash = match o.hash { None => { let factories = CryptoFactories::default(); - unblinded_output.as_transaction_output(&factories, false)?.hash() + unblinded_output.as_transaction_output(&factories)?.hash() }, Some(v) => v, }; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index d3d0461f4f..a669b5af97 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -107,14 +107,14 @@ impl NewOutputSql { impl Encryptable for NewOutputSql { fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = encrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = encrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + self.spending_key = encrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?; + self.script_private_key = encrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?; Ok(()) } fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = decrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = decrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + self.spending_key = decrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?; + self.script_private_key = decrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?; Ok(()) } } diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 850e34097d..ca630b862e 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -218,14 +218,14 @@ impl OutputSql { impl Encryptable for OutputSql { fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = encrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = encrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + self.spending_key = encrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?; + self.script_private_key = encrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?; Ok(()) } fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), AeadError> { - self.spending_key = decrypt_bytes_integral_nonce(&cipher, self.spending_key.clone())?; - self.script_private_key = decrypt_bytes_integral_nonce(&cipher, self.script_private_key.clone())?; + self.spending_key = decrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?; + self.script_private_key = decrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?; Ok(()) } } diff --git a/base_layer/wallet/src/tokens/token_manager.rs b/base_layer/wallet/src/tokens/token_manager.rs index 5c41cc7a46..15a67febb2 100644 --- a/base_layer/wallet/src/tokens/token_manager.rs +++ b/base_layer/wallet/src/tokens/token_manager.rs @@ -67,7 +67,7 @@ impl TokenManager { // Filter out asset registrations that don't have a parent pub key ub.unblinded_output.features.parent_public_key.is_some() }) - .map(|unblinded_output| convert_to_token(unblinded_output)) + .map(convert_to_token) .collect::>()?; Ok(assets) } @@ -83,8 +83,7 @@ fn convert_to_token(unblinded_output: DbUnblindedOutput) -> Result Result