From b3f15073263e6be90d4d8de39606e70ded158cb7 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Tue, 3 Sep 2024 12:32:40 +0400 Subject: [PATCH] fix(consensus)!: defer proposing txs with conflicting unversioned inputs (#1125) Description --- fix(consensus)!: defer proposing txs with conflicting unversioned inputs refactor: locks take into account if the requested input was versioned or not Motivation and Context --- A transaction may provide an unversioned input. In the multi-shard case, if this input is locked by another transaction, the first transaction should not be proposed until that lock is cleared. How Has This Been Tested? --- Manually, submitting transactions with conflicting unversioned inputs. What process can a PR reviewer use to test or verify this change? --- Submit transactions with transaction_generator and transaction_submitter. The default generated transactions use unversioned inputs. Breaking Changes --- - [ ] None - [x] Requires data directory to be deleted - [ ] Other - Please specify --- Cargo.lock | 4 +- .../src/base_layer_scanner.rs | 3 +- .../src/transaction_executor.rs | 32 +-- .../src/command/transaction.rs | 4 +- .../src/handlers/accounts.rs | 4 +- .../src/handlers/nfts.rs | 3 +- .../src/indexer_jrpc_impl.rs | 4 +- .../services/transaction_service/handle.rs | 3 +- .../services/transaction_service/service.rs | 4 +- .../tari_indexer/src/dry_run/processor.rs | 4 +- .../src/transaction_manager/mod.rs | 4 +- .../tari_validator_node/src/bootstrap.rs | 13 +- .../consensus/block_transaction_executor.rs | 10 +- .../src/p2p/rpc/service_impl.rs | 7 +- .../src/p2p/services/mempool/service.rs | 9 +- .../src/p2p/services/mempool/traits.rs | 4 +- .../src/substate_resolver.rs | 6 +- .../src/command/transaction.rs | 4 +- .../src/component_manager.rs | 2 +- clients/tari_indexer_client/src/types.rs | 4 +- clients/validator_node_client/src/lib.rs | 4 + clients/wallet_daemon_client/src/types.rs | 4 +- dan_layer/common_types/Cargo.toml | 1 + dan_layer/common_types/src/lib.rs | 7 + dan_layer/common_types/src/lock_intent.rs | 80 +++++++ .../common_types/src/substate_address.rs | 4 + .../src/versioned_substate_id.rs} | 13 +- dan_layer/consensus/src/hotstuff/error.rs | 4 +- .../src/hotstuff/on_catch_up_sync.rs | 2 +- .../src/hotstuff/on_next_sync_view.rs | 3 +- .../consensus/src/hotstuff/on_propose.rs | 79 +++--- .../on_ready_to_vote_on_local_block.rs | 83 +++---- .../hotstuff/on_receive_foreign_proposal.rs | 7 +- .../src/hotstuff/on_receive_new_view.rs | 2 +- .../src/hotstuff/substate_store/error.rs | 49 ++-- .../hotstuff/substate_store/pending_store.rs | 214 +++++++++++------ .../hotstuff/transaction_manager/manager.rs | 92 +++---- .../hotstuff/transaction_manager/prepared.rs | 16 +- dan_layer/consensus/src/hotstuff/worker.rs | 3 +- dan_layer/consensus/src/messages/message.rs | 2 +- dan_layer/consensus/src/messages/new_view.rs | 3 +- .../consensus/src/traits/substate_store.rs | 3 +- .../src/traits/transaction_executor.rs | 6 +- dan_layer/consensus_tests/src/consensus.rs | 13 +- .../consensus_tests/src/substate_store.rs | 36 ++- .../src/support/epoch_manager.rs | 1 + .../consensus_tests/src/support/harness.rs | 14 +- .../consensus_tests/src/support/helpers.rs | 2 +- .../src/support/transaction.rs | 4 +- .../src/support/transaction_executor.rs | 6 +- .../indexer_lib/src/transaction_autofiller.rs | 4 +- dan_layer/p2p/proto/consensus.proto | 3 +- dan_layer/p2p/src/conversions/consensus.rs | 15 +- dan_layer/p2p/src/conversions/transaction.rs | 4 +- dan_layer/rpc_state_sync/src/manager.rs | 2 +- dan_layer/state_store_sqlite/src/reader.rs | 17 +- .../src/sql_models/block_diff.rs | 3 +- dan_layer/state_store_sqlite/src/writer.rs | 15 +- .../src/consensus_models/block_pledges.rs | 5 +- .../storage/src/consensus_models/evidence.rs | 4 +- .../consensus_models/executed_transaction.rs | 94 +------- .../storage/src/consensus_models/high_qc.rs | 4 +- .../src/consensus_models/lock_intent.rs | 224 ++++++++++++++++++ dan_layer/storage/src/consensus_models/mod.rs | 2 + .../consensus_models/quorum_certificate.rs | 2 +- .../storage/src/consensus_models/substate.rs | 77 +----- .../src/consensus_models/substate_change.rs | 4 +- .../src/consensus_models/substate_lock.rs | 6 +- .../src/consensus_models/transaction.rs | 3 +- .../src/consensus_models/transaction_pool.rs | 2 + dan_layer/storage/src/state_store/mod.rs | 15 +- .../src/template_test.rs | 6 +- dan_layer/transaction/Cargo.toml | 1 - dan_layer/transaction/src/builder.rs | 4 +- dan_layer/transaction/src/lib.rs | 2 - dan_layer/transaction/src/signature.rs | 4 +- dan_layer/transaction/src/transaction.rs | 11 +- .../transaction/src/unsigned_transaction.rs | 4 +- dan_layer/wallet/sdk/src/apis/substate.rs | 3 +- dan_layer/wallet/sdk/src/apis/transaction.rs | 7 +- dan_layer/wallet/sdk/src/models/substate.rs | 2 +- .../sdk/src/models/wallet_transaction.rs | 3 +- dan_layer/wallet/sdk/src/network.rs | 4 +- dan_layer/wallet/sdk/src/storage.rs | 4 +- .../sdk/tests/confidential_output_api.rs | 4 +- dan_layer/wallet/storage_sqlite/src/writer.rs | 3 +- integration_tests/src/lib.rs | 2 +- integration_tests/src/validator_node_cli.rs | 2 +- integration_tests/src/wallet_daemon_cli.rs | 4 +- utilities/tariswap_test_bench/Cargo.toml | 1 + utilities/tariswap_test_bench/src/accounts.rs | 3 +- utilities/tariswap_test_bench/src/runner.rs | 3 +- utilities/tariswap_test_bench/src/tariswap.rs | 3 +- utilities/transaction_generator/Cargo.toml | 1 + .../src/transaction_builders/free_coins.rs | 3 +- .../src/transaction_writer.rs | 3 +- utilities/transaction_submitter/src/cli.rs | 2 +- utilities/transaction_submitter/src/main.rs | 20 +- 98 files changed, 906 insertions(+), 593 deletions(-) create mode 100644 dan_layer/common_types/src/lock_intent.rs rename dan_layer/{transaction/src/substate.rs => common_types/src/versioned_substate_id.rs} (97%) create mode 100644 dan_layer/storage/src/consensus_models/lock_intent.rs diff --git a/Cargo.lock b/Cargo.lock index 3dc29255d..b9cb814e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9110,6 +9110,7 @@ dependencies = [ "tari_hashing", "tari_mmr", "tari_template_lib", + "thiserror", "ts-rs", ] @@ -9991,7 +9992,6 @@ dependencies = [ "tari_dan_common_types", "tari_engine_types", "tari_template_lib", - "thiserror", "ts-rs", ] @@ -10207,6 +10207,7 @@ dependencies = [ "fern", "log", "tari_crypto", + "tari_dan_common_types", "tari_dan_wallet_daemon", "tari_dan_wallet_sdk", "tari_dan_wallet_storage_sqlite", @@ -10818,6 +10819,7 @@ dependencies = [ "rayon", "serde", "tari_crypto", + "tari_dan_common_types", "tari_engine_types", "tari_template_builtin", "tari_template_lib", diff --git a/applications/tari_dan_app_utilities/src/base_layer_scanner.rs b/applications/tari_dan_app_utilities/src/base_layer_scanner.rs index 5bc79f536..bd3efff14 100644 --- a/applications/tari_dan_app_utilities/src/base_layer_scanner.rs +++ b/applications/tari_dan_app_utilities/src/base_layer_scanner.rs @@ -43,7 +43,7 @@ use tari_crypto::{ ristretto::RistrettoPublicKey, tari_utilities::{hex::Hex, ByteArray}, }; -use tari_dan_common_types::{optional::Optional, NodeAddressable}; +use tari_dan_common_types::{optional::Optional, NodeAddressable, VersionedSubstateId}; use tari_dan_storage::{ consensus_models::{BurntUtxo, SubstateRecord}, global::{GlobalDb, MetadataKey}, @@ -59,7 +59,6 @@ use tari_epoch_manager::{base_layer::EpochManagerHandle, EpochManagerError, Epoc use tari_shutdown::ShutdownSignal; use tari_state_store_sqlite::SqliteStateStore; use tari_template_lib::models::{EncryptedData, TemplateAddress, UnclaimedConfidentialOutputAddress}; -use tari_transaction::VersionedSubstateId; use tokio::{task, task::JoinHandle, time}; use crate::{ diff --git a/applications/tari_dan_app_utilities/src/transaction_executor.rs b/applications/tari_dan_app_utilities/src/transaction_executor.rs index 2ee288b18..3396ac21b 100644 --- a/applications/tari_dan_app_utilities/src/transaction_executor.rs +++ b/applications/tari_dan_app_utilities/src/transaction_executor.rs @@ -8,7 +8,12 @@ use log::*; use tari_common::configuration::Network; use tari_common_types::types::PublicKey; use tari_crypto::tari_utilities::ByteArray; -use tari_dan_common_types::services::template_provider::TemplateProvider; +use tari_dan_common_types::{ + services::template_provider::TemplateProvider, + SubstateLockType, + SubstateRequirement, + VersionedSubstateId, +}; use tari_dan_engine::{ fees::{FeeModule, FeeTable}, runtime::{AuthParams, RuntimeModule}, @@ -16,14 +21,10 @@ use tari_dan_engine::{ template::LoadedTemplate, transaction::{TransactionError, TransactionProcessor}, }; -use tari_dan_storage::consensus_models::{SubstateLockType, VersionedSubstateIdLockIntent}; -use tari_engine_types::{ - commit_result::ExecuteResult, - substate::{Substate, SubstateId}, - virtual_substate::VirtualSubstates, -}; +use tari_dan_storage::consensus_models::VersionedSubstateIdLockIntent; +use tari_engine_types::{commit_result::ExecuteResult, substate::Substate, virtual_substate::VirtualSubstates}; use tari_template_lib::{crypto::RistrettoPublicKeyBytes, prelude::NonFungibleAddress}; -use tari_transaction::{Transaction, VersionedSubstateId}; +use tari_transaction::Transaction; const _LOG_TARGET: &str = "tari::dan::transaction_executor"; @@ -45,12 +46,15 @@ pub struct ExecutionOutput { } impl ExecutionOutput { - pub fn resolve_inputs(&self, inputs: &IndexMap) -> Vec { + pub fn resolve_inputs( + &self, + inputs: &IndexMap, + ) -> Vec { if let Some(diff) = self.result.finalize.accept() { inputs .iter() - .map(|(substate_id, substate)| { - let lock_flag = if diff.down_iter().any(|(id, _)| id == substate_id) { + .map(|(substate_req, substate)| { + let lock_flag = if diff.down_iter().any(|(id, _)| id == substate_req.substate_id()) { // Update all inputs that were DOWNed to be write locked SubstateLockType::Write } else { @@ -58,7 +62,7 @@ impl ExecutionOutput { SubstateLockType::Read }; VersionedSubstateIdLockIntent::new( - VersionedSubstateId::new(substate_id.clone(), substate.version()), + VersionedSubstateId::new(substate_req.substate_id().clone(), substate.version()), lock_flag, ) }) @@ -68,9 +72,9 @@ impl ExecutionOutput { // shards involved but do not lock them. We dont actually lock anything for rejected transactions anyway. inputs .iter() - .map(|(substate_id, substate)| { + .map(|(substate_req, substate)| { VersionedSubstateIdLockIntent::new( - VersionedSubstateId::new(substate_id.clone(), substate.version()), + VersionedSubstateId::new(substate_req.substate_id().clone(), substate.version()), SubstateLockType::Read, ) }) diff --git a/applications/tari_dan_wallet_cli/src/command/transaction.rs b/applications/tari_dan_wallet_cli/src/command/transaction.rs index 531dd8cb6..45c0ac99d 100644 --- a/applications/tari_dan_wallet_cli/src/command/transaction.rs +++ b/applications/tari_dan_wallet_cli/src/command/transaction.rs @@ -34,7 +34,7 @@ use anyhow::anyhow; use clap::{Args, Subcommand}; use tari_bor::decode_exact; use tari_common_types::types::PublicKey; -use tari_dan_common_types::{Epoch, SubstateAddress}; +use tari_dan_common_types::{Epoch, SubstateAddress, SubstateRequirement}; use tari_dan_engine::abi::Type; use tari_dan_wallet_sdk::apis::confidential_transfer::ConfidentialTransferInputSelection; use tari_engine_types::{ @@ -53,7 +53,7 @@ use tari_template_lib::{ models::{Amount, BucketId, NonFungibleAddress, NonFungibleId}, prelude::ResourceAddress, }; -use tari_transaction::{SubstateRequirement, TransactionId}; +use tari_transaction::TransactionId; use tari_transaction_manifest::{parse_manifest, ManifestValue}; use tari_utilities::{hex::to_hex, ByteArray}; use tari_wallet_daemon_client::{ diff --git a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs index afa985573..b1e886dc8 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs @@ -13,7 +13,7 @@ use tari_crypto::{ ristretto::{RistrettoComSig, RistrettoPublicKey}, tari_utilities::ByteArray, }; -use tari_dan_common_types::optional::Optional; +use tari_dan_common_types::{optional::Optional, SubstateRequirement}; use tari_dan_wallet_crypto::ConfidentialProofStatement; use tari_dan_wallet_sdk::{ apis::{confidential_transfer::TransferParams, jwt::JrpcPermission, key_manager, substate::ValidatorScanResult}, @@ -36,7 +36,7 @@ use tari_template_lib::{ models::{Amount, UnclaimedConfidentialOutputAddress}, prelude::CONFIDENTIAL_TARI_RESOURCE_ADDRESS, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; use tari_wallet_daemon_client::{ types::{ AccountGetDefaultRequest, diff --git a/applications/tari_dan_wallet_daemon/src/handlers/nfts.rs b/applications/tari_dan_wallet_daemon/src/handlers/nfts.rs index c768a3a94..05f92b651 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/nfts.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/nfts.rs @@ -7,6 +7,7 @@ use anyhow::anyhow; use log::info; use tari_common_types::types::PublicKey; use tari_crypto::{keys::PublicKey as PK, ristretto::RistrettoSecretKey, tari_utilities::ByteArray}; +use tari_dan_common_types::SubstateRequirement; use tari_dan_wallet_sdk::{ apis::{jwt::JrpcPermission, key_manager}, models::Account, @@ -18,7 +19,7 @@ use tari_template_lib::{ crypto::RistrettoPublicKeyBytes, prelude::{Amount, ComponentAddress, Metadata, NonFungibleAddress, NonFungibleId, ResourceAddress}, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tari_wallet_daemon_client::types::{ GetAccountNftRequest, GetAccountNftResponse, diff --git a/applications/tari_dan_wallet_daemon/src/indexer_jrpc_impl.rs b/applications/tari_dan_wallet_daemon/src/indexer_jrpc_impl.rs index a5472d270..b6be96763 100644 --- a/applications/tari_dan_wallet_daemon/src/indexer_jrpc_impl.rs +++ b/applications/tari_dan_wallet_daemon/src/indexer_jrpc_impl.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex}; use axum::async_trait; use reqwest::{IntoUrl, Url}; -use tari_dan_common_types::{optional::IsNotFoundError, substate_type::SubstateType}; +use tari_dan_common_types::{optional::IsNotFoundError, substate_type::SubstateType, SubstateRequirement}; use tari_dan_wallet_sdk::network::{ SubstateListItem, SubstateListResult, @@ -28,7 +28,7 @@ use tari_indexer_client::{ }, }; use tari_template_lib::models::TemplateAddress; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use url::ParseError; #[derive(Debug, Clone)] diff --git a/applications/tari_dan_wallet_daemon/src/services/transaction_service/handle.rs b/applications/tari_dan_wallet_daemon/src/services/transaction_service/handle.rs index ddd0e2a2a..a21d9a854 100644 --- a/applications/tari_dan_wallet_daemon/src/services/transaction_service/handle.rs +++ b/applications/tari_dan_wallet_daemon/src/services/transaction_service/handle.rs @@ -1,9 +1,10 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +use tari_dan_common_types::SubstateRequirement; use tari_dan_wallet_sdk::models::NewAccountInfo; use tari_engine_types::commit_result::ExecuteResult; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tokio::sync::{mpsc, oneshot}; use super::TransactionServiceError; diff --git a/applications/tari_dan_wallet_daemon/src/services/transaction_service/service.rs b/applications/tari_dan_wallet_daemon/src/services/transaction_service/service.rs index f13fdfe4e..c1870c31e 100644 --- a/applications/tari_dan_wallet_daemon/src/services/transaction_service/service.rs +++ b/applications/tari_dan_wallet_daemon/src/services/transaction_service/service.rs @@ -4,7 +4,7 @@ use std::{sync::Arc, time::Duration}; use log::*; -use tari_dan_common_types::optional::IsNotFoundError; +use tari_dan_common_types::{optional::IsNotFoundError, SubstateRequirement}; use tari_dan_wallet_sdk::{ models::{NewAccountInfo, TransactionStatus}, network::WalletNetworkInterface, @@ -13,7 +13,7 @@ use tari_dan_wallet_sdk::{ }; use tari_engine_types::commit_result::ExecuteResult; use tari_shutdown::ShutdownSignal; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tokio::{ sync::{mpsc, watch, Semaphore}, time, diff --git a/applications/tari_indexer/src/dry_run/processor.rs b/applications/tari_indexer/src/dry_run/processor.rs index 8faeea7ae..744ca1a79 100644 --- a/applications/tari_indexer/src/dry_run/processor.rs +++ b/applications/tari_indexer/src/dry_run/processor.rs @@ -28,7 +28,7 @@ use tari_dan_app_utilities::{ template_manager::implementation::TemplateManager, transaction_executor::{TariDanTransactionProcessor, TransactionExecutor as _}, }; -use tari_dan_common_types::{Epoch, PeerAddress, SubstateAddress}; +use tari_dan_common_types::{Epoch, PeerAddress, SubstateAddress, SubstateRequirement}; use tari_dan_engine::{fees::FeeTable, state_store::new_memory_store}; use tari_engine_types::{ commit_result::ExecuteResult, @@ -42,7 +42,7 @@ use tari_indexer_lib::{ substate_scanner::SubstateScanner, transaction_autofiller::TransactionAutofiller, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; use tari_validator_node_rpc::client::{ SubstateResult, TariValidatorNodeRpcClientFactory, diff --git a/applications/tari_indexer/src/transaction_manager/mod.rs b/applications/tari_indexer/src/transaction_manager/mod.rs index 493658fac..92e1beef5 100644 --- a/applications/tari_indexer/src/transaction_manager/mod.rs +++ b/applications/tari_indexer/src/transaction_manager/mod.rs @@ -29,6 +29,8 @@ use tari_dan_common_types::{ optional::{IsNotFoundError, Optional}, NodeAddressable, SubstateAddress, + SubstateRequirement, + ToSubstateAddress, }; use tari_engine_types::substate::SubstateId; use tari_epoch_manager::EpochManagerReader; @@ -37,7 +39,7 @@ use tari_indexer_lib::{ substate_scanner::SubstateScanner, transaction_autofiller::TransactionAutofiller, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tari_validator_node_rpc::client::{ SubstateResult, TransactionResultStatus, diff --git a/applications/tari_validator_node/src/bootstrap.rs b/applications/tari_validator_node/src/bootstrap.rs index ed9de1055..b9a55008e 100644 --- a/applications/tari_validator_node/src/bootstrap.rs +++ b/applications/tari_validator_node/src/bootstrap.rs @@ -49,7 +49,16 @@ use tari_dan_app_utilities::{ template_manager::{implementation::TemplateManager, interface::TemplateManagerHandle}, transaction_executor::TariDanTransactionProcessor, }; -use tari_dan_common_types::{shard::Shard, Epoch, NodeAddressable, NodeHeight, NumPreshards, PeerAddress, ShardGroup}; +use tari_dan_common_types::{ + shard::Shard, + Epoch, + NodeAddressable, + NodeHeight, + NumPreshards, + PeerAddress, + ShardGroup, + VersionedSubstateId, +}; use tari_dan_engine::fees::FeeTable; use tari_dan_p2p::TariMessagingSpec; use tari_dan_storage::{ @@ -86,7 +95,7 @@ use tari_template_lib::{ prelude::{ComponentAccessRules, OwnerRule, ResourceType}, resource::TOKEN_SYMBOL, }; -use tari_transaction::{Transaction, VersionedSubstateId}; +use tari_transaction::Transaction; use tari_validator_node_rpc::client::TariValidatorNodeRpcClientFactory; use tokio::{sync::mpsc, task::JoinHandle}; diff --git a/applications/tari_validator_node/src/consensus/block_transaction_executor.rs b/applications/tari_validator_node/src/consensus/block_transaction_executor.rs index fa2a4eb18..2f050e79f 100644 --- a/applications/tari_validator_node/src/consensus/block_transaction_executor.rs +++ b/applications/tari_validator_node/src/consensus/block_transaction_executor.rs @@ -7,11 +7,11 @@ use indexmap::IndexMap; use log::info; use tari_consensus::traits::{BlockTransactionExecutor, BlockTransactionExecutorError}; use tari_dan_app_utilities::transaction_executor::TransactionExecutor; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_dan_engine::state_store::{memory::MemoryStateStore, new_memory_store, AtomicDb, StateWriter}; use tari_dan_storage::{consensus_models::ExecutedTransaction, StateStore}; use tari_engine_types::{ - substate::{Substate, SubstateId}, + substate::Substate, virtual_substate::{VirtualSubstate, VirtualSubstateId, VirtualSubstates}, }; use tari_transaction::Transaction; @@ -38,7 +38,7 @@ where TExecutor: TransactionExecutor fn add_substates_to_memory_db( &self, - inputs: &IndexMap, + inputs: &IndexMap, out: &MemoryStateStore, ) -> Result<(), BlockTransactionExecutorError> { // TODO: pass the impl SubstateStore directly into the engine @@ -47,7 +47,7 @@ where TExecutor: TransactionExecutor .map_err(|e| BlockTransactionExecutorError::StateStoreError(e.to_string()))?; for (id, substate) in inputs { access - .set_state(id, substate) + .set_state(id.substate_id(), substate) .map_err(|e| BlockTransactionExecutorError::StateStoreError(e.to_string()))?; } access @@ -81,7 +81,7 @@ where &self, transaction: Transaction, current_epoch: Epoch, - resolved_inputs: &IndexMap, + resolved_inputs: &IndexMap, ) -> Result { let id = *transaction.id(); diff --git a/applications/tari_validator_node/src/p2p/rpc/service_impl.rs b/applications/tari_validator_node/src/p2p/rpc/service_impl.rs index f15e6b5dc..ba788074b 100644 --- a/applications/tari_validator_node/src/p2p/rpc/service_impl.rs +++ b/applications/tari_validator_node/src/p2p/rpc/service_impl.rs @@ -309,10 +309,15 @@ impl ValidatorNodeRpcService for ValidatorNodeRpcServiceImpl { } async fn get_high_qc(&self, _request: Request) -> Result, RpcStatus> { + let current_epoch = self + .epoch_manager + .current_epoch() + .await + .map_err(RpcStatus::log_internal_error(LOG_TARGET))?; let high_qc = self .shard_state_store .with_read_tx(|tx| { - HighQc::get(tx) + HighQc::get(tx, current_epoch) .optional()? .map(|hqc| hqc.get_quorum_certificate(tx)) .transpose() diff --git a/applications/tari_validator_node/src/p2p/services/mempool/service.rs b/applications/tari_validator_node/src/p2p/services/mempool/service.rs index fe9c6e92d..3422350de 100644 --- a/applications/tari_validator_node/src/p2p/services/mempool/service.rs +++ b/applications/tari_validator_node/src/p2p/services/mempool/service.rs @@ -23,7 +23,14 @@ use std::{collections::HashSet, fmt::Display, iter}; use log::*; -use tari_dan_common_types::{optional::Optional, NumPreshards, PeerAddress, ShardGroup, SubstateAddress}; +use tari_dan_common_types::{ + optional::Optional, + NumPreshards, + PeerAddress, + ShardGroup, + SubstateAddress, + ToSubstateAddress, +}; use tari_dan_p2p::{DanMessage, NewTransactionMessage}; use tari_dan_storage::{consensus_models::TransactionRecord, StateStore}; use tari_engine_types::commit_result::RejectReason; diff --git a/applications/tari_validator_node/src/p2p/services/mempool/traits.rs b/applications/tari_validator_node/src/p2p/services/mempool/traits.rs index a9bb7bae6..139acb9f4 100644 --- a/applications/tari_validator_node/src/p2p/services/mempool/traits.rs +++ b/applications/tari_validator_node/src/p2p/services/mempool/traits.rs @@ -5,12 +5,12 @@ use std::collections::HashSet; use async_trait::async_trait; use indexmap::IndexMap; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_engine_types::{ substate::{Substate, SubstateId}, virtual_substate::VirtualSubstates, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; pub struct ResolvedSubstates { pub local: IndexMap, diff --git a/applications/tari_validator_node/src/substate_resolver.rs b/applications/tari_validator_node/src/substate_resolver.rs index db7146994..27ba2be0c 100644 --- a/applications/tari_validator_node/src/substate_resolver.rs +++ b/applications/tari_validator_node/src/substate_resolver.rs @@ -7,7 +7,7 @@ use async_trait::async_trait; use indexmap::IndexMap; use log::*; use tari_common_types::types::PublicKey; -use tari_dan_common_types::{Epoch, SubstateAddress}; +use tari_dan_common_types::{Epoch, SubstateAddress, SubstateRequirement}; use tari_dan_engine::state_store::StateStoreError; use tari_dan_storage::{consensus_models::SubstateRecord, StateStore, StorageError}; use tari_engine_types::{ @@ -17,7 +17,7 @@ use tari_engine_types::{ }; use tari_epoch_manager::{EpochManagerError, EpochManagerReader}; use tari_indexer_lib::{error::IndexerError, substate_cache::SubstateCache, substate_scanner::SubstateScanner}; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tari_validator_node_rpc::client::{SubstateResult, ValidatorNodeClientFactory}; use crate::{ @@ -115,7 +115,7 @@ where "🐞 BUG: Requested substate {} was not missing but was also not found", requested_input.substate_id() ); - SubstateResolverError::InputSubstateDoesNotExist { substate_requirement: requested_input.clone()} + SubstateResolverError::InputSubstateDoesNotExist { substate_requirement: requested_input.clone() } })?; if substate.is_destroyed() { diff --git a/applications/tari_validator_node_cli/src/command/transaction.rs b/applications/tari_validator_node_cli/src/command/transaction.rs index 5d10eae0f..2e7ebf34c 100644 --- a/applications/tari_validator_node_cli/src/command/transaction.rs +++ b/applications/tari_validator_node_cli/src/command/transaction.rs @@ -29,7 +29,7 @@ use std::{ use anyhow::anyhow; use clap::{Args, Subcommand}; -use tari_dan_common_types::{optional::Optional, SubstateAddress}; +use tari_dan_common_types::{optional::Optional, SubstateAddress, SubstateRequirement}; use tari_dan_engine::abi::Type; use tari_engine_types::{ commit_result::{ExecuteResult, FinalizeResult, RejectReason, TransactionResult}, @@ -45,7 +45,7 @@ use tari_template_lib::{ models::{Amount, BucketId, NonFungibleAddress, NonFungibleId}, prelude::ResourceAddress, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tari_transaction_manifest::parse_manifest; use tari_validator_node_client::{ types::{ diff --git a/applications/tari_validator_node_cli/src/component_manager.rs b/applications/tari_validator_node_cli/src/component_manager.rs index 9ba6aa8e5..ea5fee61e 100644 --- a/applications/tari_validator_node_cli/src/component_manager.rs +++ b/applications/tari_validator_node_cli/src/component_manager.rs @@ -25,11 +25,11 @@ use std::{fs, io, path::Path}; use anyhow::anyhow; use jfs::Config; use serde::{Deserialize, Serialize}; +use tari_dan_common_types::SubstateRequirement; use tari_engine_types::{ serde_with, substate::{SubstateDiff, SubstateId}, }; -use tari_transaction::SubstateRequirement; pub struct ComponentManager { store: jfs::Store, diff --git a/clients/tari_indexer_client/src/types.rs b/clients/tari_indexer_client/src/types.rs index 2e65cdb38..fe7beda83 100644 --- a/clients/tari_indexer_client/src/types.rs +++ b/clients/tari_indexer_client/src/types.rs @@ -9,7 +9,7 @@ use serde_json::Value as JsonValue; use serde_with::{serde_as, DisplayFromStr}; use tari_base_node_client::types::BaseLayerValidatorNode; use tari_common_types::types::{FixedHash, PublicKey}; -use tari_dan_common_types::{substate_type::SubstateType, Epoch}; +use tari_dan_common_types::{substate_type::SubstateType, Epoch, SubstateRequirement}; use tari_dan_storage::consensus_models::Decision; use tari_engine_types::{ commit_result::ExecuteResult, @@ -18,7 +18,7 @@ use tari_engine_types::{ TemplateAddress, }; use tari_template_abi::TemplateDef; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; #[cfg(feature = "ts")] use ts_rs::TS; diff --git a/clients/validator_node_client/src/lib.rs b/clients/validator_node_client/src/lib.rs index 5a1ca4b70..b106492b7 100644 --- a/clients/validator_node_client/src/lib.rs +++ b/clients/validator_node_client/src/lib.rs @@ -55,6 +55,10 @@ impl ValidatorNodeClient { }) } + pub fn endpoint(&self) -> &Url { + &self.endpoint + } + pub async fn get_identity(&mut self) -> Result { self.send_request("get_identity", json!({})).await } diff --git a/clients/wallet_daemon_client/src/types.rs b/clients/wallet_daemon_client/src/types.rs index 2be9be5b1..673eaa17d 100644 --- a/clients/wallet_daemon_client/src/types.rs +++ b/clients/wallet_daemon_client/src/types.rs @@ -29,7 +29,7 @@ use std::{collections::HashMap, time::Duration}; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; use tari_common_types::types::PublicKey; -use tari_dan_common_types::{substate_type::SubstateType, Epoch, SubstateAddress}; +use tari_dan_common_types::{substate_type::SubstateType, Epoch, SubstateAddress, SubstateRequirement}; use tari_dan_wallet_sdk::{ apis::{confidential_transfer::ConfidentialTransferInputSelection, jwt::Claims, key_manager}, models::{Account, ConfidentialProofId, NonFungibleToken, TransactionStatus}, @@ -49,7 +49,7 @@ use tari_template_lib::{ models::{Amount, ConfidentialOutputStatement, NonFungibleId, ResourceAddress, VaultId}, prelude::{ComponentAddress, ConfidentialWithdrawProof, ResourceType}, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId, UnsignedTransaction}; +use tari_transaction::{Transaction, TransactionId, UnsignedTransaction}; #[cfg(feature = "ts")] use ts_rs::TS; diff --git a/dan_layer/common_types/Cargo.toml b/dan_layer/common_types/Cargo.toml index efb0c5bbe..a7143f771 100644 --- a/dan_layer/common_types/Cargo.toml +++ b/dan_layer/common_types/Cargo.toml @@ -30,6 +30,7 @@ prost = { workspace = true } prost-types = { workspace = true } serde = { workspace = true, default-features = true } ts-rs = { workspace = true, optional = true } +thiserror = { workspace = true } [dev-dependencies] indexmap = { workspace = true } diff --git a/dan_layer/common_types/src/lib.rs b/dan_layer/common_types/src/lib.rs index a3d67c423..688249ba0 100644 --- a/dan_layer/common_types/src/lib.rs +++ b/dan_layer/common_types/src/lib.rs @@ -38,3 +38,10 @@ pub use num_preshards::*; pub mod uint; pub use tari_engine_types::serde_with; + +mod versioned_substate_id; + +pub use versioned_substate_id::*; + +mod lock_intent; +pub use lock_intent::*; diff --git a/dan_layer/common_types/src/lock_intent.rs b/dan_layer/common_types/src/lock_intent.rs new file mode 100644 index 000000000..47aeb579b --- /dev/null +++ b/dan_layer/common_types/src/lock_intent.rs @@ -0,0 +1,80 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{fmt, str::FromStr}; + +use tari_bor::{Deserialize, Serialize}; +use tari_engine_types::substate::SubstateId; + +use crate::{SubstateAddress, ToSubstateAddress, VersionedSubstateId}; + +pub trait LockIntent { + fn substate_id(&self) -> &SubstateId; + fn lock_type(&self) -> SubstateLockType; + fn version_to_lock(&self) -> u32; + fn requested_version(&self) -> Option; + + fn to_versioned_substate_id(&self) -> VersionedSubstateId { + VersionedSubstateId::new(self.substate_id().clone(), self.version_to_lock()) + } +} + +impl ToSubstateAddress for T { + fn to_substate_address(&self) -> SubstateAddress { + SubstateAddress::from_substate_id(self.substate_id(), self.version_to_lock()) + } +} + +/// Substate lock flags +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr( + feature = "ts", + derive(ts_rs::TS), + ts(export, export_to = "../../bindings/src/types/") +)] +pub enum SubstateLockType { + Read, + Write, + Output, +} + +impl SubstateLockType { + pub fn is_write(&self) -> bool { + matches!(self, Self::Write) + } + + pub fn is_read(&self) -> bool { + matches!(self, Self::Read) + } + + pub fn is_output(&self) -> bool { + matches!(self, Self::Output) + } +} + +impl fmt::Display for SubstateLockType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Read => write!(f, "Read"), + Self::Write => write!(f, "Write"), + Self::Output => write!(f, "Output"), + } + } +} + +impl FromStr for SubstateLockType { + type Err = SubstateLockFlagParseError; + + fn from_str(s: &str) -> Result { + match s { + "Read" => Ok(Self::Read), + "Write" => Ok(Self::Write), + "Output" => Ok(Self::Output), + _ => Err(SubstateLockFlagParseError), + } + } +} + +#[derive(Debug, thiserror::Error)] +#[error("Failed to parse SubstateLockFlag")] +pub struct SubstateLockFlagParseError; diff --git a/dan_layer/common_types/src/substate_address.rs b/dan_layer/common_types/src/substate_address.rs index 98ced0958..130da7787 100644 --- a/dan_layer/common_types/src/substate_address.rs +++ b/dan_layer/common_types/src/substate_address.rs @@ -20,6 +20,10 @@ use tari_template_lib::models::ObjectKey; use crate::{shard::Shard, uint::U256, NumPreshards, ShardGroup}; +pub trait ToSubstateAddress { + fn to_substate_address(&self) -> SubstateAddress; +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] #[cfg_attr( feature = "ts", diff --git a/dan_layer/transaction/src/substate.rs b/dan_layer/common_types/src/versioned_substate_id.rs similarity index 97% rename from dan_layer/transaction/src/substate.rs rename to dan_layer/common_types/src/versioned_substate_id.rs index fad4b4bf6..587a6a201 100644 --- a/dan_layer/transaction/src/substate.rs +++ b/dan_layer/common_types/src/versioned_substate_id.rs @@ -4,9 +4,10 @@ use std::{borrow::Borrow, fmt::Display, str::FromStr}; use serde::{Deserialize, Serialize}; -use tari_dan_common_types::{shard::Shard, NumPreshards, ShardGroup, SubstateAddress}; use tari_engine_types::{serde_with, substate::SubstateId}; +use crate::{shard::Shard, NumPreshards, ShardGroup, SubstateAddress, ToSubstateAddress}; + #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr( feature = "ts", @@ -184,10 +185,6 @@ impl VersionedSubstateId { self.version } - pub fn to_substate_address(&self) -> SubstateAddress { - SubstateAddress::from_substate_id(self.substate_id(), self.version()) - } - /// Calculates and returns the shard number that this SubstateAddress belongs. /// A shard is an equal division of the 256-bit shard space. pub fn to_shard(&self, num_shards: NumPreshards) -> Shard { @@ -209,6 +206,12 @@ impl VersionedSubstateId { } } +impl ToSubstateAddress for VersionedSubstateId { + fn to_substate_address(&self) -> SubstateAddress { + SubstateAddress::from_substate_id(self.substate_id(), self.version()) + } +} + impl FromStr for VersionedSubstateId { type Err = SubstateRequirementParseError; diff --git a/dan_layer/consensus/src/hotstuff/error.rs b/dan_layer/consensus/src/hotstuff/error.rs index ec51ae94b..717f0ddd9 100644 --- a/dan_layer/consensus/src/hotstuff/error.rs +++ b/dan_layer/consensus/src/hotstuff/error.rs @@ -2,14 +2,14 @@ // SPDX-License-Identifier: BSD-3-Clause use tari_common_types::types::FixedHash; -use tari_dan_common_types::{Epoch, NodeHeight}; +use tari_dan_common_types::{Epoch, NodeHeight, VersionedSubstateIdError}; use tari_dan_storage::{ consensus_models::{BlockId, LeafBlock, LockedBlock, QuorumCertificate, TransactionPoolError}, StorageError, }; use tari_epoch_manager::EpochManagerError; use tari_state_tree::StateTreeError; -use tari_transaction::{TransactionId, VersionedSubstateIdError}; +use tari_transaction::TransactionId; use tokio::task::JoinError; use crate::{ diff --git a/dan_layer/consensus/src/hotstuff/on_catch_up_sync.rs b/dan_layer/consensus/src/hotstuff/on_catch_up_sync.rs index ddc96f943..18f432c59 100644 --- a/dan_layer/consensus/src/hotstuff/on_catch_up_sync.rs +++ b/dan_layer/consensus/src/hotstuff/on_catch_up_sync.rs @@ -33,7 +33,7 @@ impl OnCatchUpSync { } pub async fn request_sync(&mut self, epoch: Epoch, from: &TConsensusSpec::Addr) -> Result<(), HotStuffError> { - let high_qc = self.store.with_read_tx(|tx| HighQc::get(tx))?; + let high_qc = self.store.with_read_tx(|tx| HighQc::get(tx, epoch))?; info!( target: LOG_TARGET, "⏰ Catch up required from block {} from {} (current view: {})", diff --git a/dan_layer/consensus/src/hotstuff/on_next_sync_view.rs b/dan_layer/consensus/src/hotstuff/on_next_sync_view.rs index fa2d0c231..706c3073b 100644 --- a/dan_layer/consensus/src/hotstuff/on_next_sync_view.rs +++ b/dan_layer/consensus/src/hotstuff/on_next_sync_view.rs @@ -51,7 +51,7 @@ impl OnNextSyncViewHandler { } let (high_qc, last_sent_vote) = self.store.with_read_tx(|tx| { - let high_qc = HighQc::get(tx)?.get_quorum_certificate(tx)?; + let high_qc = HighQc::get(tx, epoch)?.get_quorum_certificate(tx)?; let last_sent_vote = LastSentVote::get(tx) .optional()? .filter(|vote| high_qc.block_height() < vote.block_height); @@ -67,7 +67,6 @@ impl OnNextSyncViewHandler { let message = NewViewMessage { high_qc, new_height, - epoch, last_vote: last_sent_vote.map(VoteMessage::from), }; diff --git a/dan_layer/consensus/src/hotstuff/on_propose.rs b/dan_layer/consensus/src/hotstuff/on_propose.rs index 57705ed21..cc29c22ef 100644 --- a/dan_layer/consensus/src/hotstuff/on_propose.rs +++ b/dan_layer/consensus/src/hotstuff/on_propose.rs @@ -16,6 +16,8 @@ use tari_dan_common_types::{ shard::Shard, Epoch, NodeHeight, + ToSubstateAddress, + VersionedSubstateId, }; use tari_dan_storage::{ consensus_models::{ @@ -33,6 +35,7 @@ use tari_dan_storage::{ PendingShardStateTreeDiff, QuorumCertificate, SubstateChange, + SubstateRequirementLockIntent, TransactionAtom, TransactionExecution, TransactionPool, @@ -44,7 +47,7 @@ use tari_dan_storage::{ }; use tari_engine_types::{commit_result::RejectReason, substate::Substate}; use tari_epoch_manager::EpochManagerReader; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use crate::{ hotstuff::{ @@ -164,7 +167,7 @@ where TConsensusSpec: ConsensusSpec let base_layer_block_height = current_base_layer_block_height; let (next_block, foreign_proposals) = self.store.with_write_tx(|tx| { - let high_qc = HighQc::get(&**tx)?; + let high_qc = HighQc::get(&**tx, epoch)?; let high_qc_cert = high_qc.get_quorum_certificate(&**tx)?; let (next_block, foreign_proposals, executed_transactions) = self.build_next_block( tx, @@ -415,16 +418,7 @@ where TConsensusSpec: ConsensusSpec substate: Substate::new(0, utxo.substate_value), }; - if let Err(err) = substate_store.put(change) { - let err = err.or_fatal_error()?; - warn!( - target: LOG_TARGET, - "❌ NO VOTE: Failed to store mint confidential output for {}. Error: {}", - utxo.substate_id, - err - ); - return Err(err.into()); - } + substate_store.put(change)?; } debug!( @@ -477,6 +471,7 @@ where TConsensusSpec: ConsensusSpec Ok((next_block, foreign_proposals, executed_transactions)) } + #[allow(clippy::too_many_lines)] fn prepare_transaction( &self, parent_block: &LeafBlock, @@ -491,7 +486,7 @@ where TConsensusSpec: ConsensusSpec tx_rec.transaction_id(), ); - let prepared = self + let (prepared, lock_status) = self .transaction_manager .prepare( substate_store, @@ -501,6 +496,16 @@ where TConsensusSpec: ConsensusSpec ) .map_err(|e| HotStuffError::TransactionExecutorError(e.to_string()))?; + if lock_status.is_any_failed() && !lock_status.is_hard_conflict() { + warn!( + target: LOG_TARGET, + "⚠️ Transaction {} has lock conflicts, but no hard conflicts. Skipping proposing this transaction...", + tx_rec.transaction_id(), + ); + + return Ok(None); + } + let command = match prepared.clone() { PreparedTransaction::LocalOnly(LocalPreparedTransaction::Accept(executed)) => { let execution = executed.into_execution(); @@ -532,7 +537,7 @@ where TConsensusSpec: ConsensusSpec err, ); // Only error if it is not related to lock errors - let _err = err.or_fatal_error()?; + let _err = err.ok_lock_failed()?; return Ok(None); } } @@ -616,29 +621,29 @@ where TConsensusSpec: ConsensusSpec self.execute_transaction(tx, &parent_block.block_id, parent_block.epoch, tx_rec.transaction_id())?; // Try to lock all local outputs - let local_outputs = execution.resulting_outputs().iter().filter(|o| { - o.substate_id().is_transaction_receipt() || - local_committee_info.includes_substate_address(&o.to_substate_address()) - }); - match substate_store.try_lock_all(*tx_rec.transaction_id(), local_outputs, false) { - Ok(()) => {}, - Err(err) => { - warn!( - target: LOG_TARGET, - "⚠️ Failed to lock outputs for transaction {}: {}", - tx_rec.transaction_id(), - err, - ); - // Only error if it is not related to lock errors - let err = err.or_fatal_error()?; - execution.set_abort_reason(RejectReason::FailedToLockOutputs(err.to_string())); - tx_rec.update_from_execution(&execution); - executed_transactions.insert(*tx_rec.transaction_id(), execution); - // If the transaction does not lock, we propose to abort it - return Ok(Some(Command::LocalAccept( - self.get_current_transaction_atom(local_committee_info, tx_rec)?, - ))); - }, + let local_outputs = execution + .resulting_outputs() + .iter() + .filter(|o| { + o.substate_id().is_transaction_receipt() || + local_committee_info.includes_substate_address(&o.to_substate_address()) + }) + .map(|output| SubstateRequirementLockIntent::from(output.clone())); + let lock_status = substate_store.try_lock_all(*tx_rec.transaction_id(), local_outputs, false)?; + if let Some(err) = lock_status.failures().first() { + warn!( + target: LOG_TARGET, + "⚠️ Failed to lock outputs for transaction {}: {}", + tx_rec.transaction_id(), + err, + ); + execution.set_abort_reason(RejectReason::FailedToLockOutputs(err.to_string())); + tx_rec.update_from_execution(&execution); + executed_transactions.insert(*tx_rec.transaction_id(), execution); + // If the transaction does not lock, we propose to abort it + return Ok(Some(Command::LocalAccept( + self.get_current_transaction_atom(local_committee_info, tx_rec)?, + ))); } tx_rec.update_from_execution(&execution); diff --git a/dan_layer/consensus/src/hotstuff/on_ready_to_vote_on_local_block.rs b/dan_layer/consensus/src/hotstuff/on_ready_to_vote_on_local_block.rs index f87b66e28..76a809b75 100644 --- a/dan_layer/consensus/src/hotstuff/on_ready_to_vote_on_local_block.rs +++ b/dan_layer/consensus/src/hotstuff/on_ready_to_vote_on_local_block.rs @@ -6,7 +6,13 @@ use std::num::NonZeroU64; use log::*; use tari_crypto::ristretto::RistrettoPublicKey; -use tari_dan_common_types::{committee::CommitteeInfo, optional::Optional, Epoch}; +use tari_dan_common_types::{ + committee::CommitteeInfo, + optional::Optional, + Epoch, + ToSubstateAddress, + VersionedSubstateId, +}; use tari_dan_storage::{ consensus_models::{ Block, @@ -34,7 +40,7 @@ use tari_dan_storage::{ StateStore, }; use tari_engine_types::{commit_result::RejectReason, substate::Substate}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use tokio::sync::broadcast; use crate::{ @@ -527,7 +533,7 @@ where TConsensusSpec: ConsensusSpec } // TODO(perf): proposer shouldn't have to do this twice, esp. executing the transaction and locking - let prepared = self + let (prepared, _lock_status) = self .transaction_manager .prepare(substate_store, local_committee_info, block.epoch(), *atom.id()) .map_err(|e| HotStuffError::TransactionExecutorError(e.to_string()))?; @@ -580,7 +586,7 @@ where TConsensusSpec: ConsensusSpec tx_rec.transaction_id(), err ); - let _err = err.or_fatal_error()?; + let _err = err.ok_lock_failed()?; return Ok(false); } } @@ -695,7 +701,7 @@ where TConsensusSpec: ConsensusSpec return Ok(false); } - let prepared = self + let (prepared, _lock_status) = self .transaction_manager .prepare(substate_store, local_committee_info, block.epoch(), *atom.id()) .map_err(|e| HotStuffError::TransactionExecutorError(e.to_string()))?; @@ -1011,11 +1017,11 @@ where TConsensusSpec: ConsensusSpec let maybe_execution = if tx_rec.current_decision().is_commit() { let execution = self.execute_transaction(tx, block.id(), block.epoch(), tx_rec.transaction_id())?; - let execution = execution.into_transaction_execution(); + let mut execution = execution.into_transaction_execution(); // TODO: can we modify the locks at this point? For multi-shard input transactions, we locked all inputs as - // Write due to lack of information. We now know what locks are necessary, however this changes pledges - // already sent. + // Write due to lack of information. We now know what locks are necessary, and this block has the correct + // evidence (TODO: verify the atom) so this should be fine. tx_rec.update_from_execution(&execution); // Lock all local outputs @@ -1023,42 +1029,39 @@ where TConsensusSpec: ConsensusSpec o.substate_id().is_transaction_receipt() || local_committee_info.includes_substate_address(&o.to_substate_address()) }); - match substate_store.try_lock_all(*tx_rec.transaction_id(), local_outputs, false) { - Ok(()) => {}, - Err(err) => { - let err = err.or_fatal_error()?; - - if atom.decision.is_commit() { - // If we disagree with any local decision we abstain from voting - warn!( - target: LOG_TARGET, - "❌ NO VOTE LocalAccept: Lock failure: {} but leader decided COMMIT for tx {} in block {}. Leader proposed COMMIT, we decided ABORT", - err, - tx_rec.transaction_id(), - block, - ); - return Ok(false); - } - - info!( + let lock_status = substate_store.try_lock_all(*tx_rec.transaction_id(), local_outputs, false)?; + if let Some(err) = lock_status.failures().first() { + if atom.decision.is_commit() { + // If we disagree with any local decision we abstain from voting + warn!( target: LOG_TARGET, - "⚠️ Failed to lock outputs for transaction {} in block {}. Error: {}", - block, + "❌ NO VOTE LocalAccept: Lock failure: {} but leader decided COMMIT for tx {} in block {}. Leader proposed COMMIT, we decided ABORT", + err, tx_rec.transaction_id(), - err + block, ); + return Ok(false); + } - tx_rec.set_local_decision(Decision::Abort); - proposed_block_change_set - .set_next_transaction_update( - &tx_rec, - TransactionPoolStage::LocalAccepted, - tx_rec.evidence().all_addresses_justified(), - )? - .add_transaction_execution(execution)?; + info!( + target: LOG_TARGET, + "⚠️ Failed to lock outputs for transaction {} in block {}. Error: {}", + block, + tx_rec.transaction_id(), + err + ); - return Ok(true); - }, + tx_rec.set_local_decision(Decision::Abort); + execution.set_abort_reason(RejectReason::FailedToLockOutputs(err.to_string())); + proposed_block_change_set + .set_next_transaction_update( + &tx_rec, + TransactionPoolStage::LocalAccepted, + tx_rec.evidence().all_addresses_justified(), + )? + .add_transaction_execution(execution)?; + + return Ok(true); } Some(execution) } else { @@ -1342,7 +1345,7 @@ where TConsensusSpec: ConsensusSpec }; if let Err(err) = substate_store.put(change) { - let err = err.or_fatal_error()?; + let err = err.ok_lock_failed()?; warn!( target: LOG_TARGET, "❌ NO VOTE: Failed to store mint confidential output for {}. Error: {}", diff --git a/dan_layer/consensus/src/hotstuff/on_receive_foreign_proposal.rs b/dan_layer/consensus/src/hotstuff/on_receive_foreign_proposal.rs index 4408e7342..87f8ce65f 100644 --- a/dan_layer/consensus/src/hotstuff/on_receive_foreign_proposal.rs +++ b/dan_layer/consensus/src/hotstuff/on_receive_foreign_proposal.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause use log::*; -use tari_dan_common_types::{committee::CommitteeInfo, optional::Optional, ShardGroup}; +use tari_dan_common_types::{committee::CommitteeInfo, optional::Optional, ShardGroup, ToSubstateAddress}; use tari_dan_storage::{ consensus_models::{ Block, @@ -313,7 +313,7 @@ where TConsensusSpec: ConsensusSpec tx_rec.current_stage(), tx_rec.evidence().all_input_addresses_justified() ); - tx_rec.add_pending_status_update(tx, local_leaf, tx_rec.current_stage(), false)?; + tx_rec.add_pending_status_update(tx, local_leaf, tx_rec.current_stage(), tx_rec.is_ready())?; } }, Command::LocalAccept(atom) => { @@ -445,7 +445,8 @@ where TConsensusSpec: ConsensusSpec tx_rec.current_stage(), tx_rec.evidence().all_addresses_justified() ); - tx_rec.add_pending_status_update(tx, local_leaf, tx_rec.current_stage(), false)?; + // Still need to update the evidence + tx_rec.add_pending_status_update(tx, local_leaf, tx_rec.current_stage(), tx_rec.is_ready())?; } }, // Should never receive this diff --git a/dan_layer/consensus/src/hotstuff/on_receive_new_view.rs b/dan_layer/consensus/src/hotstuff/on_receive_new_view.rs index 8097d0c7d..a165172ec 100644 --- a/dan_layer/consensus/src/hotstuff/on_receive_new_view.rs +++ b/dan_layer/consensus/src/hotstuff/on_receive_new_view.rs @@ -81,9 +81,9 @@ where TConsensusSpec: ConsensusSpec let NewViewMessage { high_qc, new_height, - epoch, last_vote, } = message; + let epoch = high_qc.epoch(); debug!( target: LOG_TARGET, "🌟 Received NEWVIEW for qc {} new height {} from {}", diff --git a/dan_layer/consensus/src/hotstuff/substate_store/error.rs b/dan_layer/consensus/src/hotstuff/substate_store/error.rs index 21d96a762..223dcc4a9 100644 --- a/dan_layer/consensus/src/hotstuff/substate_store/error.rs +++ b/dan_layer/consensus/src/hotstuff/substate_store/error.rs @@ -1,12 +1,13 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use tari_dan_common_types::optional::IsNotFoundError; -use tari_dan_storage::{consensus_models::SubstateLockType, StorageError}; -use tari_transaction::VersionedSubstateId; +use tari_dan_common_types::{optional::IsNotFoundError, SubstateLockType, VersionedSubstateId}; +use tari_dan_storage::StorageError; #[derive(Debug, thiserror::Error)] pub enum SubstateStoreError { + #[error(transparent)] + LockFailed(#[from] LockFailedError), #[error("Substate {id} not found")] SubstateNotFound { id: VersionedSubstateId }, #[error("Substate {id} is DOWN")] @@ -15,25 +16,6 @@ pub enum SubstateStoreError { ExpectedSubstateNotExist { id: VersionedSubstateId }, #[error("Expected substate {id} to be DOWN but it was UP")] ExpectedSubstateDown { id: VersionedSubstateId }, - #[error( - "Failed to {requested_lock} lock substate {substate_id} due to conflict with existing {existing_lock} lock" - )] - LockConflict { - substate_id: VersionedSubstateId, - existing_lock: SubstateLockType, - requested_lock: SubstateLockType, - }, - #[error("Substate {substate_id} requires lock {required_lock} but is currently locked with {existing_lock}")] - RequiresLock { - substate_id: VersionedSubstateId, - existing_lock: SubstateLockType, - required_lock: SubstateLockType, - }, - #[error("Substate {substate_id} is not {required_lock} locked")] - NotLocked { - substate_id: VersionedSubstateId, - required_lock: SubstateLockType, - }, #[error(transparent)] StoreError(#[from] StorageError), @@ -44,6 +26,7 @@ pub enum SubstateStoreError { impl IsNotFoundError for SubstateStoreError { fn is_not_found_error(&self) -> bool { match self { + SubstateStoreError::LockFailed(LockFailedError::SubstateNotFound { .. }) => true, SubstateStoreError::SubstateNotFound { .. } => true, SubstateStoreError::StoreError(err) => err.is_not_found_error(), _ => false, @@ -52,11 +35,25 @@ impl IsNotFoundError for SubstateStoreError { } impl SubstateStoreError { - pub fn or_fatal_error(self) -> Result { + pub fn ok_lock_failed(self) -> Result { match self { - err @ SubstateStoreError::StoreError(_) => Err(err), - err @ SubstateStoreError::StateTreeError(_) => Err(err), - other => Ok(other), + SubstateStoreError::LockFailed(err) => Ok(err), + other => Err(other), } } } + +#[derive(Debug, thiserror::Error)] +pub enum LockFailedError { + #[error("Substate {id} not found")] + SubstateNotFound { id: VersionedSubstateId }, + + #[error( + "Failed to {requested_lock} lock substate {substate_id} due to conflict with existing {existing_lock} lock" + )] + LockConflict { + substate_id: VersionedSubstateId, + existing_lock: SubstateLockType, + requested_lock: SubstateLockType, + }, +} diff --git a/dan_layer/consensus/src/hotstuff/substate_store/pending_store.rs b/dan_layer/consensus/src/hotstuff/substate_store/pending_store.rs index bac0f68c1..d40dfedac 100644 --- a/dan_layer/consensus/src/hotstuff/substate_store/pending_store.rs +++ b/dan_layer/consensus/src/hotstuff/substate_store/pending_store.rs @@ -1,32 +1,32 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use std::{ - borrow::{Borrow, Cow}, - collections::HashMap, -}; +use std::{borrow::Cow, collections::HashMap, fmt::Display}; use indexmap::IndexMap; use log::*; -use tari_dan_common_types::{optional::Optional, NumPreshards, SubstateAddress}; +use tari_dan_common_types::{ + optional::Optional, + LockIntent, + NumPreshards, + SubstateAddress, + SubstateLockType, + ToSubstateAddress, + VersionedSubstateId, +}; use tari_dan_storage::{ - consensus_models::{ - BlockDiff, - BlockId, - SubstateChange, - SubstateLock, - SubstateLockType, - SubstateRecord, - VersionedSubstateIdLockIntent, - }, + consensus_models::{BlockDiff, BlockId, SubstateChange, SubstateLock, SubstateRecord}, StateStore, StateStoreReadTransaction, }; use tari_engine_types::substate::{Substate, SubstateDiff, SubstateId}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use super::error::SubstateStoreError; -use crate::traits::{ReadableSubstateStore, WriteableSubstateStore}; +use crate::{ + hotstuff::substate_store::LockFailedError, + traits::{ReadableSubstateStore, WriteableSubstateStore}, +}; const LOG_TARGET: &str = "tari::dan::hotstuff::substate_store::pending_store"; @@ -62,7 +62,7 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> ReadableSubstateStore for PendingSu type Error = SubstateStoreError; fn get(&self, id: &VersionedSubstateId) -> Result { - if let Some(change) = self.get_pending(id) { + if let Some(change) = self.get_pending(&id.to_substate_address()) { return change.up().cloned().ok_or_else(|| SubstateStoreError::SubstateIsDown { id: change.versioned_substate_id().clone(), }); @@ -154,48 +154,74 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor Ok(substate.into_substate()) } - pub fn try_lock_all, B: Borrow>( + pub fn try_lock_all( &mut self, transaction_id: TransactionId, id_locks: I, is_local_only: bool, - ) -> Result<(), SubstateStoreError> { - for id_lock in id_locks { - self.try_lock(transaction_id, id_lock.borrow(), is_local_only)?; + ) -> Result + where + I: IntoIterator, + L: LockIntent + Display, + { + let mut lock_status = LockStatus::new(); + for lock in id_locks { + match self.try_lock(transaction_id, &lock, is_local_only) { + Ok(()) => continue, + Err(err) => { + let error = err.ok_lock_failed()?; + match error { + err @ LockFailedError::SubstateNotFound { .. } => { + // If the substate does not exist, the transaction is invalid + let index = lock_status.add_failed(err); + lock_status.hard_conflict_idx = Some(index); + }, + err @ LockFailedError::LockConflict { .. } => { + let index = lock_status.add_failed(err); + // If the requested lock is for a specific version, the transaction must be ABORTED + if lock.requested_version().is_some() { + lock_status.hard_conflict_idx = Some(index); + } + }, + } + }, + } + + if lock_status.is_hard_conflict() { + // If there are hard conflicts, there is no need to continue as this transaction will be ABORTED + break; + } } - Ok(()) + Ok(lock_status) } #[allow(clippy::too_many_lines)] - pub fn try_lock( + pub fn try_lock( &mut self, transaction_id: TransactionId, - requested_lock: &VersionedSubstateIdLockIntent, + requested_lock: &L, is_local_only: bool, ) -> Result<(), SubstateStoreError> { let requested_lock_type = requested_lock.lock_type(); - let requested_substate_id = requested_lock.versioned_substate_id().substate_id(); info!( target: LOG_TARGET, "🔒️ Requested substate lock: {}", requested_lock ); - let Some(existing) = self.get_latest_lock_by_id(requested_substate_id)? else { + let versioned_substate_id = requested_lock.to_versioned_substate_id(); + + let Some(existing) = self.get_latest_lock_by_id(versioned_substate_id.substate_id())? else { if requested_lock_type.is_output() { - self.assert_not_exist(requested_lock.versioned_substate_id())?; + self.assert_not_exist(&versioned_substate_id)?; } else { - self.assert_is_up(requested_lock.versioned_substate_id())?; + self.assert_is_up(&versioned_substate_id)?; } + let version = versioned_substate_id.version(); self.add_new_lock( - requested_lock.versioned_substate_id().substate_id.clone(), - SubstateLock::new( - transaction_id, - requested_lock.versioned_substate_id().version(), - requested_lock_type, - is_local_only, - ), + versioned_substate_id.substate_id, + SubstateLock::new(transaction_id, version, requested_lock_type, is_local_only), ); return Ok(()); }; @@ -221,26 +247,23 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor warn!( target: LOG_TARGET, "⚠️ Lock conflict: [{}] Read lock(local_only={}) is present. Requested lock is {}(local_only={})", - requested_lock.versioned_substate_id(), + versioned_substate_id, existing.is_local_only(), requested_lock_type, is_local_only ); - return Err(SubstateStoreError::LockConflict { - substate_id: requested_lock.versioned_substate_id().clone(), + return Err(LockFailedError::LockConflict { + substate_id: versioned_substate_id, existing_lock: existing.substate_lock(), requested_lock: requested_lock_type, - }); + } + .into()); } + let version = versioned_substate_id.version(); self.add_new_lock( - requested_lock.versioned_substate_id().substate_id.clone(), - SubstateLock::new( - transaction_id, - requested_lock.versioned_substate_id().version(), - requested_lock_type, - is_local_only, - ), + versioned_substate_id.substate_id, + SubstateLock::new(transaction_id, version, requested_lock_type, is_local_only), ); }, @@ -254,42 +277,40 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor warn!( target: LOG_TARGET, "⚠️ Lock conflict: [{}] Write lock(local_only={}) is present. Requested lock is {}(local_only={})", - requested_lock.versioned_substate_id(), + versioned_substate_id, existing.is_local_only(), requested_lock_type, is_local_only ); - return Err(SubstateStoreError::LockConflict { - substate_id: requested_lock.versioned_substate_id().clone(), + return Err(LockFailedError::LockConflict { + substate_id: versioned_substate_id, existing_lock: existing.substate_lock(), requested_lock: requested_lock_type, - }); + } + .into()); } if !requested_lock_type.is_output() { warn!( target: LOG_TARGET, "⚠️ Lock conflict: [{}] Write lock(local_only={}) is present. Requested lock is {}(local_only={})", - requested_lock.versioned_substate_id(), + versioned_substate_id, existing.is_local_only(), requested_lock_type, is_local_only ); - return Err(SubstateStoreError::LockConflict { - substate_id: requested_lock.versioned_substate_id().clone(), + return Err(LockFailedError::LockConflict { + substate_id: versioned_substate_id, existing_lock: existing.substate_lock(), requested_lock: requested_lock_type, - }); + } + .into()); } + let version = versioned_substate_id.version(); self.add_new_lock( - requested_lock.versioned_substate_id().substate_id.clone(), - SubstateLock::new( - transaction_id, - requested_lock.versioned_substate_id().version(), - SubstateLockType::Output, - is_local_only, - ), + versioned_substate_id.substate_id, + SubstateLock::new(transaction_id, version, SubstateLockType::Output, is_local_only), ); }, // If a substate is already locked as OUTPUT: @@ -303,16 +324,17 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor target: LOG_TARGET, "⚠️ Lock conflict: [{}, {}] Output lock(local_only={}) is present. Requested lock is {}(local_only={})", transaction_id, - requested_lock.versioned_substate_id(), + versioned_substate_id, existing.is_local_only(), requested_lock_type, is_local_only ); - return Err(SubstateStoreError::LockConflict { - substate_id: requested_lock.versioned_substate_id().clone(), + return Err(LockFailedError::LockConflict { + substate_id: versioned_substate_id, existing_lock: existing.substate_lock(), requested_lock: requested_lock_type, - }); + } + .into()); } if requested_lock_type.is_output() { @@ -320,22 +342,24 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor target: LOG_TARGET, "⚠️ Lock conflict: [{}, {}] Output lock(local_only={}) is present. Requested lock is Output(local_only={})", transaction_id, - requested_lock.versioned_substate_id(), + versioned_substate_id, existing.is_local_only(), is_local_only ); - return Err(SubstateStoreError::LockConflict { - substate_id: requested_lock.versioned_substate_id().clone(), + return Err(LockFailedError::LockConflict { + substate_id: versioned_substate_id, existing_lock: existing.substate_lock(), requested_lock: requested_lock_type, - }); + } + .into()); } + let version = versioned_substate_id.version(); self.add_new_lock( - requested_lock.versioned_substate_id().substate_id.clone(), + versioned_substate_id.substate_id, SubstateLock::new( transaction_id, - requested_lock.versioned_substate_id().version(), + version, // WRITE or READ requested_lock_type, is_local_only, @@ -347,9 +371,9 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor Ok(()) } - fn get_pending(&self, key: &VersionedSubstateId) -> Option<&SubstateChange> { + fn get_pending(&self, addr: &SubstateAddress) -> Option<&SubstateChange> { self.pending - .get(&key.to_substate_address()) + .get(addr) .map(|&pos| self.diff.get(pos).expect("Index map and diff are out of sync")) } @@ -375,7 +399,7 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor } fn assert_is_up(&self, id: &VersionedSubstateId) -> Result<(), SubstateStoreError> { - if let Some(change) = self.get_pending(id) { + if let Some(change) = self.get_pending(&id.to_substate_address()) { if change.is_down() { return Err(SubstateStoreError::SubstateIsDown { id: id.clone() }); } @@ -399,7 +423,7 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor } fn assert_is_down(&self, id: &VersionedSubstateId) -> Result<(), SubstateStoreError> { - if let Some(change) = self.get_pending(id) { + if let Some(change) = self.get_pending(&id.to_substate_address()) { if change.is_up() { return Err(SubstateStoreError::ExpectedSubstateDown { id: id.clone() }); } @@ -419,7 +443,7 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor } fn assert_not_exist(&self, id: &VersionedSubstateId) -> Result<(), SubstateStoreError> { - if let Some(change) = self.get_pending(id) { + if let Some(change) = self.get_pending(&id.to_substate_address()) { if change.is_up() { return Err(SubstateStoreError::ExpectedSubstateNotExist { id: id.clone() }); } @@ -445,3 +469,41 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor (self.diff, self.new_locks) } } + +#[derive(Debug, Default)] +pub struct LockStatus { + lock_failures: Vec, + hard_conflict_idx: Option, +} + +impl LockStatus { + pub fn new() -> Self { + Default::default() + } + + pub(self) fn add_failed(&mut self, err: LockFailedError) -> usize { + let index = self.lock_failures.len(); + self.lock_failures.push(err); + index + } + + /// Returns true if any of the lock requests failed. If not a hard conflict (see [LockStatus::hard_conflict]), the + /// transaction may be proposed later once the lock is released. + pub fn is_any_failed(&self) -> bool { + !self.lock_failures.is_empty() + } + + /// Returns the error message if there is a hard conflict. A hard conflict occurs when a VERSIONED substate lock is + /// requested and fails leading to the transaction to be ABORTED. + pub fn hard_conflict(&self) -> Option<&LockFailedError> { + self.hard_conflict_idx.map(|idx| &self.lock_failures[idx]) + } + + pub fn failures(&self) -> &[LockFailedError] { + &self.lock_failures + } + + pub fn is_hard_conflict(&self) -> bool { + self.hard_conflict_idx.is_some() + } +} diff --git a/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs b/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs index cfe8a7f5b..36dcd6c83 100644 --- a/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs +++ b/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs @@ -5,27 +5,28 @@ use std::{collections::HashSet, marker::PhantomData}; use indexmap::IndexMap; use log::*; -use tari_dan_common_types::{committee::CommitteeInfo, optional::IsNotFoundError, Epoch}; +use tari_dan_common_types::{ + committee::CommitteeInfo, + optional::IsNotFoundError, + Epoch, + SubstateRequirement, + ToSubstateAddress, + VersionedSubstateId, +}; use tari_dan_storage::{ - consensus_models::{ - Decision, - ExecutedTransaction, - SubstateLockType, - TransactionRecord, - VersionedSubstateIdLockIntent, - }, + consensus_models::{Decision, ExecutedTransaction, SubstateRequirementLockIntent, TransactionRecord}, StateStore, }; use tari_engine_types::{ commit_result::RejectReason, - substate::{Substate, SubstateId}, + substate::Substate, transaction_receipt::TransactionReceiptAddress, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId, VersionedSubstateId}; +use tari_transaction::{Transaction, TransactionId}; use super::{LocalPreparedTransaction, PledgedTransaction, PreparedTransaction}; use crate::{ - hotstuff::substate_store::PendingSubstateStore, + hotstuff::substate_store::{LockStatus, PendingSubstateStore}, traits::{BlockTransactionExecutor, BlockTransactionExecutorError, ReadableSubstateStore}, }; @@ -52,7 +53,8 @@ impl> store: &PendingSubstateStore, local_committee_info: &CommitteeInfo, transaction: &Transaction, - ) -> Result<(IndexMap, HashSet), BlockTransactionExecutorError> { + ) -> Result<(IndexMap, HashSet), BlockTransactionExecutorError> + { let mut resolved_substates = IndexMap::with_capacity(transaction.num_unique_inputs()); let mut non_local_inputs = HashSet::new(); @@ -67,12 +69,12 @@ impl> let id = VersionedSubstateId::new(input.substate_id, version); let substate = store.get(&id)?; info!(target: LOG_TARGET, "Resolved LOCAL substate: {id}"); - resolved_substates.insert(id.substate_id, substate); + resolved_substates.insert(id.into(), substate); }, None => { let substate = store.get_latest(&input.substate_id)?; info!(target: LOG_TARGET, "Resolved LOCAL unversioned substate: {input}"); - resolved_substates.insert(input.substate_id, substate); + resolved_substates.insert(input, substate); }, } } @@ -94,7 +96,7 @@ impl> { let version = id.version(); ( - id.substate_id, + id.into(), Substate::new(version, substate), ) }) @@ -115,7 +117,7 @@ impl> local_committee_info: &CommitteeInfo, current_epoch: Epoch, transaction_id: TransactionId, - ) -> Result { + ) -> Result<(PreparedTransaction, LockStatus), BlockTransactionExecutorError> { let mut transaction = TransactionRecord::get(store.read_transaction(), &transaction_id)?; let mut outputs = HashSet::new(); outputs.insert(VersionedSubstateId::new( @@ -148,14 +150,15 @@ impl> transaction.set_abort_reason(RejectReason::OneOrMoreInputsNotFound(err.to_string())); if is_local_only { warn!(target: LOG_TARGET, "⚠️ PREPARE: transaction {} only contains local inputs. Will abort locally", transaction_id); - return Ok(PreparedTransaction::new_local_early_abort(transaction)); + return Ok(( + PreparedTransaction::new_local_early_abort(transaction), + LockStatus::new(), + )); } else { warn!(target: LOG_TARGET, "⚠️ PREPARE: transaction {} has foreign inputs. Will prepare ABORT", transaction_id); - return Ok(PreparedTransaction::new_multishard( - transaction, - IndexMap::new(), - HashSet::new(), - outputs, + return Ok(( + PreparedTransaction::new_multishard(transaction, IndexMap::new(), HashSet::new(), outputs), + LockStatus::new(), )); } }, @@ -165,7 +168,10 @@ impl> // CASE: Invalid transaction, no inputs warn!(target: LOG_TARGET, "⚠️ PREPARE: transaction {transaction_id} has no inputs. Aborting..."); transaction.set_abort_reason(RejectReason::NoInputs); - return Ok(PreparedTransaction::new_local_early_abort(transaction)); + return Ok(( + PreparedTransaction::new_local_early_abort(transaction), + LockStatus::new(), + )); } let mut prepared = if non_local_inputs.is_empty() { @@ -214,14 +220,14 @@ impl> PreparedTransaction::new_multishard(transaction, local_inputs, non_local_inputs, outputs) }; - let lock_result = match &prepared { + let lock_summary = match &prepared { PreparedTransaction::LocalOnly(LocalPreparedTransaction::Accept(executed)) => { let requested_locks = executed.resolved_inputs().iter().chain(executed.resulting_outputs()); - store.try_lock_all(transaction_id, requested_locks, true) + store.try_lock_all(transaction_id, requested_locks, true)? }, PreparedTransaction::LocalOnly(LocalPreparedTransaction::EarlyAbort { .. }) => { // ABORT - No locks - Ok(()) + LockStatus::new() }, PreparedTransaction::MultiShard(multishard) => { if multishard.transaction().current_decision().is_commit() { @@ -229,39 +235,19 @@ impl> // specify this or we can correct the locks after execution. Currently, this limitation // prevents concurrent multi-shard read locks. let requested_locks = multishard.local_inputs().iter().map(|(substate_id, substate)| { - VersionedSubstateIdLockIntent::new( - VersionedSubstateId::new(substate_id.clone(), substate.version()), - SubstateLockType::Write, - ) - }) - // If outputs are known, lock all local outputs - .chain( - multishard - .outputs() - .iter() - .filter(|o| o.substate_id.is_transaction_receipt() || - local_committee_info.includes_substate_address(&o.to_substate_address())) - .map(|output| { - VersionedSubstateIdLockIntent::new(output.clone(), SubstateLockType::Output) - }), - ); - store.try_lock_all(transaction_id, requested_locks, false) + SubstateRequirementLockIntent::write(substate_id.clone(), substate.version()) + }); + store.try_lock_all(transaction_id, requested_locks, false)? } else { // ABORT - no locks - Ok(()) + LockStatus::new() } }, }; - match lock_result { - Ok(()) => Ok(prepared), - Err(err) => { - // TODO: In propose, we should only fail the transaction if versions are specified. Otherwise, we should - // wait to propose the transaction until the inputs are available. - let err = err.or_fatal_error()?; - prepared.set_abort_reason(RejectReason::FailedToLockInputs(err.to_string())); - Ok(prepared) - }, + if let Some(err) = lock_summary.hard_conflict() { + prepared.set_abort_reason(RejectReason::FailedToLockInputs(err.to_string())); } + Ok((prepared, lock_summary)) } } diff --git a/dan_layer/consensus/src/hotstuff/transaction_manager/prepared.rs b/dan_layer/consensus/src/hotstuff/transaction_manager/prepared.rs index 871b2f156..06c7aed79 100644 --- a/dan_layer/consensus/src/hotstuff/transaction_manager/prepared.rs +++ b/dan_layer/consensus/src/hotstuff/transaction_manager/prepared.rs @@ -4,20 +4,16 @@ use std::collections::HashSet; use indexmap::IndexMap; +use tari_dan_common_types::{SubstateLockType, SubstateRequirement, VersionedSubstateId}; use tari_dan_storage::consensus_models::{ Decision, Evidence, ExecutedTransaction, - SubstateLockType, TransactionExecution, TransactionRecord, VersionedSubstateIdLockIntent, }; -use tari_engine_types::{ - commit_result::RejectReason, - substate::{Substate, SubstateId}, -}; -use tari_transaction::{SubstateRequirement, VersionedSubstateId}; +use tari_engine_types::{commit_result::RejectReason, substate::Substate}; #[derive(Debug, Clone)] pub enum PreparedTransaction { @@ -36,7 +32,7 @@ impl PreparedTransaction { pub fn new_multishard( transaction: TransactionRecord, - local_inputs: IndexMap, + local_inputs: IndexMap, foreign_inputs: HashSet, outputs: HashSet, ) -> Self { @@ -82,7 +78,7 @@ impl LocalPreparedTransaction { #[derive(Debug, Clone)] pub struct MultiShardPreparedTransaction { transaction: TransactionRecord, - local_inputs: IndexMap, + local_inputs: IndexMap, outputs: HashSet, foreign_inputs: HashSet, } @@ -100,7 +96,7 @@ impl MultiShardPreparedTransaction { &self.foreign_inputs } - pub fn local_inputs(&self) -> &IndexMap { + pub fn local_inputs(&self) -> &IndexMap { &self.local_inputs } @@ -146,7 +142,7 @@ impl MultiShardPreparedTransaction { let inputs = self .local_inputs() .iter() - .map(|(substate_id, substate)| VersionedSubstateId::new(substate_id.clone(), substate.version())) + .map(|(requirement, substate)| VersionedSubstateId::new(requirement.substate_id.clone(), substate.version())) // TODO(correctness): to_zero_version is error prone when used in evidence and the correctness depends how it is used. // e.g. using it to determining which shard is involved is fine, but loading substate by the address is incorrect (v0 may or may not be the actual pledged substate) .chain(self.foreign_inputs().iter().map(|r| r.clone().or_zero_version())) diff --git a/dan_layer/consensus/src/hotstuff/worker.rs b/dan_layer/consensus/src/hotstuff/worker.rs index 436c918b0..c95dfe199 100644 --- a/dan_layer/consensus/src/hotstuff/worker.rs +++ b/dan_layer/consensus/src/hotstuff/worker.rs @@ -207,7 +207,8 @@ impl HotstuffWorker { .filter(|h| !h.is_zero()) .unwrap_or_else(NodeHeight::zero); - Ok::<_, HotStuffError>((current_epoch, current_height, HighQc::get(tx)?)) + let high_qc = HighQc::get(tx, leaf.epoch())?; + Ok::<_, HotStuffError>((current_epoch, current_height, high_qc)) })?; info!( diff --git a/dan_layer/consensus/src/messages/message.rs b/dan_layer/consensus/src/messages/message.rs index 17cbcb05e..64c07564c 100644 --- a/dan_layer/consensus/src/messages/message.rs +++ b/dan_layer/consensus/src/messages/message.rs @@ -39,7 +39,7 @@ impl HotstuffMessage { pub fn epoch(&self) -> Epoch { match self { - Self::NewView(msg) => msg.epoch, + Self::NewView(msg) => msg.high_qc.epoch(), Self::Proposal(msg) => msg.block.epoch(), Self::ForeignProposal(msg) => msg.block.epoch(), Self::Vote(msg) => msg.epoch, diff --git a/dan_layer/consensus/src/messages/new_view.rs b/dan_layer/consensus/src/messages/new_view.rs index 50ba30bd3..95c185eca 100644 --- a/dan_layer/consensus/src/messages/new_view.rs +++ b/dan_layer/consensus/src/messages/new_view.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause use serde::Serialize; -use tari_dan_common_types::{Epoch, NodeHeight}; +use tari_dan_common_types::NodeHeight; use tari_dan_storage::consensus_models::QuorumCertificate; use super::VoteMessage; @@ -10,7 +10,6 @@ use super::VoteMessage; #[derive(Debug, Clone, Serialize)] pub struct NewViewMessage { pub high_qc: QuorumCertificate, - pub epoch: Epoch, pub new_height: NodeHeight, pub last_vote: Option, } diff --git a/dan_layer/consensus/src/traits/substate_store.rs b/dan_layer/consensus/src/traits/substate_store.rs index fe754533e..f9754de40 100644 --- a/dan_layer/consensus/src/traits/substate_store.rs +++ b/dan_layer/consensus/src/traits/substate_store.rs @@ -1,12 +1,13 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +use tari_dan_common_types::{ToSubstateAddress, VersionedSubstateId}; use tari_dan_storage::{ consensus_models::{SubstateChange, SubstateRecord}, StateStoreReadTransaction, }; use tari_engine_types::substate::{Substate, SubstateDiff}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use crate::hotstuff::substate_store::SubstateStoreError; diff --git a/dan_layer/consensus/src/traits/transaction_executor.rs b/dan_layer/consensus/src/traits/transaction_executor.rs index e6d6dd5c1..364e5b943 100644 --- a/dan_layer/consensus/src/traits/transaction_executor.rs +++ b/dan_layer/consensus/src/traits/transaction_executor.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: BSD-3-Clause use indexmap::IndexMap; -use tari_dan_common_types::{optional::IsNotFoundError, Epoch}; +use tari_dan_common_types::{optional::IsNotFoundError, Epoch, SubstateRequirement}; use tari_dan_storage::{consensus_models::ExecutedTransaction, StateStore, StorageError}; -use tari_engine_types::substate::{Substate, SubstateId}; +use tari_engine_types::substate::Substate; use tari_transaction::Transaction; use crate::hotstuff::substate_store::SubstateStoreError; @@ -47,6 +47,6 @@ pub trait BlockTransactionExecutor { &self, transaction: Transaction, current_epoch: Epoch, - resolved_inputs: &IndexMap, + resolved_inputs: &IndexMap, ) -> Result; } diff --git a/dan_layer/consensus_tests/src/consensus.rs b/dan_layer/consensus_tests/src/consensus.rs index 40d34eed9..ff909255a 100644 --- a/dan_layer/consensus_tests/src/consensus.rs +++ b/dan_layer/consensus_tests/src/consensus.rs @@ -12,20 +12,13 @@ use std::time::Duration; use tari_common_types::types::PrivateKey; use tari_consensus::hotstuff::HotStuffError; -use tari_dan_common_types::{optional::Optional, Epoch, NodeHeight}; +use tari_dan_common_types::{optional::Optional, Epoch, NodeHeight, SubstateLockType, SubstateRequirement}; use tari_dan_storage::{ - consensus_models::{ - BlockId, - Command, - Decision, - SubstateLockType, - TransactionRecord, - VersionedSubstateIdLockIntent, - }, + consensus_models::{BlockId, Command, Decision, TransactionRecord, VersionedSubstateIdLockIntent}, StateStore, StateStoreReadTransaction, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; use crate::support::{ build_transaction_from, diff --git a/dan_layer/consensus_tests/src/substate_store.rs b/dan_layer/consensus_tests/src/substate_store.rs index 7b3e9710c..b69dbec16 100644 --- a/dan_layer/consensus_tests/src/substate_store.rs +++ b/dan_layer/consensus_tests/src/substate_store.rs @@ -2,19 +2,12 @@ // SPDX-License-Identifier: BSD-3-Clause use tari_consensus::{ - hotstuff::substate_store::{PendingSubstateStore, SubstateStoreError}, + hotstuff::substate_store::{LockFailedError, PendingSubstateStore, SubstateStoreError}, traits::{ReadableSubstateStore, WriteableSubstateStore}, }; -use tari_dan_common_types::{shard::Shard, NodeAddressable, PeerAddress}; +use tari_dan_common_types::{shard::Shard, NodeAddressable, PeerAddress, SubstateLockType, VersionedSubstateId}; use tari_dan_storage::{ - consensus_models::{ - BlockId, - QcId, - SubstateChange, - SubstateLockType, - SubstateRecord, - VersionedSubstateIdLockIntent, - }, + consensus_models::{BlockId, QcId, SubstateChange, SubstateRecord, SubstateRequirementLockIntent}, StateStore, }; use tari_engine_types::{ @@ -23,7 +16,6 @@ use tari_engine_types::{ }; use tari_state_store_sqlite::SqliteStateStore; use tari_template_lib::models::{ComponentAddress, EntityId, ObjectKey}; -use tari_transaction::VersionedSubstateId; use crate::support::{logging::setup_logger, TEST_NUM_PRESHARDS}; @@ -129,14 +121,14 @@ fn it_disallows_more_than_one_write_lock_non_local_only() { store .try_lock( tx_id(1), - &VersionedSubstateIdLockIntent::new(id.clone(), SubstateLockType::Read), + &SubstateRequirementLockIntent::new(id.clone(), 0, SubstateLockType::Read), true, ) .unwrap(); store .try_lock( tx_id(2), - &VersionedSubstateIdLockIntent::new(id.clone(), SubstateLockType::Read), + &SubstateRequirementLockIntent::new(id.clone(), 0, SubstateLockType::Read), true, ) .unwrap(); @@ -148,12 +140,15 @@ fn it_disallows_more_than_one_write_lock_non_local_only() { let err = store .try_lock( tx_id(3), - &VersionedSubstateIdLockIntent::new(id.clone(), SubstateLockType::Write), + &SubstateRequirementLockIntent::new(id.clone(), 0, SubstateLockType::Write), false, ) .unwrap_err(); - assert!(matches!(err, SubstateStoreError::LockConflict { .. })); + assert!(matches!( + err.ok_lock_failed().unwrap(), + LockFailedError::LockConflict { .. } + )); } #[test] @@ -168,7 +163,7 @@ fn it_allows_requesting_the_same_lock_within_one_transaction() { store .try_lock( tx_id(1), - &VersionedSubstateIdLockIntent::new(id.clone(), SubstateLockType::Write), + &SubstateRequirementLockIntent::new(id.clone(), 0, SubstateLockType::Write), false, ) .unwrap(); @@ -176,17 +171,20 @@ fn it_allows_requesting_the_same_lock_within_one_transaction() { let err = store .try_lock( tx_id(2), - &VersionedSubstateIdLockIntent::new(id.to_next_version(), SubstateLockType::Output), + &SubstateRequirementLockIntent::new(id.to_next_version(), 0, SubstateLockType::Output), false, ) .unwrap_err(); - assert!(matches!(err, SubstateStoreError::LockConflict { .. })); + assert!(matches!( + err.ok_lock_failed().unwrap(), + LockFailedError::LockConflict { .. } + )); // The same transaction is able to lock store .try_lock( tx_id(1), - &VersionedSubstateIdLockIntent::new(id.to_next_version(), SubstateLockType::Output), + &SubstateRequirementLockIntent::new(id.to_next_version(), 0, SubstateLockType::Output), false, ) .unwrap(); diff --git a/dan_layer/consensus_tests/src/support/epoch_manager.rs b/dan_layer/consensus_tests/src/support/epoch_manager.rs index a88ef3c1f..2e88ee16a 100644 --- a/dan_layer/consensus_tests/src/support/epoch_manager.rs +++ b/dan_layer/consensus_tests/src/support/epoch_manager.rs @@ -10,6 +10,7 @@ use tari_dan_common_types::{ Epoch, ShardGroup, SubstateAddress, + ToSubstateAddress, }; use tari_dan_storage::global::models::ValidatorNode; use tari_epoch_manager::{EpochManagerError, EpochManagerEvent, EpochManagerReader}; diff --git a/dan_layer/consensus_tests/src/support/harness.rs b/dan_layer/consensus_tests/src/support/harness.rs index 4d35521c8..86c48e1c3 100644 --- a/dan_layer/consensus_tests/src/support/harness.rs +++ b/dan_layer/consensus_tests/src/support/harness.rs @@ -10,13 +10,21 @@ use std::{ use futures::{stream::FuturesUnordered, FutureExt, StreamExt}; use itertools::Itertools; use tari_consensus::hotstuff::HotstuffEvent; -use tari_dan_common_types::{committee::Committee, shard::Shard, Epoch, NodeHeight, NumPreshards, ShardGroup}; +use tari_dan_common_types::{ + committee::Committee, + shard::Shard, + Epoch, + NodeHeight, + NumPreshards, + ShardGroup, + SubstateLockType, + VersionedSubstateId, +}; use tari_dan_storage::{ consensus_models::{ BlockId, Decision, QcId, - SubstateLockType, SubstateRecord, TransactionExecution, TransactionRecord, @@ -32,7 +40,7 @@ use tari_engine_types::{ }; use tari_epoch_manager::EpochManagerReader; use tari_shutdown::{Shutdown, ShutdownSignal}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use tokio::{sync::broadcast, task, time::sleep}; use super::{ diff --git a/dan_layer/consensus_tests/src/support/helpers.rs b/dan_layer/consensus_tests/src/support/helpers.rs index bd25e3fa8..c63e0512c 100644 --- a/dan_layer/consensus_tests/src/support/helpers.rs +++ b/dan_layer/consensus_tests/src/support/helpers.rs @@ -11,10 +11,10 @@ use tari_dan_common_types::{ NumPreshards, ShardGroup, SubstateAddress, + VersionedSubstateId, }; use tari_engine_types::substate::SubstateId; use tari_template_lib::models::{ComponentAddress, ComponentKey, EntityId, ObjectKey}; -use tari_transaction::VersionedSubstateId; use crate::support::TestAddress; diff --git a/dan_layer/consensus_tests/src/support/transaction.rs b/dan_layer/consensus_tests/src/support/transaction.rs index e2bb7b045..c9d485816 100644 --- a/dan_layer/consensus_tests/src/support/transaction.rs +++ b/dan_layer/consensus_tests/src/support/transaction.rs @@ -5,10 +5,10 @@ use std::{iter, time::Duration}; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; use tari_common_types::types::PrivateKey; +use tari_dan_common_types::{SubstateLockType, VersionedSubstateId}; use tari_dan_storage::consensus_models::{ Decision, ExecutedTransaction, - SubstateLockType, TransactionExecution, TransactionRecord, VersionedSubstateIdLockIntent, @@ -20,7 +20,7 @@ use tari_engine_types::{ substate::{Substate, SubstateDiff, SubstateId}, transaction_receipt::{TransactionReceipt, TransactionReceiptAddress}, }; -use tari_transaction::{Transaction, TransactionId, VersionedSubstateId}; +use tari_transaction::{Transaction, TransactionId}; use crate::support::{committee_number_to_shard_group, helpers::random_substate_in_shard_group, TEST_NUM_PRESHARDS}; diff --git a/dan_layer/consensus_tests/src/support/transaction_executor.rs b/dan_layer/consensus_tests/src/support/transaction_executor.rs index 2b90aebb2..1f43f7aff 100644 --- a/dan_layer/consensus_tests/src/support/transaction_executor.rs +++ b/dan_layer/consensus_tests/src/support/transaction_executor.rs @@ -3,12 +3,12 @@ use indexmap::IndexMap; use tari_consensus::traits::{BlockTransactionExecutor, BlockTransactionExecutorError}; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_dan_storage::{ consensus_models::{ExecutedTransaction, TransactionRecord}, StateStore, }; -use tari_engine_types::substate::{Substate, SubstateId}; +use tari_engine_types::substate::Substate; use tari_transaction::Transaction; use crate::support::executions_store::TestTransactionExecutionsStore; @@ -38,7 +38,7 @@ impl BlockTransactionExecutor for TestBloc &self, transaction: Transaction, _current_epoch: Epoch, - _resolved_inputs: &IndexMap, + _resolved_inputs: &IndexMap, ) -> Result { let execution = self.store.get(transaction.id()).unwrap_or_else(|| { panic!( diff --git a/dan_layer/indexer_lib/src/transaction_autofiller.rs b/dan_layer/indexer_lib/src/transaction_autofiller.rs index a16b93a37..a31aa475e 100644 --- a/dan_layer/indexer_lib/src/transaction_autofiller.rs +++ b/dan_layer/indexer_lib/src/transaction_autofiller.rs @@ -5,13 +5,13 @@ use std::{collections::HashMap, sync::Arc}; use futures::{stream::FuturesOrdered, StreamExt}; use log::*; -use tari_dan_common_types::NodeAddressable; +use tari_dan_common_types::{NodeAddressable, SubstateRequirement, VersionedSubstateId}; use tari_engine_types::{ indexed_value::IndexedValueError, substate::{Substate, SubstateId}, }; use tari_epoch_manager::EpochManagerReader; -use tari_transaction::{SubstateRequirement, Transaction, VersionedSubstateId}; +use tari_transaction::Transaction; use tari_validator_node_rpc::client::{SubstateResult, ValidatorNodeClientFactory}; use tokio::task::JoinError; diff --git a/dan_layer/p2p/proto/consensus.proto b/dan_layer/p2p/proto/consensus.proto index 2521e386e..e60fd6021 100644 --- a/dan_layer/p2p/proto/consensus.proto +++ b/dan_layer/p2p/proto/consensus.proto @@ -24,8 +24,7 @@ message HotStuffMessage { message NewViewMessage { QuorumCertificate high_qc = 1; uint64 new_height = 2; - uint64 epoch = 3; - VoteMessage last_vote = 4; + VoteMessage last_vote = 3; } message ProposalMessage { diff --git a/dan_layer/p2p/src/conversions/consensus.rs b/dan_layer/p2p/src/conversions/consensus.rs index 1d87fc2a7..13bf6e541 100644 --- a/dan_layer/p2p/src/conversions/consensus.rs +++ b/dan_layer/p2p/src/conversions/consensus.rs @@ -38,7 +38,15 @@ use tari_consensus::messages::{ VoteMessage, }; use tari_crypto::tari_utilities::ByteArray; -use tari_dan_common_types::{shard::Shard, Epoch, NodeHeight, ShardGroup, ValidatorMetadata}; +use tari_dan_common_types::{ + shard::Shard, + Epoch, + NodeHeight, + ShardGroup, + SubstateLockType, + ValidatorMetadata, + VersionedSubstateId, +}; use tari_dan_storage::consensus_models::{ BlockId, Command, @@ -53,14 +61,13 @@ use tari_dan_storage::consensus_models::{ QuorumCertificate, QuorumDecision, SubstateDestroyed, - SubstateLockType, SubstatePledge, SubstatePledges, SubstateRecord, TransactionAtom, }; use tari_engine_types::substate::{SubstateId, SubstateValue}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use crate::proto::{self}; // -------------------------------- HotstuffMessage -------------------------------- // @@ -126,7 +133,6 @@ impl From<&NewViewMessage> for proto::consensus::NewViewMessage { Self { high_qc: Some((&value.high_qc).into()), new_height: value.new_height.0, - epoch: value.epoch.as_u64(), last_vote: value.last_vote.as_ref().map(|a| a.into()), } } @@ -139,7 +145,6 @@ impl TryFrom for NewViewMessage { Ok(NewViewMessage { high_qc: value.high_qc.ok_or_else(|| anyhow!("High QC is missing"))?.try_into()?, new_height: value.new_height.into(), - epoch: Epoch(value.epoch), last_vote: value .last_vote .map(|a: proto::consensus::VoteMessage| a.try_into()) diff --git a/dan_layer/p2p/src/conversions/transaction.rs b/dan_layer/p2p/src/conversions/transaction.rs index 88c08784a..0246c52ae 100644 --- a/dan_layer/p2p/src/conversions/transaction.rs +++ b/dan_layer/p2p/src/conversions/transaction.rs @@ -26,7 +26,7 @@ use anyhow::anyhow; use tari_bor::decode_exact; use tari_common_types::types::{Commitment, PrivateKey, PublicKey}; use tari_crypto::{ristretto::RistrettoComSig, tari_utilities::ByteArray}; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement, VersionedSubstateId}; use tari_engine_types::{confidential::ConfidentialClaim, instruction::Instruction, substate::SubstateId}; use tari_template_lib::{ args::Arg, @@ -41,7 +41,7 @@ use tari_template_lib::{ ViewableBalanceProof, }, }; -use tari_transaction::{SubstateRequirement, Transaction, UnsignedTransaction, VersionedSubstateId}; +use tari_transaction::{Transaction, UnsignedTransaction}; use crate::{ proto::{ diff --git a/dan_layer/rpc_state_sync/src/manager.rs b/dan_layer/rpc_state_sync/src/manager.rs index b7611788d..bc48f8c87 100644 --- a/dan_layer/rpc_state_sync/src/manager.rs +++ b/dan_layer/rpc_state_sync/src/manager.rs @@ -19,6 +19,7 @@ use tari_dan_common_types::{ NodeHeight, PeerAddress, ShardGroup, + VersionedSubstateId, }; use tari_dan_p2p::proto::rpc::{GetCheckpointRequest, GetCheckpointResponse, SyncStateRequest}; use tari_dan_storage::{ @@ -50,7 +51,6 @@ use tari_state_tree::{ Version, SPARSE_MERKLE_PLACEHOLDER_HASH, }; -use tari_transaction::VersionedSubstateId; use tari_validator_node_rpc::{ client::{TariValidatorNodeRpcClientFactory, ValidatorNodeClientFactory}, rpc_service::ValidatorNodeRpcClient, diff --git a/dan_layer/state_store_sqlite/src/reader.rs b/dan_layer/state_store_sqlite/src/reader.rs index cce4be41d..31c2259b5 100644 --- a/dan_layer/state_store_sqlite/src/reader.rs +++ b/dan_layer/state_store_sqlite/src/reader.rs @@ -29,7 +29,17 @@ use indexmap::IndexMap; use log::*; use serde::{de::DeserializeOwned, Serialize}; use tari_common_types::types::{FixedHash, PublicKey}; -use tari_dan_common_types::{shard::Shard, Epoch, NodeAddressable, NodeHeight, ShardGroup, SubstateAddress}; +use tari_dan_common_types::{ + shard::Shard, + Epoch, + NodeAddressable, + NodeHeight, + ShardGroup, + SubstateAddress, + SubstateRequirement, + ToSubstateAddress, + VersionedSubstateId, +}; use tari_dan_storage::{ consensus_models::{ Block, @@ -74,7 +84,7 @@ use tari_dan_storage::{ }; use tari_engine_types::substate::SubstateId; use tari_state_tree::{Node, NodeKey, TreeNode, Version}; -use tari_transaction::{SubstateRequirement, TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use tari_utilities::ByteArray; use crate::{ @@ -453,10 +463,11 @@ impl<'tx, TAddr: NodeAddressable + Serialize + DeserializeOwned + 'tx> StateStor leaf_block.try_into() } - fn high_qc_get(&self) -> Result { + fn high_qc_get(&self, epoch: Epoch) -> Result { use crate::schema::high_qcs; let high_qc = high_qcs::table + .filter(high_qcs::epoch.eq(epoch.as_u64() as i64)) .order_by(high_qcs::id.desc()) .first::(self.connection()) .map_err(|e| SqliteStorageError::DieselError { diff --git a/dan_layer/state_store_sqlite/src/sql_models/block_diff.rs b/dan_layer/state_store_sqlite/src/sql_models/block_diff.rs index ff31e9b7a..68429417b 100644 --- a/dan_layer/state_store_sqlite/src/sql_models/block_diff.rs +++ b/dan_layer/state_store_sqlite/src/sql_models/block_diff.rs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: BSD-3-Clause use diesel::Queryable; -use tari_dan_common_types::shard::Shard; +use tari_dan_common_types::{shard::Shard, VersionedSubstateId}; use tari_dan_storage::{consensus_models, consensus_models::BlockId, StorageError}; -use tari_transaction::VersionedSubstateId; use time::PrimitiveDateTime; use crate::serialization::{deserialize_hex_try_from, deserialize_json}; diff --git a/dan_layer/state_store_sqlite/src/writer.rs b/dan_layer/state_store_sqlite/src/writer.rs index a8305b022..d461c79e5 100644 --- a/dan_layer/state_store_sqlite/src/writer.rs +++ b/dan_layer/state_store_sqlite/src/writer.rs @@ -16,7 +16,17 @@ use diesel::{ }; use indexmap::IndexMap; use log::*; -use tari_dan_common_types::{optional::Optional, shard::Shard, Epoch, NodeAddressable, NodeHeight, ShardGroup}; +use tari_dan_common_types::{ + optional::Optional, + shard::Shard, + Epoch, + NodeAddressable, + NodeHeight, + ShardGroup, + SubstateLockType, + ToSubstateAddress, + VersionedSubstateId, +}; use tari_dan_storage::{ consensus_models::{ Block, @@ -42,7 +52,6 @@ use tari_dan_storage::{ QcId, QuorumCertificate, SubstateLock, - SubstateLockType, SubstatePledge, SubstatePledges, SubstateRecord, @@ -60,7 +69,7 @@ use tari_dan_storage::{ }; use tari_engine_types::substate::SubstateId; use tari_state_tree::{Node, NodeKey, StaleTreeNode, TreeNode, Version}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use tari_utilities::ByteArray; use time::{OffsetDateTime, PrimitiveDateTime}; diff --git a/dan_layer/storage/src/consensus_models/block_pledges.rs b/dan_layer/storage/src/consensus_models/block_pledges.rs index 8c19bd4fd..f6b0b0332 100644 --- a/dan_layer/storage/src/consensus_models/block_pledges.rs +++ b/dan_layer/storage/src/consensus_models/block_pledges.rs @@ -8,10 +8,11 @@ use std::{ }; use serde::{Deserialize, Serialize}; +use tari_dan_common_types::{SubstateLockType, SubstateRequirement, VersionedSubstateId}; use tari_engine_types::substate::{SubstateId, SubstateValue}; -use tari_transaction::{SubstateRequirement, TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; -use crate::consensus_models::{SubstateLockType, VersionedSubstateIdLockIntent}; +use crate::consensus_models::VersionedSubstateIdLockIntent; #[allow(clippy::mutable_key_type)] pub type SubstatePledges = HashSet; #[derive(Debug, Clone, Serialize, Deserialize, Default)] diff --git a/dan_layer/storage/src/consensus_models/evidence.rs b/dan_layer/storage/src/consensus_models/evidence.rs index f52d7e257..205e6427c 100644 --- a/dan_layer/storage/src/consensus_models/evidence.rs +++ b/dan_layer/storage/src/consensus_models/evidence.rs @@ -8,9 +8,9 @@ use std::{ use indexmap::{IndexMap, IndexSet}; use serde::{Deserialize, Serialize}; -use tari_dan_common_types::{committee::CommitteeInfo, SubstateAddress}; +use tari_dan_common_types::{committee::CommitteeInfo, SubstateAddress, SubstateLockType, ToSubstateAddress}; -use crate::consensus_models::{QcId, SubstateLockType, VersionedSubstateIdLockIntent}; +use crate::consensus_models::{QcId, VersionedSubstateIdLockIntent}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] #[cfg_attr( diff --git a/dan_layer/storage/src/consensus_models/executed_transaction.rs b/dan_layer/storage/src/consensus_models/executed_transaction.rs index 680aca38c..31ab688cf 100644 --- a/dan_layer/storage/src/consensus_models/executed_transaction.rs +++ b/dan_layer/storage/src/consensus_models/executed_transaction.rs @@ -2,21 +2,22 @@ // SPDX-License-Identifier: BSD-3-Clause use std::{ - borrow::Borrow, collections::{HashMap, HashSet}, - fmt, hash::Hash, ops::Deref, time::Duration, }; use serde::{Deserialize, Serialize}; -use tari_dan_common_types::{optional::Optional, SubstateAddress}; -use tari_engine_types::{ - commit_result::{ExecuteResult, RejectReason}, - substate::SubstateId, +use tari_dan_common_types::{ + optional::Optional, + SubstateAddress, + SubstateLockType, + ToSubstateAddress, + VersionedSubstateId, }; -use tari_transaction::{Transaction, TransactionId, VersionedSubstateId}; +use tari_engine_types::commit_result::{ExecuteResult, RejectReason}; +use tari_transaction::{Transaction, TransactionId}; use crate::{ consensus_models::{ @@ -24,10 +25,10 @@ use crate::{ BlockTransactionExecution, Decision, Evidence, - SubstateLockType, TransactionAtom, TransactionExecution, TransactionRecord, + VersionedSubstateIdLockIntent, }, StateStoreReadTransaction, StateStoreWriteTransaction, @@ -126,7 +127,7 @@ impl ExecutedTransaction { } pub fn all_inputs_iter(&self) -> impl Iterator + '_ { - self.resolved_inputs.iter().map(|input| &input.versioned_substate_id) + self.resolved_inputs.iter().map(|input| input.versioned_substate_id()) } pub fn involved_addresses_iter(&self) -> impl Iterator + '_ { @@ -399,78 +400,3 @@ impl Hash for ExecutedTransaction { self.transaction.id().hash(state); } } - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[cfg_attr( - feature = "ts", - derive(ts_rs::TS), - ts(export, export_to = "../../bindings/src/types/") -)] -pub struct VersionedSubstateIdLockIntent { - versioned_substate_id: VersionedSubstateId, - lock_type: SubstateLockType, -} - -impl VersionedSubstateIdLockIntent { - pub fn new(versioned_substate_id: VersionedSubstateId, lock: SubstateLockType) -> Self { - Self { - versioned_substate_id, - lock_type: lock, - } - } - - pub fn read(versioned_substate_id: VersionedSubstateId) -> Self { - Self::new(versioned_substate_id, SubstateLockType::Read) - } - - pub fn write(versioned_substate_id: VersionedSubstateId) -> Self { - Self::new(versioned_substate_id, SubstateLockType::Write) - } - - pub fn output(versioned_substate_id: VersionedSubstateId) -> Self { - Self::new(versioned_substate_id, SubstateLockType::Output) - } - - pub fn to_substate_address(&self) -> SubstateAddress { - self.versioned_substate_id.to_substate_address() - } - - pub fn versioned_substate_id(&self) -> &VersionedSubstateId { - &self.versioned_substate_id - } - - pub fn into_versioned_substate_id(self) -> VersionedSubstateId { - self.versioned_substate_id - } - - pub fn substate_id(&self) -> &SubstateId { - self.versioned_substate_id.substate_id() - } - - pub fn version(&self) -> u32 { - self.versioned_substate_id.version() - } - - pub fn lock_type(&self) -> SubstateLockType { - self.lock_type - } -} - -impl Borrow for VersionedSubstateIdLockIntent { - fn borrow(&self) -> &SubstateId { - self.substate_id() - } -} - -impl fmt::Display for VersionedSubstateIdLockIntent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} ({})", self.versioned_substate_id, self.lock_type) - } -} - -impl Hash for VersionedSubstateIdLockIntent { - fn hash(&self, state: &mut H) { - // A VersionedSubstateIdLockIntent is uniquely identified by the VersionedSubstateId - self.versioned_substate_id.hash(state); - } -} diff --git a/dan_layer/storage/src/consensus_models/high_qc.rs b/dan_layer/storage/src/consensus_models/high_qc.rs index dc7cdab40..49b78936a 100644 --- a/dan_layer/storage/src/consensus_models/high_qc.rs +++ b/dan_layer/storage/src/consensus_models/high_qc.rs @@ -67,8 +67,8 @@ impl HighQc { } impl HighQc { - pub fn get(tx: &TTx) -> Result { - tx.high_qc_get() + pub fn get(tx: &TTx, epoch: Epoch) -> Result { + tx.high_qc_get(epoch) } pub fn get_quorum_certificate( diff --git a/dan_layer/storage/src/consensus_models/lock_intent.rs b/dan_layer/storage/src/consensus_models/lock_intent.rs new file mode 100644 index 000000000..dcdaa7bed --- /dev/null +++ b/dan_layer/storage/src/consensus_models/lock_intent.rs @@ -0,0 +1,224 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{borrow::Borrow, fmt, hash::Hash}; + +use serde::{Deserialize, Serialize}; +use tari_dan_common_types::{LockIntent, SubstateAddress, SubstateLockType, SubstateRequirement, VersionedSubstateId}; +use tari_engine_types::substate::SubstateId; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr( + feature = "ts", + derive(ts_rs::TS), + ts(export, export_to = "../../bindings/src/types/") +)] +pub struct VersionedSubstateIdLockIntent { + versioned_substate_id: VersionedSubstateId, + lock_type: SubstateLockType, +} + +impl VersionedSubstateIdLockIntent { + pub fn new(versioned_substate_id: VersionedSubstateId, lock: SubstateLockType) -> Self { + Self { + versioned_substate_id, + lock_type: lock, + } + } + + pub fn read(versioned_substate_id: VersionedSubstateId) -> Self { + Self::new(versioned_substate_id, SubstateLockType::Read) + } + + pub fn write(versioned_substate_id: VersionedSubstateId) -> Self { + Self::new(versioned_substate_id, SubstateLockType::Write) + } + + pub fn output(versioned_substate_id: VersionedSubstateId) -> Self { + Self::new(versioned_substate_id, SubstateLockType::Output) + } + + pub fn versioned_substate_id(&self) -> &VersionedSubstateId { + &self.versioned_substate_id + } + + pub fn into_versioned_substate_id(self) -> VersionedSubstateId { + self.versioned_substate_id + } + + pub fn substate_id(&self) -> &SubstateId { + self.versioned_substate_id.substate_id() + } + + pub fn version(&self) -> u32 { + self.versioned_substate_id.version() + } + + pub fn lock_type(&self) -> SubstateLockType { + self.lock_type + } +} + +impl Borrow for VersionedSubstateIdLockIntent { + fn borrow(&self) -> &SubstateId { + self.substate_id() + } +} + +impl fmt::Display for VersionedSubstateIdLockIntent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} ({})", self.versioned_substate_id, self.lock_type) + } +} + +impl Hash for VersionedSubstateIdLockIntent { + fn hash(&self, state: &mut H) { + // A VersionedSubstateIdLockIntent is uniquely identified by the VersionedSubstateId + self.versioned_substate_id.hash(state); + } +} + +impl<'a> LockIntent for &'a VersionedSubstateIdLockIntent { + fn substate_id(&self) -> &SubstateId { + self.versioned_substate_id.substate_id() + } + + fn lock_type(&self) -> SubstateLockType { + self.lock_type + } + + fn version_to_lock(&self) -> u32 { + self.versioned_substate_id.version() + } + + fn requested_version(&self) -> Option { + Some(self.versioned_substate_id.version()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr( + feature = "ts", + derive(ts_rs::TS), + ts(export, export_to = "../../bindings/src/types/") +)] +pub struct SubstateRequirementLockIntent { + substate_requirement: SubstateRequirement, + version_to_lock: u32, + lock_type: SubstateLockType, +} + +impl SubstateRequirementLockIntent { + pub fn new>(substate_id: T, version_to_lock: u32, lock: SubstateLockType) -> Self { + Self { + substate_requirement: substate_id.into(), + version_to_lock, + lock_type: lock, + } + } + + pub fn read(substate_id: SubstateRequirement, version_to_lock: u32) -> Self { + Self::new(substate_id, version_to_lock, SubstateLockType::Read) + } + + pub fn write(substate_id: SubstateRequirement, version_to_lock: u32) -> Self { + Self::new(substate_id, version_to_lock, SubstateLockType::Write) + } + + pub fn output(substate_id: SubstateRequirement, version_to_lock: u32) -> Self { + Self::new(substate_id, version_to_lock, SubstateLockType::Output) + } + + pub fn to_substate_address(&self) -> Option { + self.substate_requirement.to_substate_address() + } + + pub fn substate_requirement(&self) -> &SubstateRequirement { + &self.substate_requirement + } + + pub fn into_substate_requirement(self) -> SubstateRequirement { + self.substate_requirement + } + + pub fn substate_id(&self) -> &SubstateId { + self.substate_requirement.substate_id() + } + + pub fn version_to_lock(&self) -> u32 { + self.version_to_lock + } + + pub fn lock_type(&self) -> SubstateLockType { + self.lock_type + } + + pub fn to_versioned_lock_intent(&self) -> VersionedSubstateIdLockIntent { + VersionedSubstateIdLockIntent::new( + VersionedSubstateId::new(self.substate_id().clone(), self.version_to_lock), + self.lock_type, + ) + } +} + +impl<'a> LockIntent for &'a SubstateRequirementLockIntent { + fn substate_id(&self) -> &SubstateId { + self.substate_requirement.substate_id() + } + + fn lock_type(&self) -> SubstateLockType { + self.lock_type + } + + fn version_to_lock(&self) -> u32 { + self.version_to_lock + } + + fn requested_version(&self) -> Option { + self.substate_requirement.version() + } +} + +impl LockIntent for SubstateRequirementLockIntent { + fn substate_id(&self) -> &SubstateId { + self.substate_requirement.substate_id() + } + + fn lock_type(&self) -> SubstateLockType { + self.lock_type + } + + fn version_to_lock(&self) -> u32 { + self.version_to_lock + } + + fn requested_version(&self) -> Option { + self.substate_requirement.version() + } +} + +impl From for SubstateRequirementLockIntent { + fn from(intent: VersionedSubstateIdLockIntent) -> Self { + let version = intent.versioned_substate_id.version(); + Self::new(intent.versioned_substate_id, version, intent.lock_type) + } +} + +impl Borrow for SubstateRequirementLockIntent { + fn borrow(&self) -> &SubstateId { + self.substate_id() + } +} + +impl fmt::Display for SubstateRequirementLockIntent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} ({})", self.substate_requirement, self.lock_type) + } +} + +impl Hash for SubstateRequirementLockIntent { + fn hash(&self, state: &mut H) { + // A SubstateRequirementLockIntent is uniquely identified by the SubstateRequirement + self.substate_requirement.hash(state); + } +} diff --git a/dan_layer/storage/src/consensus_models/mod.rs b/dan_layer/storage/src/consensus_models/mod.rs index dae65b846..692b71848 100644 --- a/dan_layer/storage/src/consensus_models/mod.rs +++ b/dan_layer/storage/src/consensus_models/mod.rs @@ -20,6 +20,7 @@ mod last_sent_vote; mod last_voted; mod leader_fee; mod leaf_block; +mod lock_intent; mod locked_block; mod quorum; mod quorum_certificate; @@ -56,6 +57,7 @@ pub use last_sent_vote::*; pub use last_voted::*; pub use leader_fee::*; pub use leaf_block::*; +pub use lock_intent::*; pub use locked_block::*; pub use quorum::*; pub use quorum_certificate::*; diff --git a/dan_layer/storage/src/consensus_models/quorum_certificate.rs b/dan_layer/storage/src/consensus_models/quorum_certificate.rs index b1b16aa85..d4cecc4a1 100644 --- a/dan_layer/storage/src/consensus_models/quorum_certificate.rs +++ b/dan_layer/storage/src/consensus_models/quorum_certificate.rs @@ -195,7 +195,7 @@ impl QuorumCertificate { TTx: StateStoreWriteTransaction + Deref + ?Sized, TTx::Target: StateStoreReadTransaction, { - let high_qc = HighQc::get(&**tx)?; + let high_qc = HighQc::get(&**tx, self.epoch)?; if high_qc.block_height() < self.block_height() { return Ok((true, self.as_high_qc())); } diff --git a/dan_layer/storage/src/consensus_models/substate.rs b/dan_layer/storage/src/consensus_models/substate.rs index 7cbc630c5..fc5ef2038 100644 --- a/dan_layer/storage/src/consensus_models/substate.rs +++ b/dan_layer/storage/src/consensus_models/substate.rs @@ -1,23 +1,20 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use std::{ - borrow::Borrow, - collections::HashSet, - fmt, - fmt::Display, - hash::Hash, - iter, - iter::Peekable, - ops::RangeInclusive, - str::FromStr, -}; +use std::{borrow::Borrow, collections::HashSet, fmt, fmt::Display, iter, iter::Peekable, ops::RangeInclusive}; use serde::{Deserialize, Serialize}; use tari_common_types::types::FixedHash; -use tari_dan_common_types::{shard::Shard, Epoch, NodeHeight, SubstateAddress}; +use tari_dan_common_types::{ + shard::Shard, + Epoch, + NodeHeight, + SubstateAddress, + SubstateRequirement, + VersionedSubstateId, +}; use tari_engine_types::substate::{hash_substate, Substate, SubstateId, SubstateValue}; -use tari_transaction::{SubstateRequirement, TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use crate::{ consensus_models::{BlockId, QcId, QuorumCertificate, SubstateLock}, @@ -399,60 +396,6 @@ impl Display for SubstateUpdate { } } -/// Substate lock flags -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[cfg_attr( - feature = "ts", - derive(ts_rs::TS), - ts(export, export_to = "../../bindings/src/types/") -)] -pub enum SubstateLockType { - Read, - Write, - Output, -} - -impl SubstateLockType { - pub fn is_write(&self) -> bool { - matches!(self, Self::Write) - } - - pub fn is_read(&self) -> bool { - matches!(self, Self::Read) - } - - pub fn is_output(&self) -> bool { - matches!(self, Self::Output) - } -} - -impl fmt::Display for SubstateLockType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Read => write!(f, "Read"), - Self::Write => write!(f, "Write"), - Self::Output => write!(f, "Output"), - } - } -} - -impl FromStr for SubstateLockType { - type Err = SubstateLockFlagParseError; - - fn from_str(s: &str) -> Result { - match s { - "Read" => Ok(Self::Read), - "Write" => Ok(Self::Write), - "Output" => Ok(Self::Output), - _ => Err(SubstateLockFlagParseError), - } - } -} - -#[derive(Debug, thiserror::Error)] -#[error("Failed to parse SubstateLockFlag")] -pub struct SubstateLockFlagParseError; - #[derive(Debug, Clone, Copy)] pub enum SubstateLockState { /// The lock was successfully acquired diff --git a/dan_layer/storage/src/consensus_models/substate_change.rs b/dan_layer/storage/src/consensus_models/substate_change.rs index f04de6b53..fee64b261 100644 --- a/dan_layer/storage/src/consensus_models/substate_change.rs +++ b/dan_layer/storage/src/consensus_models/substate_change.rs @@ -1,10 +1,10 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use tari_dan_common_types::{shard::Shard, SubstateAddress}; +use tari_dan_common_types::{shard::Shard, SubstateAddress, ToSubstateAddress, VersionedSubstateId}; use tari_engine_types::substate::Substate; use tari_state_tree::SubstateTreeChange; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; use crate::consensus_models::SubstateRecord; diff --git a/dan_layer/storage/src/consensus_models/substate_lock.rs b/dan_layer/storage/src/consensus_models/substate_lock.rs index 05485114e..083c97a3b 100644 --- a/dan_layer/storage/src/consensus_models/substate_lock.rs +++ b/dan_layer/storage/src/consensus_models/substate_lock.rs @@ -3,11 +3,11 @@ use std::fmt; -use tari_dan_common_types::SubstateAddress; +use tari_dan_common_types::{SubstateAddress, SubstateLockType, VersionedSubstateId}; use tari_engine_types::substate::{SubstateId, SubstateValue}; -use tari_transaction::{TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; -use crate::consensus_models::{BlockId, SubstateLockType, VersionedSubstateIdLockIntent}; +use crate::consensus_models::{BlockId, VersionedSubstateIdLockIntent}; #[derive(Debug, Clone, Copy)] pub struct SubstateLock { diff --git a/dan_layer/storage/src/consensus_models/transaction.rs b/dan_layer/storage/src/consensus_models/transaction.rs index 7caa63992..529a36f4e 100644 --- a/dan_layer/storage/src/consensus_models/transaction.rs +++ b/dan_layer/storage/src/consensus_models/transaction.rs @@ -5,7 +5,7 @@ use std::{collections::HashSet, ops::Deref, time::Duration}; use log::*; use serde::Deserialize; -use tari_dan_common_types::committee::CommitteeInfo; +use tari_dan_common_types::{committee::CommitteeInfo, SubstateLockType}; use tari_engine_types::{ commit_result::{ExecuteResult, FinalizeResult, RejectReason}, transaction_receipt::TransactionReceiptAddress, @@ -17,7 +17,6 @@ use crate::{ BlockId, Decision, ExecutedTransaction, - SubstateLockType, SubstatePledge, SubstatePledges, TransactionExecution, diff --git a/dan_layer/storage/src/consensus_models/transaction_pool.rs b/dan_layer/storage/src/consensus_models/transaction_pool.rs index 41570ef51..c634465a9 100644 --- a/dan_layer/storage/src/consensus_models/transaction_pool.rs +++ b/dan_layer/storage/src/consensus_models/transaction_pool.rs @@ -15,6 +15,7 @@ use tari_dan_common_types::{ committee::CommitteeInfo, optional::{IsNotFoundError, Optional}, ShardGroup, + ToSubstateAddress, }; use tari_transaction::TransactionId; @@ -605,6 +606,7 @@ impl TransactionPoolRecord { }; update.insert(tx)?; + self.is_ready = is_ready; self.pending_stage = Some(pending_stage); Ok(()) diff --git a/dan_layer/storage/src/state_store/mod.rs b/dan_layer/storage/src/state_store/mod.rs index b700b0621..98c0a4a6f 100644 --- a/dan_layer/storage/src/state_store/mod.rs +++ b/dan_layer/storage/src/state_store/mod.rs @@ -11,10 +11,19 @@ use std::{ use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use tari_common_types::types::{FixedHash, PublicKey}; -use tari_dan_common_types::{shard::Shard, Epoch, NodeAddressable, NodeHeight, ShardGroup, SubstateAddress}; +use tari_dan_common_types::{ + shard::Shard, + Epoch, + NodeAddressable, + NodeHeight, + ShardGroup, + SubstateAddress, + SubstateRequirement, + VersionedSubstateId, +}; use tari_engine_types::substate::SubstateId; use tari_state_tree::{Node, NodeKey, StaleTreeNode, Version}; -use tari_transaction::{SubstateRequirement, TransactionId, VersionedSubstateId}; +use tari_transaction::TransactionId; #[cfg(feature = "ts")] use ts_rs::TS; @@ -106,7 +115,7 @@ pub trait StateStoreReadTransaction: Sized { fn last_proposed_get(&self) -> Result; fn locked_block_get(&self) -> Result; fn leaf_block_get(&self) -> Result; - fn high_qc_get(&self) -> Result; + fn high_qc_get(&self, epoch: Epoch) -> Result; fn foreign_proposals_get_any<'a, I: IntoIterator>( &self, block_ids: I, diff --git a/dan_layer/template_test_tooling/src/template_test.rs b/dan_layer/template_test_tooling/src/template_test.rs index 3c485dd8e..390ac008b 100644 --- a/dan_layer/template_test_tooling/src/template_test.rs +++ b/dan_layer/template_test_tooling/src/template_test.rs @@ -18,7 +18,7 @@ use tari_crypto::{ ristretto::{RistrettoPublicKey, RistrettoSecretKey}, tari_utilities::{hex::Hex, ByteArray}, }; -use tari_dan_common_types::crypto::create_key_pair_from_seed; +use tari_dan_common_types::{crypto::create_key_pair_from_seed, VersionedSubstateId}; use tari_dan_engine::{ fees::{FeeModule, FeeTable}, runtime::{AuthParams, RuntimeModule}, @@ -53,7 +53,7 @@ use tari_template_lib::{ prelude::{ComponentAccessRules, CONFIDENTIAL_TARI_RESOURCE_ADDRESS}, Hash, }; -use tari_transaction::{Transaction, VersionedSubstateId}; +use tari_transaction::Transaction; use tari_transaction_manifest::{parse_manifest, ManifestValue}; use crate::{read_only_state_store::ReadOnlyStateStore, track_calls::TrackCallsModule, Package}; @@ -594,7 +594,7 @@ impl TemplateTest { .name_to_template .iter() // Account is implicitly imported. - .filter(|(name, _)|* name != "Account") + .filter(|(name, _)| *name != "Account") .map(|(name, addr)| format!("use template_{} as {};", addr, name)) .collect::>() .join("\n"); diff --git a/dan_layer/transaction/Cargo.toml b/dan_layer/transaction/Cargo.toml index cbe1354ec..e7876dcfa 100644 --- a/dan_layer/transaction/Cargo.toml +++ b/dan_layer/transaction/Cargo.toml @@ -17,7 +17,6 @@ tari_template_lib = { workspace = true } rand = { workspace = true } indexmap = { workspace = true, features = ["serde"] } serde = { workspace = true, default-features = true } -thiserror = { workspace = true } ts-rs = { workspace = true, optional = true } [features] diff --git a/dan_layer/transaction/src/builder.rs b/dan_layer/transaction/src/builder.rs index 2068c7973..b61e30e34 100644 --- a/dan_layer/transaction/src/builder.rs +++ b/dan_layer/transaction/src/builder.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause use tari_common_types::types::{PrivateKey, PublicKey}; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_engine_types::{confidential::ConfidentialClaim, instruction::Instruction, TemplateAddress}; use tari_template_lib::{ args, @@ -10,7 +10,7 @@ use tari_template_lib::{ models::{Amount, ComponentAddress, ConfidentialWithdrawProof, ResourceAddress}, }; -use crate::{unsigned_transaction::UnsignedTransaction, SubstateRequirement, Transaction, TransactionSignature}; +use crate::{unsigned_transaction::UnsignedTransaction, Transaction, TransactionSignature}; #[derive(Debug, Clone, Default)] pub struct TransactionBuilder { diff --git a/dan_layer/transaction/src/lib.rs b/dan_layer/transaction/src/lib.rs index 4fb8d7025..69b577137 100644 --- a/dan_layer/transaction/src/lib.rs +++ b/dan_layer/transaction/src/lib.rs @@ -22,14 +22,12 @@ mod builder; mod signature; -mod substate; mod transaction; mod transaction_id; mod unsigned_transaction; pub use builder::TransactionBuilder; pub use signature::TransactionSignature; -pub use substate::*; pub use tari_engine_types::instruction::Instruction; pub use transaction::*; pub use transaction_id::*; diff --git a/dan_layer/transaction/src/signature.rs b/dan_layer/transaction/src/signature.rs index 7c37327ec..1f1839662 100644 --- a/dan_layer/transaction/src/signature.rs +++ b/dan_layer/transaction/src/signature.rs @@ -9,7 +9,7 @@ use tari_crypto::{ keys::PublicKey as PublicKeyT, ristretto::{RistrettoPublicKey, RistrettoSecretKey}, }; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_engine_types::{ hashing::{hasher64, EngineHashDomainLabel}, instruction::Instruction, @@ -17,7 +17,7 @@ use tari_engine_types::{ #[cfg(feature = "ts")] use ts_rs::TS; -use crate::{unsigned_transaction::UnsignedTransaction, SubstateRequirement}; +use crate::unsigned_transaction::UnsignedTransaction; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] #[cfg_attr(feature = "ts", derive(TS), ts(export, export_to = "../../bindings/src/types/"))] diff --git a/dan_layer/transaction/src/transaction.rs b/dan_layer/transaction/src/transaction.rs index 0fb46750a..a674530df 100644 --- a/dan_layer/transaction/src/transaction.rs +++ b/dan_layer/transaction/src/transaction.rs @@ -7,7 +7,7 @@ use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use tari_common_types::types::PublicKey; use tari_crypto::ristretto::RistrettoSecretKey; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement, VersionedSubstateId}; use tari_engine_types::{ hashing::{hasher32, EngineHashDomainLabel}, indexed_value::{IndexedValue, IndexedValueError}, @@ -16,14 +16,7 @@ use tari_engine_types::{ }; use tari_template_lib::{models::ComponentAddress, Hash}; -use crate::{ - builder::TransactionBuilder, - transaction_id::TransactionId, - SubstateRequirement, - TransactionSignature, - UnsignedTransaction, - VersionedSubstateId, -}; +use crate::{builder::TransactionBuilder, transaction_id::TransactionId, TransactionSignature, UnsignedTransaction}; #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr( diff --git a/dan_layer/transaction/src/unsigned_transaction.rs b/dan_layer/transaction/src/unsigned_transaction.rs index ae55fe113..0241583e1 100644 --- a/dan_layer/transaction/src/unsigned_transaction.rs +++ b/dan_layer/transaction/src/unsigned_transaction.rs @@ -6,7 +6,7 @@ use std::collections::HashSet; use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use tari_crypto::ristretto::RistrettoSecretKey; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_engine_types::{ indexed_value::{IndexedValue, IndexedValueError}, instruction::Instruction, @@ -14,7 +14,7 @@ use tari_engine_types::{ }; use tari_template_lib::models::ComponentAddress; -use crate::{builder::TransactionBuilder, SubstateRequirement, Transaction, TransactionSignature}; +use crate::{builder::TransactionBuilder, Transaction, TransactionSignature}; #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[cfg_attr( diff --git a/dan_layer/wallet/sdk/src/apis/substate.rs b/dan_layer/wallet/sdk/src/apis/substate.rs index 509c6d942..6658342b9 100644 --- a/dan_layer/wallet/sdk/src/apis/substate.rs +++ b/dan_layer/wallet/sdk/src/apis/substate.rs @@ -7,6 +7,7 @@ use log::*; use tari_dan_common_types::{ optional::{IsNotFoundError, Optional}, substate_type::SubstateType, + SubstateRequirement, }; use tari_engine_types::{ indexed_value::{IndexedValueError, IndexedWellKnownTypes}, @@ -14,7 +15,7 @@ use tari_engine_types::{ transaction_receipt::TransactionReceiptAddress, TemplateAddress, }; -use tari_transaction::{SubstateRequirement, TransactionId}; +use tari_transaction::TransactionId; use crate::{ models::{SubstateModel, VersionedSubstateId}, diff --git a/dan_layer/wallet/sdk/src/apis/transaction.rs b/dan_layer/wallet/sdk/src/apis/transaction.rs index 8f5fc3ab3..a93319c8b 100644 --- a/dan_layer/wallet/sdk/src/apis/transaction.rs +++ b/dan_layer/wallet/sdk/src/apis/transaction.rs @@ -4,13 +4,16 @@ use std::collections::HashMap; use log::*; -use tari_dan_common_types::optional::{IsNotFoundError, Optional}; +use tari_dan_common_types::{ + optional::{IsNotFoundError, Optional}, + SubstateRequirement, +}; use tari_engine_types::{ indexed_value::{IndexedValueError, IndexedWellKnownTypes}, substate::SubstateDiff, }; use tari_template_lib::prelude::ComponentAddress; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use crate::{ models::{NewAccountInfo, TransactionStatus, VersionedSubstateId, WalletTransaction}, diff --git a/dan_layer/wallet/sdk/src/models/substate.rs b/dan_layer/wallet/sdk/src/models/substate.rs index b9fa54a91..b41e0db8e 100644 --- a/dan_layer/wallet/sdk/src/models/substate.rs +++ b/dan_layer/wallet/sdk/src/models/substate.rs @@ -5,8 +5,8 @@ use std::{fmt::Display, str::FromStr}; use serde::{Deserialize, Serialize}; use tari_common_types::types::FixedHash; +use tari_dan_common_types::SubstateRequirement; use tari_engine_types::{serde_with, substate::SubstateId, TemplateAddress}; -use tari_transaction::SubstateRequirement; #[derive(Debug, Clone)] pub struct SubstateModel { diff --git a/dan_layer/wallet/sdk/src/models/wallet_transaction.rs b/dan_layer/wallet/sdk/src/models/wallet_transaction.rs index a3dea83f8..216f37e4c 100644 --- a/dan_layer/wallet/sdk/src/models/wallet_transaction.rs +++ b/dan_layer/wallet/sdk/src/models/wallet_transaction.rs @@ -6,10 +6,11 @@ use std::{fmt::Display, str::FromStr, time::Duration}; use anyhow::anyhow; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; +use tari_dan_common_types::SubstateRequirement; use tari_dan_storage::consensus_models::QuorumCertificate; use tari_engine_types::commit_result::FinalizeResult; use tari_template_lib::models::Amount; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; #[cfg(feature = "ts")] use ts_rs::TS; diff --git a/dan_layer/wallet/sdk/src/network.rs b/dan_layer/wallet/sdk/src/network.rs index fe729b73d..30316ddf3 100644 --- a/dan_layer/wallet/sdk/src/network.rs +++ b/dan_layer/wallet/sdk/src/network.rs @@ -6,7 +6,7 @@ use std::time::Duration; use async_trait::async_trait; use serde::{Deserialize, Serialize}; use serde_json::Value; -use tari_dan_common_types::substate_type::SubstateType; +use tari_dan_common_types::{substate_type::SubstateType, SubstateRequirement}; use tari_dan_storage::consensus_models::Decision; use tari_engine_types::{ commit_result::ExecuteResult, @@ -14,7 +14,7 @@ use tari_engine_types::{ }; use tari_template_abi::TemplateDef; use tari_template_lib::prelude::TemplateAddress; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; #[async_trait] pub trait WalletNetworkInterface { diff --git a/dan_layer/wallet/sdk/src/storage.rs b/dan_layer/wallet/sdk/src/storage.rs index eeafde31d..06f7d7e41 100644 --- a/dan_layer/wallet/sdk/src/storage.rs +++ b/dan_layer/wallet/sdk/src/storage.rs @@ -7,14 +7,14 @@ use std::{ }; use tari_common_types::types::Commitment; -use tari_dan_common_types::{optional::IsNotFoundError, substate_type::SubstateType}; +use tari_dan_common_types::{optional::IsNotFoundError, substate_type::SubstateType, SubstateRequirement}; use tari_dan_storage::consensus_models::QuorumCertificate; use tari_engine_types::{commit_result::FinalizeResult, substate::SubstateId, TemplateAddress}; use tari_template_lib::{ models::Amount, prelude::{ComponentAddress, NonFungibleId, ResourceAddress}, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use crate::models::{ Account, diff --git a/dan_layer/wallet/sdk/tests/confidential_output_api.rs b/dan_layer/wallet/sdk/tests/confidential_output_api.rs index e4d303b54..1b2d13b14 100644 --- a/dan_layer/wallet/sdk/tests/confidential_output_api.rs +++ b/dan_layer/wallet/sdk/tests/confidential_output_api.rs @@ -6,7 +6,7 @@ use std::{convert::Infallible, time::Duration}; use async_trait::async_trait; use tari_common_types::types::Commitment; use tari_crypto::commitment::HomomorphicCommitmentFactory; -use tari_dan_common_types::optional::Optional; +use tari_dan_common_types::{optional::Optional, SubstateRequirement}; use tari_dan_wallet_sdk::{ models::{ConfidentialOutputModel, ConfidentialProofId, OutputStatus}, network::{SubstateQueryResult, TransactionQueryResult, WalletNetworkInterface}, @@ -22,7 +22,7 @@ use tari_template_lib::{ models::{Amount, EncryptedData, TemplateAddress}, resource::ResourceType, }; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; #[test] fn outputs_locked_and_released() { diff --git a/dan_layer/wallet/storage_sqlite/src/writer.rs b/dan_layer/wallet/storage_sqlite/src/writer.rs index 7e0e8b66d..6376fd01f 100644 --- a/dan_layer/wallet/storage_sqlite/src/writer.rs +++ b/dan_layer/wallet/storage_sqlite/src/writer.rs @@ -14,6 +14,7 @@ use log::*; use serde::Serialize; use tari_bor::json_encoding::CborValueJsonSerializeWrapper; use tari_common_types::types::{Commitment, PublicKey}; +use tari_dan_common_types::SubstateRequirement; use tari_dan_storage::consensus_models::QuorumCertificate; use tari_dan_wallet_sdk::{ models::{ @@ -31,7 +32,7 @@ use tari_dan_wallet_sdk::{ }; use tari_engine_types::{commit_result::FinalizeResult, substate::SubstateId, TemplateAddress}; use tari_template_lib::models::{Amount, EncryptedData}; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tari_utilities::hex::Hex; use crate::{ diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 3e4a577e4..cf96cfd75 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -46,7 +46,7 @@ use tari_crypto::{ keys::{PublicKey as _, SecretKey}, ristretto::{RistrettoComSig, RistrettoSecretKey}, }; -use tari_transaction::SubstateRequirement; +use tari_dan_common_types::SubstateRequirement; use template::RegisteredTemplate; use validator_node::ValidatorNodeProcess; use wallet::WalletProcess; diff --git a/integration_tests/src/validator_node_cli.rs b/integration_tests/src/validator_node_cli.rs index 1d749ce81..68f6074ac 100644 --- a/integration_tests/src/validator_node_cli.rs +++ b/integration_tests/src/validator_node_cli.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr}; +use tari_dan_common_types::SubstateRequirement; use tari_engine_types::{ commit_result::RejectReason, instruction::Instruction, @@ -10,7 +11,6 @@ use tari_engine_types::{ }; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::args; -use tari_transaction::SubstateRequirement; use tari_transaction_manifest::{parse_manifest, ManifestValue}; use tari_validator_node_cli::{ command::transaction::{handle_submit, submit_transaction, CliArg, CliInstruction, CommonSubmitArgs, SubmitArgs}, diff --git a/integration_tests/src/wallet_daemon_cli.rs b/integration_tests/src/wallet_daemon_cli.rs index 0f50cb0bf..f272906b9 100644 --- a/integration_tests/src/wallet_daemon_cli.rs +++ b/integration_tests/src/wallet_daemon_cli.rs @@ -29,7 +29,7 @@ use tari_crypto::{ signatures::CommitmentSignature, tari_utilities::ByteArray, }; -use tari_dan_common_types::Epoch; +use tari_dan_common_types::{Epoch, SubstateRequirement}; use tari_dan_wallet_sdk::apis::confidential_transfer::ConfidentialTransferInputSelection; use tari_engine_types::instruction::Instruction; use tari_template_lib::{ @@ -39,7 +39,7 @@ use tari_template_lib::{ prelude::{ComponentAddress, ResourceAddress}, resource::TOKEN_SYMBOL, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; use tari_transaction_manifest::{parse_manifest, ManifestValue}; use tari_validator_node_cli::command::transaction::CliArg; use tari_wallet_daemon_client::{ diff --git a/utilities/tariswap_test_bench/Cargo.toml b/utilities/tariswap_test_bench/Cargo.toml index 57be23557..fac5d467a 100644 --- a/utilities/tariswap_test_bench/Cargo.toml +++ b/utilities/tariswap_test_bench/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true tari_crypto = { workspace = true } tari_dan_wallet_daemon = { workspace = true } tari_dan_wallet_sdk = { workspace = true } +tari_dan_common_types = { workspace = true } tari_dan_wallet_storage_sqlite = { workspace = true } tari_engine_types = { workspace = true } tari_template_builtin = { workspace = true } diff --git a/utilities/tariswap_test_bench/src/accounts.rs b/utilities/tariswap_test_bench/src/accounts.rs index 2da601092..d75415ede 100644 --- a/utilities/tariswap_test_bench/src/accounts.rs +++ b/utilities/tariswap_test_bench/src/accounts.rs @@ -4,6 +4,7 @@ use std::ops::RangeInclusive; use tari_crypto::{keys::PublicKey as _, ristretto::RistrettoPublicKey}; +use tari_dan_common_types::SubstateRequirement; use tari_dan_wallet_sdk::{apis::key_manager::TRANSACTION_BRANCH, models::Account}; use tari_engine_types::component::new_component_address_from_public_key; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; @@ -13,7 +14,7 @@ use tari_template_lib::{ models::Amount, resource::ResourceType, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; use crate::{faucet::Faucet, runner::Runner}; diff --git a/utilities/tariswap_test_bench/src/runner.rs b/utilities/tariswap_test_bench/src/runner.rs index c9301b54c..3a4ceffe0 100644 --- a/utilities/tariswap_test_bench/src/runner.rs +++ b/utilities/tariswap_test_bench/src/runner.rs @@ -4,11 +4,12 @@ use std::{path::Path, time::Duration}; use log::info; +use tari_dan_common_types::SubstateRequirement; use tari_dan_wallet_daemon::indexer_jrpc_impl::IndexerJsonRpcNetworkInterface; use tari_dan_wallet_sdk::{DanWalletSdk, WalletSdkConfig}; use tari_dan_wallet_storage_sqlite::SqliteWalletStore; use tari_engine_types::commit_result::FinalizeResult; -use tari_transaction::{SubstateRequirement, Transaction, TransactionId}; +use tari_transaction::{Transaction, TransactionId}; use tari_validator_node_client::types::TemplateMetadata; use tokio::time; use url::Url; diff --git a/utilities/tariswap_test_bench/src/tariswap.rs b/utilities/tariswap_test_bench/src/tariswap.rs index 5d1f7233c..5a657ba5c 100644 --- a/utilities/tariswap_test_bench/src/tariswap.rs +++ b/utilities/tariswap_test_bench/src/tariswap.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: BSD-3-Clause use log::info; +use tari_dan_common_types::SubstateRequirement; use tari_dan_wallet_sdk::{apis::key_manager::TRANSACTION_BRANCH, models::Account}; use tari_template_lib::{ args, models::{Amount, ComponentAddress}, prelude::XTR, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; use crate::{faucet::Faucet, runner::Runner}; diff --git a/utilities/transaction_generator/Cargo.toml b/utilities/transaction_generator/Cargo.toml index d06bf633e..4cef274dc 100644 --- a/utilities/transaction_generator/Cargo.toml +++ b/utilities/transaction_generator/Cargo.toml @@ -12,6 +12,7 @@ tari_transaction = { workspace = true } tari_engine_types = { workspace = true } tari_template_builtin = { workspace = true } tari_transaction_manifest = { workspace = true } +tari_dan_common_types = { workspace = true } tari_crypto = { workspace = true } anyhow = { workspace = true } diff --git a/utilities/transaction_generator/src/transaction_builders/free_coins.rs b/utilities/transaction_generator/src/transaction_builders/free_coins.rs index 31cd32a56..309e38586 100644 --- a/utilities/transaction_generator/src/transaction_builders/free_coins.rs +++ b/utilities/transaction_generator/src/transaction_builders/free_coins.rs @@ -3,6 +3,7 @@ use rand::rngs::OsRng; use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; +use tari_dan_common_types::SubstateRequirement; use tari_engine_types::{component::new_component_address_from_public_key, instruction::Instruction}; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::{ @@ -10,7 +11,7 @@ use tari_template_lib::{ constants::{XTR_FAUCET_COMPONENT_ADDRESS, XTR_FAUCET_VAULT_ADDRESS}, models::Amount, }; -use tari_transaction::{SubstateRequirement, Transaction}; +use tari_transaction::Transaction; pub fn builder(_: u64) -> Transaction { let (signer_secret_key, signer_public_key) = RistrettoPublicKey::random_keypair(&mut OsRng); diff --git a/utilities/transaction_generator/src/transaction_writer.rs b/utilities/transaction_generator/src/transaction_writer.rs index c30870299..4120228c0 100644 --- a/utilities/transaction_generator/src/transaction_writer.rs +++ b/utilities/transaction_generator/src/transaction_writer.rs @@ -7,7 +7,8 @@ use bytes::{BufMut, Bytes, BytesMut}; use indexmap::IndexSet; use rayon::iter::{ParallelBridge, ParallelIterator}; use serde::{Deserialize, Serialize}; -use tari_transaction::{Transaction, TransactionSignature, UnsignedTransaction, VersionedSubstateId}; +use tari_dan_common_types::VersionedSubstateId; +use tari_transaction::{Transaction, TransactionSignature, UnsignedTransaction}; use crate::BoxedTransactionBuilder; diff --git a/utilities/transaction_submitter/src/cli.rs b/utilities/transaction_submitter/src/cli.rs index abdc5364c..67c3dea2b 100644 --- a/utilities/transaction_submitter/src/cli.rs +++ b/utilities/transaction_submitter/src/cli.rs @@ -31,7 +31,7 @@ pub struct StressTestArgs { #[clap(long, alias = "skip", short = 'k')] pub skip_transactions: Option, #[clap(long, short = 'a')] - pub jrpc_address: Vec, + pub jrpc_addresses: Vec, #[clap(long, short = 'f')] pub transaction_file: PathBuf, #[clap(long, short = 'y')] diff --git a/utilities/transaction_submitter/src/main.rs b/utilities/transaction_submitter/src/main.rs index c8a97889f..399e18386 100644 --- a/utilities/transaction_submitter/src/main.rs +++ b/utilities/transaction_submitter/src/main.rs @@ -40,11 +40,11 @@ async fn main() -> anyhow::Result<()> { } async fn stress_test(args: StressTestArgs) -> anyhow::Result> { - if args.jrpc_address.is_empty() { + if args.jrpc_addresses.is_empty() { bail!("No validator nodes specified"); } - let mut clients = Vec::with_capacity(args.jrpc_address.len()); - for address in args.jrpc_address { + let mut clients = Vec::with_capacity(args.jrpc_addresses.len()); + for address in args.jrpc_addresses { let mut client = ValidatorNodeClient::connect(format!("http://{}/json_rpc", address))?; if let Err(e) = client.get_identity().await { bail!("Failed to connect to {}: {}", address, e); @@ -139,7 +139,6 @@ async fn fetch_result_summary( mut submitted_rx: mpsc::UnboundedReceiver, ) -> StressTestResultSummary { let bounded_spawn = BoundedSpawn::new(clients.len()); - let mut count = 0; let (results_tx, mut results_rx) = mpsc::channel::(10); // Result collector @@ -177,11 +176,15 @@ async fn fetch_result_summary( // Result emitter while let Some(transaction_id) = submitted_rx.recv().await { - let mut client = clients[count % clients.len()].clone(); + let mut clients = clients.clone(); + let num_clients = clients.len(); let results_tx = results_tx.clone(); bounded_spawn .spawn(async move { + let mut i = 0usize; loop { + let client = &mut clients[i % num_clients]; + i += 1; match client .get_transaction_result(GetTransactionResultRequest { transaction_id }) .await @@ -215,8 +218,9 @@ async fn fetch_result_summary( }, Ok(None) => { println!( - "Transaction result not found: {}. This is likely due to a race condition. Retrying \ - later...", + "[{}] Result not found for transaction {}. This is likely due to a race condition or \ + requesting the result from a non-involved validator (multi-shard). Retrying later...", + client.endpoint(), transaction_id ); sleep(Duration::from_secs(1)).await; @@ -239,8 +243,6 @@ async fn fetch_result_summary( } }) .await; - - count += 1; } // Drop the remaining sender handle so that the result collector ends when all results have been received