From d983b365a23de61bf31c759b6d119c0e3f0ae21d Mon Sep 17 00:00:00 2001 From: Miguel Naveira <47919901+mrnaveira@users.noreply.github.com> Date: Tue, 10 Sep 2024 05:23:56 +0100 Subject: [PATCH 1/6] feat(consensus)!: add sidechain id in the genesis block (#1135) Description --- * Created the definition of a `ExtraData` (following Bitcoin and Ethereum naming) type for storing additional information inside blocks: * It contains a key-value map of arbitrary binary data * For now only a key exists for sidechain ids, but it can be extended in the future * The binary value max size is limited by definition via `MaxSizeBytes` (copied from the minotari codebase) * Updated the definition for `Block`, `ParkedBlock` and `ForeignProposal` to include an optional `extra_data` field * Updated the SQLite storage for the new field * Updated the protobuf definitions with the new field * Updated the typescript bindings * Genesis blocks now encode the sidechain id ( if present in the config) in the new `extra_data` field * New `check_sidechain_id` validation in consensus block validation, to check the sidechain id in the extra data of genesis blocks Motivation and Context --- We should have the sidechain id in the genesis block for the shard and epoch. Including it in all blocks would be wasteful, so only genesis blocks should include it. To make it more general for the future, we want to create an optional field in blocks to store any type of data, and use it to store the sidechain id. How Has This Been Tested? --- Manually with `tari_swarm`: * Setting up a `sidechain_deployment_key` in the `minotari_wallet` * Settiup up a `validator_node_sidechain_id` in `validator_node` * Setting up `sidechain_id` in the `indexer` * Performing transactions to advance the network and inspecting the blocks in the database Also tested with no sidechain What process can a PR reviewer use to test or verify this change? --- See previous section Breaking Changes --- - [ ] None - [ ] Requires data directory to be deleted - [x] Other - Network reset as the block definition and validation has changed --- .../tari_indexer/src/event_scanner.rs | 25 ++-- applications/tari_indexer/src/lib.rs | 1 + .../tari_validator_node/src/bootstrap.rs | 56 ++++++- .../tari_validator_node/src/consensus/mod.rs | 3 + bindings/dist/index.d.ts | 39 ++--- bindings/dist/index.js | 39 ++--- bindings/dist/tari-indexer-client.d.ts | 50 +++---- bindings/dist/tari-indexer-client.js | 50 +++---- bindings/dist/types/Block.d.ts | 2 + bindings/dist/types/ExtraData.d.ts | 1 + bindings/dist/types/ExtraData.js | 2 + bindings/dist/validator-node-client.d.ts | 88 +++++------ bindings/dist/validator-node-client.js | 88 +++++------ bindings/dist/wallet-daemon-client.d.ts | 140 +++++++++--------- bindings/dist/wallet-daemon-client.js | 140 +++++++++--------- bindings/src/index.ts | 39 ++--- bindings/src/tari-indexer-client.ts | 50 +++---- bindings/src/types/Block.ts | 2 + bindings/src/types/ExtraData.ts | 3 + bindings/src/validator-node-client.ts | 88 +++++------ bindings/src/wallet-daemon-client.ts | 140 +++++++++--------- dan_layer/common_types/src/bytes.rs | 94 ++++++++++++ dan_layer/common_types/src/extra_data.rs | 69 +++++++++ dan_layer/common_types/src/lib.rs | 6 + dan_layer/consensus/src/block_validations.rs | 47 +++++- dan_layer/consensus/src/hotstuff/config.rs | 2 + dan_layer/consensus/src/hotstuff/error.rs | 18 ++- .../consensus/src/hotstuff/on_propose.rs | 1 + .../src/hotstuff/on_receive_local_proposal.rs | 7 +- dan_layer/consensus/src/hotstuff/worker.rs | 13 +- .../src/support/validator/builder.rs | 1 + dan_layer/p2p/proto/consensus.proto | 5 + dan_layer/p2p/src/conversions/consensus.rs | 23 +++ .../up.sql | 3 + dan_layer/state_store_sqlite/src/schema.rs | 3 + .../src/sql_models/block.rs | 4 + .../src/sql_models/foreign_proposal.rs | 2 + dan_layer/state_store_sqlite/src/writer.rs | 3 + dan_layer/state_store_sqlite/tests/tests.rs | 3 +- .../storage/src/consensus_models/block.rs | 51 ++++++- dan_layer/storage/src/error.rs | 4 + 41 files changed, 902 insertions(+), 503 deletions(-) create mode 100644 bindings/dist/types/ExtraData.d.ts create mode 100644 bindings/dist/types/ExtraData.js create mode 100644 bindings/src/types/ExtraData.ts create mode 100644 dan_layer/common_types/src/bytes.rs create mode 100644 dan_layer/common_types/src/extra_data.rs diff --git a/applications/tari_indexer/src/event_scanner.rs b/applications/tari_indexer/src/event_scanner.rs index 20da8e6e9e..26556435ea 100644 --- a/applications/tari_indexer/src/event_scanner.rs +++ b/applications/tari_indexer/src/event_scanner.rs @@ -26,11 +26,11 @@ use futures::StreamExt; use log::*; use tari_bor::decode; use tari_common::configuration::Network; -use tari_crypto::tari_utilities::message_format::MessageFormat; +use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::message_format::MessageFormat}; use tari_dan_app_utilities::consensus_constants::ConsensusConstants; use tari_dan_common_types::{committee::Committee, Epoch, NumPreshards, PeerAddress, ShardGroup}; use tari_dan_p2p::proto::rpc::{GetTransactionResultRequest, PayloadResultStatus, SyncBlocksRequest}; -use tari_dan_storage::consensus_models::{Block, BlockId, Decision, TransactionRecord}; +use tari_dan_storage::consensus_models::{Block, BlockError, BlockId, Decision, TransactionRecord}; use tari_engine_types::{ commit_result::{ExecuteResult, TransactionResult}, events::Event, @@ -96,6 +96,7 @@ struct TransactionMetadata { pub struct EventScanner { network: Network, + sidechain_id: Option, epoch_manager: Box>, client_factory: TariValidatorNodeRpcClientFactory, substate_store: SqliteSubstateStore, @@ -105,6 +106,7 @@ pub struct EventScanner { impl EventScanner { pub fn new( network: Network, + sidechain_id: Option, epoch_manager: Box>, client_factory: TariValidatorNodeRpcClientFactory, substate_store: SqliteSubstateStore, @@ -112,6 +114,7 @@ impl EventScanner { ) -> Self { Self { network, + sidechain_id, epoch_manager, client_factory, substate_store, @@ -447,10 +450,10 @@ impl EventScanner { .collect() } - fn build_genesis_block_id(&self, num_preshards: NumPreshards) -> BlockId { + fn build_genesis_block_id(&self, num_preshards: NumPreshards) -> Result { // TODO: this should return the actual genesis for the shard group and epoch - let start_block = Block::zero_block(self.network, num_preshards); - *start_block.id() + let start_block = Block::zero_block(self.network, num_preshards, self.sidechain_id.clone())?; + Ok(*start_block.id()) } async fn get_oldest_scanned_epoch(&self) -> Result, anyhow::Error> { @@ -470,10 +473,14 @@ impl EventScanner { let start_block_id = self .substate_store .with_read_tx(|tx| tx.get_last_scanned_block_id(epoch, shard_group))?; - let start_block_id = start_block_id.unwrap_or_else(|| { - let consensus_constants = ConsensusConstants::from(self.network); - self.build_genesis_block_id(consensus_constants.num_preshards) - }); + + let start_block_id = match start_block_id { + Some(block_id) => block_id, + None => { + let consensus_constants = ConsensusConstants::from(self.network); + self.build_genesis_block_id(consensus_constants.num_preshards)? + }, + }; committee.shuffle(); let mut last_block_id = start_block_id; diff --git a/applications/tari_indexer/src/lib.rs b/applications/tari_indexer/src/lib.rs index d45c1a0206..6b3aab8180 100644 --- a/applications/tari_indexer/src/lib.rs +++ b/applications/tari_indexer/src/lib.rs @@ -181,6 +181,7 @@ pub async fn run_indexer(config: ApplicationConfig, mut shutdown_signal: Shutdow .map_err(|e| ExitError::new(ExitCode::ConfigError, format!("Invalid event filters: {}", e)))?; let event_scanner = Arc::new(EventScanner::new( config.network, + config.indexer.sidechain_id, Box::new(services.epoch_manager.clone()), services.validator_node_client_factory.clone(), services.substate_store.clone(), diff --git a/applications/tari_validator_node/src/bootstrap.rs b/applications/tari_validator_node/src/bootstrap.rs index 5e0ad8ffcc..63d95cbfa5 100644 --- a/applications/tari_validator_node/src/bootstrap.rs +++ b/applications/tari_validator_node/src/bootstrap.rs @@ -38,7 +38,7 @@ use tari_common::{ #[cfg(not(feature = "metrics"))] use tari_consensus::traits::hooks::NoopHooks; use tari_core::transactions::transaction_components::ValidatorNodeSignature; -use tari_crypto::tari_utilities::ByteArray; +use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::ByteArray}; use tari_dan_app_utilities::{ base_layer_scanner, consensus_constants::ConsensusConstants, @@ -191,7 +191,15 @@ pub async fn spawn_services( // Connect to shard db let state_store = SqliteStateStore::connect(&format!("sqlite://{}", config.validator_node.state_db_path().display()))?; - state_store.with_write_tx(|tx| bootstrap_state(tx, config.network, consensus_constants.num_preshards))?; + let sidechain_id = config.validator_node.validator_node_sidechain_id.clone(); + state_store.with_write_tx(|tx| { + bootstrap_state( + tx, + config.network, + consensus_constants.num_preshards, + sidechain_id.clone(), + ) + })?; info!(target: LOG_TARGET, "Epoch manager initializing"); let epoch_manager_config = EpochManagerConfig { @@ -265,6 +273,7 @@ pub async fn spawn_services( let signing_service = consensus::TariSignatureService::new(keypair.clone()); let (consensus_join_handle, consensus_handle) = consensus::spawn( config.network, + sidechain_id, state_store.clone(), local_address, signing_service, @@ -450,7 +459,12 @@ async fn spawn_p2p_rpc( Ok(()) } -fn bootstrap_state(tx: &mut TTx, network: Network, num_preshards: NumPreshards) -> Result<(), StorageError> +fn bootstrap_state( + tx: &mut TTx, + network: Network, + num_preshards: NumPreshards, + sidechain_id: Option, +) -> Result<(), StorageError> where TTx: StateStoreWriteTransaction + Deref, TTx::Target: StateStoreReadTransaction, @@ -473,7 +487,14 @@ where None, None, ); - create_substate(tx, network, num_preshards, PUBLIC_IDENTITY_RESOURCE_ADDRESS, value)?; + create_substate( + tx, + network, + num_preshards, + &sidechain_id, + PUBLIC_IDENTITY_RESOURCE_ADDRESS, + value, + )?; let mut xtr_resource = Resource::new( ResourceType::Confidential, @@ -498,7 +519,14 @@ where state: cbor!({"vault" => XTR_FAUCET_VAULT_ADDRESS}).unwrap(), }, }; - create_substate(tx, network, num_preshards, XTR_FAUCET_COMPONENT_ADDRESS, value)?; + create_substate( + tx, + network, + num_preshards, + &sidechain_id, + XTR_FAUCET_COMPONENT_ADDRESS, + value, + )?; xtr_resource.increase_total_supply(Amount::MAX); let value = Vault::new(ResourceContainer::Confidential { @@ -509,13 +537,21 @@ where locked_revealed_amount: Default::default(), }); - create_substate(tx, network, num_preshards, XTR_FAUCET_VAULT_ADDRESS, value)?; + create_substate( + tx, + network, + num_preshards, + &sidechain_id, + XTR_FAUCET_VAULT_ADDRESS, + value, + )?; } create_substate( tx, network, num_preshards, + &sidechain_id, CONFIDENTIAL_TARI_RESOURCE_ADDRESS, xtr_resource, )?; @@ -527,6 +563,7 @@ fn create_substate( tx: &mut TTx, network: Network, num_preshards: NumPreshards, + sidechain_id: &Option, substate_id: TId, value: TVal, ) -> Result<(), StorageError> @@ -537,7 +574,12 @@ where TId: Into, TVal: Into, { - let genesis_block = Block::genesis(network, Epoch(0), ShardGroup::all_shards(num_preshards)); + let genesis_block = Block::genesis( + network, + Epoch(0), + ShardGroup::all_shards(num_preshards), + sidechain_id.clone(), + )?; let substate_id = substate_id.into(); let id = VersionedSubstateId::new(substate_id, 0); SubstateRecord { diff --git a/applications/tari_validator_node/src/consensus/mod.rs b/applications/tari_validator_node/src/consensus/mod.rs index 0576672af6..84733ff78e 100644 --- a/applications/tari_validator_node/src/consensus/mod.rs +++ b/applications/tari_validator_node/src/consensus/mod.rs @@ -7,6 +7,7 @@ use tari_consensus::{ hotstuff::{ConsensusWorker, ConsensusWorkerContext, HotstuffConfig, HotstuffWorker}, traits::ConsensusSpec, }; +use tari_crypto::ristretto::RistrettoPublicKey; use tari_dan_app_utilities::{ consensus_constants::ConsensusConstants, template_manager::implementation::TemplateManager, @@ -60,6 +61,7 @@ pub type ConsensusTransactionValidator = BoxedValidator, store: SqliteStateStore, local_addr: PeerAddress, signing_service: TariSignatureService, @@ -83,6 +85,7 @@ pub async fn spawn( let hs_config = HotstuffConfig { network, + sidechain_id, max_base_layer_blocks_behind: consensus_constants.max_base_layer_blocks_behind, max_base_layer_blocks_ahead: consensus_constants.max_base_layer_blocks_ahead, num_preshards: consensus_constants.num_preshards, diff --git a/bindings/dist/index.d.ts b/bindings/dist/index.d.ts index 2bdfef775b..6e3b58bc25 100644 --- a/bindings/dist/index.d.ts +++ b/bindings/dist/index.d.ts @@ -1,24 +1,24 @@ export * from "./types/AccessRule"; export * from "./types/Account"; export * from "./types/Amount"; -export * from "./types/ArgDef"; export * from "./types/Arg"; +export * from "./types/ArgDef"; export * from "./types/AuthHook"; export * from "./types/Block"; export * from "./types/BucketId"; export * from "./types/Claims"; export * from "./types/Command"; +export * from "./types/Committee"; export * from "./types/CommitteeInfo"; export * from "./types/CommitteeShardInfo"; -export * from "./types/Committee"; export * from "./types/ComponentAccessRules"; export * from "./types/ComponentAddress"; export * from "./types/ComponentBody"; export * from "./types/ComponentHeader"; export * from "./types/ComponentKey"; export * from "./types/ConfidentialClaim"; -export * from "./types/ConfidentialOutputStatement"; export * from "./types/ConfidentialOutput"; +export * from "./types/ConfidentialOutputStatement"; export * from "./types/ConfidentialStatement"; export * from "./types/ConfidentialTransferInputSelection"; export * from "./types/ConfidentialWithdrawProof"; @@ -28,11 +28,12 @@ export * from "./types/EntityId"; export * from "./types/Epoch"; export * from "./types/Event"; export * from "./types/Evidence"; -export * from "./types/ExecutedTransaction"; export * from "./types/ExecuteResult"; +export * from "./types/ExecutedTransaction"; +export * from "./types/ExtraData"; export * from "./types/FeeBreakdown"; -export * from "./types/FeeClaimAddress"; export * from "./types/FeeClaim"; +export * from "./types/FeeClaimAddress"; export * from "./types/FeeCostBreakdown"; export * from "./types/FeeReceipt"; export * from "./types/FeeSource"; @@ -41,10 +42,10 @@ export * from "./types/ForeignProposalAtom"; export * from "./types/FunctionDef"; export * from "./types/IndexedValue"; export * from "./types/IndexedWellKnownTypes"; -export * from "./types/InstructionResult"; export * from "./types/Instruction"; -export * from "./types/JrpcPermissions"; +export * from "./types/InstructionResult"; export * from "./types/JrpcPermission"; +export * from "./types/JrpcPermissions"; export * from "./types/LeaderFee"; export * from "./types/LockFlag"; export * from "./types/LogEntry"; @@ -53,14 +54,14 @@ export * from "./types/Metadata"; export * from "./types/MintConfidentialOutputAtom"; export * from "./types/NetworkCommitteeInfo"; export * from "./types/NodeHeight"; -export * from "./types/NonFungibleAddressContents"; +export * from "./types/NonFungible"; export * from "./types/NonFungibleAddress"; +export * from "./types/NonFungibleAddressContents"; export * from "./types/NonFungibleContainer"; export * from "./types/NonFungibleId"; -export * from "./types/NonFungibleIndexAddress"; export * from "./types/NonFungibleIndex"; +export * from "./types/NonFungibleIndexAddress"; export * from "./types/NonFungibleToken"; -export * from "./types/NonFungible"; export * from "./types/NumPreshards"; export * from "./types/Ordering"; export * from "./types/OwnerRule"; @@ -70,47 +71,47 @@ export * from "./types/QuorumCertificate"; export * from "./types/QuorumDecision"; export * from "./types/RejectReason"; export * from "./types/RequireRule"; +export * from "./types/Resource"; export * from "./types/ResourceAccessRules"; export * from "./types/ResourceAddress"; export * from "./types/ResourceContainer"; -export * from "./types/Resource"; export * from "./types/ResourceType"; export * from "./types/RestrictedAccessRule"; export * from "./types/RuleRequirement"; +export * from "./types/Shard"; export * from "./types/ShardEvidence"; export * from "./types/ShardGroup"; -export * from "./types/Shard"; +export * from "./types/Substate"; export * from "./types/SubstateAddress"; export * from "./types/SubstateDestroyed"; export * from "./types/SubstateDiff"; export * from "./types/SubstateId"; export * from "./types/SubstateLockType"; export * from "./types/SubstateRecord"; -export * from "./types/SubstateRequirementLockIntent"; export * from "./types/SubstateRequirement"; -export * from "./types/Substate"; +export * from "./types/SubstateRequirementLockIntent"; export * from "./types/SubstateType"; export * from "./types/SubstateValue"; export * from "./types/TemplateDef"; export * from "./types/TemplateDefV1"; +export * from "./types/Transaction"; export * from "./types/TransactionAtom"; export * from "./types/TransactionPoolRecord"; export * from "./types/TransactionPoolStage"; -export * from "./types/TransactionReceiptAddress"; export * from "./types/TransactionReceipt"; +export * from "./types/TransactionReceiptAddress"; export * from "./types/TransactionResult"; export * from "./types/TransactionSignature"; export * from "./types/TransactionStatus"; -export * from "./types/Transaction"; export * from "./types/Type"; -export * from "./types/UnclaimedConfidentialOutputAddress"; export * from "./types/UnclaimedConfidentialOutput"; +export * from "./types/UnclaimedConfidentialOutputAddress"; export * from "./types/UnsignedTransaction"; export * from "./types/ValidatorSignature"; -export * from "./types/VaultId"; export * from "./types/Vault"; -export * from "./types/VersionedSubstateIdLockIntent"; +export * from "./types/VaultId"; export * from "./types/VersionedSubstateId"; +export * from "./types/VersionedSubstateIdLockIntent"; export * from "./types/ViewableBalanceProof"; export * from "./base-node-client"; export * from "./tari-indexer-client"; diff --git a/bindings/dist/index.js b/bindings/dist/index.js index 5ae5372842..1defd5c793 100644 --- a/bindings/dist/index.js +++ b/bindings/dist/index.js @@ -3,24 +3,24 @@ export * from "./types/AccessRule"; export * from "./types/Account"; export * from "./types/Amount"; -export * from "./types/ArgDef"; export * from "./types/Arg"; +export * from "./types/ArgDef"; export * from "./types/AuthHook"; export * from "./types/Block"; export * from "./types/BucketId"; export * from "./types/Claims"; export * from "./types/Command"; +export * from "./types/Committee"; export * from "./types/CommitteeInfo"; export * from "./types/CommitteeShardInfo"; -export * from "./types/Committee"; export * from "./types/ComponentAccessRules"; export * from "./types/ComponentAddress"; export * from "./types/ComponentBody"; export * from "./types/ComponentHeader"; export * from "./types/ComponentKey"; export * from "./types/ConfidentialClaim"; -export * from "./types/ConfidentialOutputStatement"; export * from "./types/ConfidentialOutput"; +export * from "./types/ConfidentialOutputStatement"; export * from "./types/ConfidentialStatement"; export * from "./types/ConfidentialTransferInputSelection"; export * from "./types/ConfidentialWithdrawProof"; @@ -30,11 +30,12 @@ export * from "./types/EntityId"; export * from "./types/Epoch"; export * from "./types/Event"; export * from "./types/Evidence"; -export * from "./types/ExecutedTransaction"; export * from "./types/ExecuteResult"; +export * from "./types/ExecutedTransaction"; +export * from "./types/ExtraData"; export * from "./types/FeeBreakdown"; -export * from "./types/FeeClaimAddress"; export * from "./types/FeeClaim"; +export * from "./types/FeeClaimAddress"; export * from "./types/FeeCostBreakdown"; export * from "./types/FeeReceipt"; export * from "./types/FeeSource"; @@ -43,10 +44,10 @@ export * from "./types/ForeignProposalAtom"; export * from "./types/FunctionDef"; export * from "./types/IndexedValue"; export * from "./types/IndexedWellKnownTypes"; -export * from "./types/InstructionResult"; export * from "./types/Instruction"; -export * from "./types/JrpcPermissions"; +export * from "./types/InstructionResult"; export * from "./types/JrpcPermission"; +export * from "./types/JrpcPermissions"; export * from "./types/LeaderFee"; export * from "./types/LockFlag"; export * from "./types/LogEntry"; @@ -55,14 +56,14 @@ export * from "./types/Metadata"; export * from "./types/MintConfidentialOutputAtom"; export * from "./types/NetworkCommitteeInfo"; export * from "./types/NodeHeight"; -export * from "./types/NonFungibleAddressContents"; +export * from "./types/NonFungible"; export * from "./types/NonFungibleAddress"; +export * from "./types/NonFungibleAddressContents"; export * from "./types/NonFungibleContainer"; export * from "./types/NonFungibleId"; -export * from "./types/NonFungibleIndexAddress"; export * from "./types/NonFungibleIndex"; +export * from "./types/NonFungibleIndexAddress"; export * from "./types/NonFungibleToken"; -export * from "./types/NonFungible"; export * from "./types/NumPreshards"; export * from "./types/Ordering"; export * from "./types/OwnerRule"; @@ -72,47 +73,47 @@ export * from "./types/QuorumCertificate"; export * from "./types/QuorumDecision"; export * from "./types/RejectReason"; export * from "./types/RequireRule"; +export * from "./types/Resource"; export * from "./types/ResourceAccessRules"; export * from "./types/ResourceAddress"; export * from "./types/ResourceContainer"; -export * from "./types/Resource"; export * from "./types/ResourceType"; export * from "./types/RestrictedAccessRule"; export * from "./types/RuleRequirement"; +export * from "./types/Shard"; export * from "./types/ShardEvidence"; export * from "./types/ShardGroup"; -export * from "./types/Shard"; +export * from "./types/Substate"; export * from "./types/SubstateAddress"; export * from "./types/SubstateDestroyed"; export * from "./types/SubstateDiff"; export * from "./types/SubstateId"; export * from "./types/SubstateLockType"; export * from "./types/SubstateRecord"; -export * from "./types/SubstateRequirementLockIntent"; export * from "./types/SubstateRequirement"; -export * from "./types/Substate"; +export * from "./types/SubstateRequirementLockIntent"; export * from "./types/SubstateType"; export * from "./types/SubstateValue"; export * from "./types/TemplateDef"; export * from "./types/TemplateDefV1"; +export * from "./types/Transaction"; export * from "./types/TransactionAtom"; export * from "./types/TransactionPoolRecord"; export * from "./types/TransactionPoolStage"; -export * from "./types/TransactionReceiptAddress"; export * from "./types/TransactionReceipt"; +export * from "./types/TransactionReceiptAddress"; export * from "./types/TransactionResult"; export * from "./types/TransactionSignature"; export * from "./types/TransactionStatus"; -export * from "./types/Transaction"; export * from "./types/Type"; -export * from "./types/UnclaimedConfidentialOutputAddress"; export * from "./types/UnclaimedConfidentialOutput"; +export * from "./types/UnclaimedConfidentialOutputAddress"; export * from "./types/UnsignedTransaction"; export * from "./types/ValidatorSignature"; -export * from "./types/VaultId"; export * from "./types/Vault"; -export * from "./types/VersionedSubstateIdLockIntent"; +export * from "./types/VaultId"; export * from "./types/VersionedSubstateId"; +export * from "./types/VersionedSubstateIdLockIntent"; export * from "./types/ViewableBalanceProof"; export * from "./base-node-client"; export * from "./tari-indexer-client"; diff --git a/bindings/dist/tari-indexer-client.d.ts b/bindings/dist/tari-indexer-client.d.ts index 2bc290afbc..03fbfd5375 100644 --- a/bindings/dist/tari-indexer-client.d.ts +++ b/bindings/dist/tari-indexer-client.d.ts @@ -1,34 +1,34 @@ -export * from "./types/tari-indexer-client/IndexerSubmitTransactionResponse"; +export * from "./types/tari-indexer-client/IndexerGetEpochManagerStatsResponse"; +export * from "./types/tari-indexer-client/IndexerAddPeerRequest"; +export * from "./types/tari-indexer-client/NonFungibleSubstate"; +export * from "./types/tari-indexer-client/GetNonFungibleCountResponse"; +export * from "./types/tari-indexer-client/ListSubstatesRequest"; +export * from "./types/tari-indexer-client/IndexerGetIdentityResponse"; export * from "./types/tari-indexer-client/GetTemplateDefinitionRequest"; -export * from "./types/tari-indexer-client/ListSubstatesResponse"; -export * from "./types/tari-indexer-client/IndexerAddPeerResponse"; -export * from "./types/tari-indexer-client/InspectSubstateRequest"; -export * from "./types/tari-indexer-client/ListSubstateItem"; +export * from "./types/tari-indexer-client/IndexerGetSubstateRequest"; +export * from "./types/tari-indexer-client/GetTemplateDefinitionResponse"; +export * from "./types/tari-indexer-client/ListTemplatesRequest"; export * from "./types/tari-indexer-client/IndexerGetAllVnsResponse"; -export * from "./types/tari-indexer-client/ListSubstatesRequest"; -export * from "./types/tari-indexer-client/GetRelatedTransactionsResponse"; -export * from "./types/tari-indexer-client/IndexerTransactionFinalizedResult"; +export * from "./types/tari-indexer-client/GetNonFungiblesRequest"; export * from "./types/tari-indexer-client/IndexerGetSubstateResponse"; +export * from "./types/tari-indexer-client/IndexerSubmitTransactionRequest"; +export * from "./types/tari-indexer-client/IndexerConnection"; +export * from "./types/tari-indexer-client/InspectSubstateResponse"; export * from "./types/tari-indexer-client/IndexerGetAllVnsRequest"; +export * from "./types/tari-indexer-client/InspectSubstateRequest"; export * from "./types/tari-indexer-client/IndexerGetCommsStatsResponse"; -export * from "./types/tari-indexer-client/IndexerConnection"; -export * from "./types/tari-indexer-client/GetNonFungiblesRequest"; -export * from "./types/tari-indexer-client/IndexerAddPeerRequest"; -export * from "./types/tari-indexer-client/GetRelatedTransactionsRequest"; -export * from "./types/tari-indexer-client/GetNonFungiblesResponse"; -export * from "./types/tari-indexer-client/GetTemplateDefinitionResponse"; +export * from "./types/tari-indexer-client/GetNonFungibleCountRequest"; +export * from "./types/tari-indexer-client/GetRelatedTransactionsResponse"; +export * from "./types/tari-indexer-client/IndexerAddPeerResponse"; export * from "./types/tari-indexer-client/IndexerGetConnectionsResponse"; -export * from "./types/tari-indexer-client/IndexerSubmitTransactionRequest"; +export * from "./types/tari-indexer-client/GetNonFungiblesResponse"; +export * from "./types/tari-indexer-client/GetRelatedTransactionsRequest"; +export * from "./types/tari-indexer-client/IndexerGetTransactionResultResponse"; export * from "./types/tari-indexer-client/ListTemplatesResponse"; -export * from "./types/tari-indexer-client/InspectSubstateResponse"; +export * from "./types/tari-indexer-client/IndexerGetTransactionResultRequest"; +export * from "./types/tari-indexer-client/ListSubstatesResponse"; +export * from "./types/tari-indexer-client/IndexerTransactionFinalizedResult"; export * from "./types/tari-indexer-client/GetNonFungibleCollectionsResponse"; -export * from "./types/tari-indexer-client/IndexerGetSubstateRequest"; -export * from "./types/tari-indexer-client/IndexerGetEpochManagerStatsResponse"; -export * from "./types/tari-indexer-client/NonFungibleSubstate"; -export * from "./types/tari-indexer-client/GetNonFungibleCountResponse"; -export * from "./types/tari-indexer-client/ListTemplatesRequest"; -export * from "./types/tari-indexer-client/IndexerGetTransactionResultResponse"; +export * from "./types/tari-indexer-client/IndexerSubmitTransactionResponse"; export * from "./types/tari-indexer-client/IndexerConnectionDirection"; -export * from "./types/tari-indexer-client/IndexerGetIdentityResponse"; -export * from "./types/tari-indexer-client/IndexerGetTransactionResultRequest"; -export * from "./types/tari-indexer-client/GetNonFungibleCountRequest"; +export * from "./types/tari-indexer-client/ListSubstateItem"; diff --git a/bindings/dist/tari-indexer-client.js b/bindings/dist/tari-indexer-client.js index d6c53a6f9d..633c07b9ee 100644 --- a/bindings/dist/tari-indexer-client.js +++ b/bindings/dist/tari-indexer-client.js @@ -1,36 +1,36 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -export * from "./types/tari-indexer-client/IndexerSubmitTransactionResponse"; +export * from "./types/tari-indexer-client/IndexerGetEpochManagerStatsResponse"; +export * from "./types/tari-indexer-client/IndexerAddPeerRequest"; +export * from "./types/tari-indexer-client/NonFungibleSubstate"; +export * from "./types/tari-indexer-client/GetNonFungibleCountResponse"; +export * from "./types/tari-indexer-client/ListSubstatesRequest"; +export * from "./types/tari-indexer-client/IndexerGetIdentityResponse"; export * from "./types/tari-indexer-client/GetTemplateDefinitionRequest"; -export * from "./types/tari-indexer-client/ListSubstatesResponse"; -export * from "./types/tari-indexer-client/IndexerAddPeerResponse"; -export * from "./types/tari-indexer-client/InspectSubstateRequest"; -export * from "./types/tari-indexer-client/ListSubstateItem"; +export * from "./types/tari-indexer-client/IndexerGetSubstateRequest"; +export * from "./types/tari-indexer-client/GetTemplateDefinitionResponse"; +export * from "./types/tari-indexer-client/ListTemplatesRequest"; export * from "./types/tari-indexer-client/IndexerGetAllVnsResponse"; -export * from "./types/tari-indexer-client/ListSubstatesRequest"; -export * from "./types/tari-indexer-client/GetRelatedTransactionsResponse"; -export * from "./types/tari-indexer-client/IndexerTransactionFinalizedResult"; +export * from "./types/tari-indexer-client/GetNonFungiblesRequest"; export * from "./types/tari-indexer-client/IndexerGetSubstateResponse"; +export * from "./types/tari-indexer-client/IndexerSubmitTransactionRequest"; +export * from "./types/tari-indexer-client/IndexerConnection"; +export * from "./types/tari-indexer-client/InspectSubstateResponse"; export * from "./types/tari-indexer-client/IndexerGetAllVnsRequest"; +export * from "./types/tari-indexer-client/InspectSubstateRequest"; export * from "./types/tari-indexer-client/IndexerGetCommsStatsResponse"; -export * from "./types/tari-indexer-client/IndexerConnection"; -export * from "./types/tari-indexer-client/GetNonFungiblesRequest"; -export * from "./types/tari-indexer-client/IndexerAddPeerRequest"; -export * from "./types/tari-indexer-client/GetRelatedTransactionsRequest"; -export * from "./types/tari-indexer-client/GetNonFungiblesResponse"; -export * from "./types/tari-indexer-client/GetTemplateDefinitionResponse"; +export * from "./types/tari-indexer-client/GetNonFungibleCountRequest"; +export * from "./types/tari-indexer-client/GetRelatedTransactionsResponse"; +export * from "./types/tari-indexer-client/IndexerAddPeerResponse"; export * from "./types/tari-indexer-client/IndexerGetConnectionsResponse"; -export * from "./types/tari-indexer-client/IndexerSubmitTransactionRequest"; +export * from "./types/tari-indexer-client/GetNonFungiblesResponse"; +export * from "./types/tari-indexer-client/GetRelatedTransactionsRequest"; +export * from "./types/tari-indexer-client/IndexerGetTransactionResultResponse"; export * from "./types/tari-indexer-client/ListTemplatesResponse"; -export * from "./types/tari-indexer-client/InspectSubstateResponse"; +export * from "./types/tari-indexer-client/IndexerGetTransactionResultRequest"; +export * from "./types/tari-indexer-client/ListSubstatesResponse"; +export * from "./types/tari-indexer-client/IndexerTransactionFinalizedResult"; export * from "./types/tari-indexer-client/GetNonFungibleCollectionsResponse"; -export * from "./types/tari-indexer-client/IndexerGetSubstateRequest"; -export * from "./types/tari-indexer-client/IndexerGetEpochManagerStatsResponse"; -export * from "./types/tari-indexer-client/NonFungibleSubstate"; -export * from "./types/tari-indexer-client/GetNonFungibleCountResponse"; -export * from "./types/tari-indexer-client/ListTemplatesRequest"; -export * from "./types/tari-indexer-client/IndexerGetTransactionResultResponse"; +export * from "./types/tari-indexer-client/IndexerSubmitTransactionResponse"; export * from "./types/tari-indexer-client/IndexerConnectionDirection"; -export * from "./types/tari-indexer-client/IndexerGetIdentityResponse"; -export * from "./types/tari-indexer-client/IndexerGetTransactionResultRequest"; -export * from "./types/tari-indexer-client/GetNonFungibleCountRequest"; +export * from "./types/tari-indexer-client/ListSubstateItem"; diff --git a/bindings/dist/types/Block.d.ts b/bindings/dist/types/Block.d.ts index 4c04f70feb..5ac2b4aca6 100644 --- a/bindings/dist/types/Block.d.ts +++ b/bindings/dist/types/Block.d.ts @@ -1,5 +1,6 @@ import type { Command } from "./Command"; import type { Epoch } from "./Epoch"; +import type { ExtraData } from "./ExtraData"; import type { NodeHeight } from "./NodeHeight"; import type { QuorumCertificate } from "./QuorumCertificate"; import type { Shard } from "./Shard"; @@ -29,4 +30,5 @@ export interface Block { timestamp: number; base_layer_block_height: number; base_layer_block_hash: string; + extra_data: ExtraData | null; } diff --git a/bindings/dist/types/ExtraData.d.ts b/bindings/dist/types/ExtraData.d.ts new file mode 100644 index 0000000000..85d9982c80 --- /dev/null +++ b/bindings/dist/types/ExtraData.d.ts @@ -0,0 +1 @@ +export type ExtraData = string; diff --git a/bindings/dist/types/ExtraData.js b/bindings/dist/types/ExtraData.js new file mode 100644 index 0000000000..e5b481d1ea --- /dev/null +++ b/bindings/dist/types/ExtraData.js @@ -0,0 +1,2 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +export {}; diff --git a/bindings/dist/validator-node-client.d.ts b/bindings/dist/validator-node-client.d.ts index 46ef6ce68a..cfd8756059 100644 --- a/bindings/dist/validator-node-client.d.ts +++ b/bindings/dist/validator-node-client.d.ts @@ -1,56 +1,56 @@ +export * from "./types/validator-node-client/GetCommitteeRequest"; +export * from "./types/validator-node-client/GetRecentTransactionsResponse"; +export * from "./types/validator-node-client/GetRecentTransactionsRequest"; +export * from "./types/validator-node-client/GetTemplatesRequest"; +export * from "./types/validator-node-client/GetBlocksCountResponse"; +export * from "./types/validator-node-client/VNAddPeerRequest"; +export * from "./types/validator-node-client/GetCommitteeResponse"; export * from "./types/validator-node-client/VNSubmitTransactionRequest"; +export * from "./types/validator-node-client/VNConnectionDirection"; +export * from "./types/validator-node-client/VNAddPeerResponse"; +export * from "./types/validator-node-client/GetSubstatesByTransactionResponse"; +export * from "./types/validator-node-client/VNCommitteeShardInfo"; +export * from "./types/validator-node-client/VNFunctionDef"; +export * from "./types/validator-node-client/VNGetValidatorFeesResponse"; +export * from "./types/validator-node-client/VNGetAllVnsResponse"; +export * from "./types/validator-node-client/TemplateAbi"; +export * from "./types/validator-node-client/GetMempoolStatsResponse"; export * from "./types/validator-node-client/TemplateMetadata"; -export * from "./types/validator-node-client/GetFilteredBlocksCountRequest"; +export * from "./types/validator-node-client/GetBlockResponse"; +export * from "./types/validator-node-client/VNLogLevel"; +export * from "./types/validator-node-client/VNGetAllVnsRequest"; export * from "./types/validator-node-client/VNLogEntry"; export * from "./types/validator-node-client/GetShardKeyRequest"; -export * from "./types/validator-node-client/VNGetTransactionResultResponse"; +export * from "./types/validator-node-client/GetBlockRequest"; export * from "./types/validator-node-client/VNSubmitTransactionResponse"; +export * from "./types/validator-node-client/DryRunTransactionFinalizeResult"; +export * from "./types/validator-node-client/GetTransactionResponse"; +export * from "./types/validator-node-client/GetStateResponse"; +export * from "./types/validator-node-client/VNGetSubstateRequest"; +export * from "./types/validator-node-client/VNGetSubstateResponse"; +export * from "./types/validator-node-client/ListBlocksResponse"; +export * from "./types/validator-node-client/SubstateStatus"; +export * from "./types/validator-node-client/ValidatorNode"; +export * from "./types/validator-node-client/GetFilteredBlocksCountRequest"; +export * from "./types/validator-node-client/ListBlocksRequest"; +export * from "./types/validator-node-client/GetEpochManagerStatsResponse"; export * from "./types/validator-node-client/GetTxPoolResponse"; +export * from "./types/validator-node-client/VNConnection"; export * from "./types/validator-node-client/GetTemplateResponse"; -export * from "./types/validator-node-client/GetBlocksCountResponse"; -export * from "./types/validator-node-client/VNGetValidatorFeesRequest"; -export * from "./types/validator-node-client/GetSubstatesByTransactionResponse"; -export * from "./types/validator-node-client/VNConnectionDirection"; -export * from "./types/validator-node-client/VNAddPeerRequest"; -export * from "./types/validator-node-client/GetTemplatesResponse"; -export * from "./types/validator-node-client/GetTransactionRequest"; +export * from "./types/validator-node-client/ValidatorFee"; +export * from "./types/validator-node-client/VNGetConnectionsResponse"; +export * from "./types/validator-node-client/VNGetCommsStatsResponse"; +export * from "./types/validator-node-client/GetTemplateRequest"; export * from "./types/validator-node-client/GetStateRequest"; -export * from "./types/validator-node-client/VNGetIdentityResponse"; -export * from "./types/validator-node-client/VNFunctionDef"; export * from "./types/validator-node-client/GetBlocksRequest"; -export * from "./types/validator-node-client/GetTemplateRequest"; +export * from "./types/validator-node-client/VNGetTransactionResultRequest"; +export * from "./types/validator-node-client/GetTransactionRequest"; +export * from "./types/validator-node-client/GetTemplatesResponse"; +export * from "./types/validator-node-client/VNGetTransactionResultResponse"; +export * from "./types/validator-node-client/VNArgDef"; export * from "./types/validator-node-client/GetShardKeyResponse"; -export * from "./types/validator-node-client/GetSubstatesByTransactionRequest"; -export * from "./types/validator-node-client/VNGetCommsStatsResponse"; -export * from "./types/validator-node-client/GetTransactionResponse"; -export * from "./types/validator-node-client/GetCommitteeResponse"; -export * from "./types/validator-node-client/VNGetAllVnsRequest"; -export * from "./types/validator-node-client/GetCommitteeRequest"; -export * from "./types/validator-node-client/DryRunTransactionFinalizeResult"; -export * from "./types/validator-node-client/VNGetValidatorFeesResponse"; -export * from "./types/validator-node-client/GetStateResponse"; -export * from "./types/validator-node-client/SubstateStatus"; export * from "./types/validator-node-client/GetBlocksResponse"; -export * from "./types/validator-node-client/ListBlocksResponse"; -export * from "./types/validator-node-client/GetBlockRequest"; -export * from "./types/validator-node-client/VNGetTransactionResultRequest"; -export * from "./types/validator-node-client/GetRecentTransactionsRequest"; -export * from "./types/validator-node-client/ValidatorFee"; -export * from "./types/validator-node-client/VNLogLevel"; -export * from "./types/validator-node-client/GetRecentTransactionsResponse"; -export * from "./types/validator-node-client/VNAddPeerResponse"; -export * from "./types/validator-node-client/VNConnection"; -export * from "./types/validator-node-client/ValidatorNode"; -export * from "./types/validator-node-client/VNGetConnectionsResponse"; -export * from "./types/validator-node-client/ListBlocksRequest"; +export * from "./types/validator-node-client/VNGetIdentityResponse"; +export * from "./types/validator-node-client/GetSubstatesByTransactionRequest"; export * from "./types/validator-node-client/GetNetworkCommitteeResponse"; -export * from "./types/validator-node-client/VNGetSubstateRequest"; -export * from "./types/validator-node-client/GetMempoolStatsResponse"; -export * from "./types/validator-node-client/TemplateAbi"; -export * from "./types/validator-node-client/GetTemplatesRequest"; -export * from "./types/validator-node-client/VNGetSubstateResponse"; -export * from "./types/validator-node-client/VNGetAllVnsResponse"; -export * from "./types/validator-node-client/VNArgDef"; -export * from "./types/validator-node-client/GetEpochManagerStatsResponse"; -export * from "./types/validator-node-client/GetBlockResponse"; -export * from "./types/validator-node-client/VNCommitteeShardInfo"; +export * from "./types/validator-node-client/VNGetValidatorFeesRequest"; diff --git a/bindings/dist/validator-node-client.js b/bindings/dist/validator-node-client.js index 89eb92b006..477e5dd455 100644 --- a/bindings/dist/validator-node-client.js +++ b/bindings/dist/validator-node-client.js @@ -1,58 +1,58 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +export * from "./types/validator-node-client/GetCommitteeRequest"; +export * from "./types/validator-node-client/GetRecentTransactionsResponse"; +export * from "./types/validator-node-client/GetRecentTransactionsRequest"; +export * from "./types/validator-node-client/GetTemplatesRequest"; +export * from "./types/validator-node-client/GetBlocksCountResponse"; +export * from "./types/validator-node-client/VNAddPeerRequest"; +export * from "./types/validator-node-client/GetCommitteeResponse"; export * from "./types/validator-node-client/VNSubmitTransactionRequest"; +export * from "./types/validator-node-client/VNConnectionDirection"; +export * from "./types/validator-node-client/VNAddPeerResponse"; +export * from "./types/validator-node-client/GetSubstatesByTransactionResponse"; +export * from "./types/validator-node-client/VNCommitteeShardInfo"; +export * from "./types/validator-node-client/VNFunctionDef"; +export * from "./types/validator-node-client/VNGetValidatorFeesResponse"; +export * from "./types/validator-node-client/VNGetAllVnsResponse"; +export * from "./types/validator-node-client/TemplateAbi"; +export * from "./types/validator-node-client/GetMempoolStatsResponse"; export * from "./types/validator-node-client/TemplateMetadata"; -export * from "./types/validator-node-client/GetFilteredBlocksCountRequest"; +export * from "./types/validator-node-client/GetBlockResponse"; +export * from "./types/validator-node-client/VNLogLevel"; +export * from "./types/validator-node-client/VNGetAllVnsRequest"; export * from "./types/validator-node-client/VNLogEntry"; export * from "./types/validator-node-client/GetShardKeyRequest"; -export * from "./types/validator-node-client/VNGetTransactionResultResponse"; +export * from "./types/validator-node-client/GetBlockRequest"; export * from "./types/validator-node-client/VNSubmitTransactionResponse"; +export * from "./types/validator-node-client/DryRunTransactionFinalizeResult"; +export * from "./types/validator-node-client/GetTransactionResponse"; +export * from "./types/validator-node-client/GetStateResponse"; +export * from "./types/validator-node-client/VNGetSubstateRequest"; +export * from "./types/validator-node-client/VNGetSubstateResponse"; +export * from "./types/validator-node-client/ListBlocksResponse"; +export * from "./types/validator-node-client/SubstateStatus"; +export * from "./types/validator-node-client/ValidatorNode"; +export * from "./types/validator-node-client/GetFilteredBlocksCountRequest"; +export * from "./types/validator-node-client/ListBlocksRequest"; +export * from "./types/validator-node-client/GetEpochManagerStatsResponse"; export * from "./types/validator-node-client/GetTxPoolResponse"; +export * from "./types/validator-node-client/VNConnection"; export * from "./types/validator-node-client/GetTemplateResponse"; -export * from "./types/validator-node-client/GetBlocksCountResponse"; -export * from "./types/validator-node-client/VNGetValidatorFeesRequest"; -export * from "./types/validator-node-client/GetSubstatesByTransactionResponse"; -export * from "./types/validator-node-client/VNConnectionDirection"; -export * from "./types/validator-node-client/VNAddPeerRequest"; -export * from "./types/validator-node-client/GetTemplatesResponse"; -export * from "./types/validator-node-client/GetTransactionRequest"; +export * from "./types/validator-node-client/ValidatorFee"; +export * from "./types/validator-node-client/VNGetConnectionsResponse"; +export * from "./types/validator-node-client/VNGetCommsStatsResponse"; +export * from "./types/validator-node-client/GetTemplateRequest"; export * from "./types/validator-node-client/GetStateRequest"; -export * from "./types/validator-node-client/VNGetIdentityResponse"; -export * from "./types/validator-node-client/VNFunctionDef"; export * from "./types/validator-node-client/GetBlocksRequest"; -export * from "./types/validator-node-client/GetTemplateRequest"; +export * from "./types/validator-node-client/VNGetTransactionResultRequest"; +export * from "./types/validator-node-client/GetTransactionRequest"; +export * from "./types/validator-node-client/GetTemplatesResponse"; +export * from "./types/validator-node-client/VNGetTransactionResultResponse"; +export * from "./types/validator-node-client/VNArgDef"; export * from "./types/validator-node-client/GetShardKeyResponse"; -export * from "./types/validator-node-client/GetSubstatesByTransactionRequest"; -export * from "./types/validator-node-client/VNGetCommsStatsResponse"; -export * from "./types/validator-node-client/GetTransactionResponse"; -export * from "./types/validator-node-client/GetCommitteeResponse"; -export * from "./types/validator-node-client/VNGetAllVnsRequest"; -export * from "./types/validator-node-client/GetCommitteeRequest"; -export * from "./types/validator-node-client/DryRunTransactionFinalizeResult"; -export * from "./types/validator-node-client/VNGetValidatorFeesResponse"; -export * from "./types/validator-node-client/GetStateResponse"; -export * from "./types/validator-node-client/SubstateStatus"; export * from "./types/validator-node-client/GetBlocksResponse"; -export * from "./types/validator-node-client/ListBlocksResponse"; -export * from "./types/validator-node-client/GetBlockRequest"; -export * from "./types/validator-node-client/VNGetTransactionResultRequest"; -export * from "./types/validator-node-client/GetRecentTransactionsRequest"; -export * from "./types/validator-node-client/ValidatorFee"; -export * from "./types/validator-node-client/VNLogLevel"; -export * from "./types/validator-node-client/GetRecentTransactionsResponse"; -export * from "./types/validator-node-client/VNAddPeerResponse"; -export * from "./types/validator-node-client/VNConnection"; -export * from "./types/validator-node-client/ValidatorNode"; -export * from "./types/validator-node-client/VNGetConnectionsResponse"; -export * from "./types/validator-node-client/ListBlocksRequest"; +export * from "./types/validator-node-client/VNGetIdentityResponse"; +export * from "./types/validator-node-client/GetSubstatesByTransactionRequest"; export * from "./types/validator-node-client/GetNetworkCommitteeResponse"; -export * from "./types/validator-node-client/VNGetSubstateRequest"; -export * from "./types/validator-node-client/GetMempoolStatsResponse"; -export * from "./types/validator-node-client/TemplateAbi"; -export * from "./types/validator-node-client/GetTemplatesRequest"; -export * from "./types/validator-node-client/VNGetSubstateResponse"; -export * from "./types/validator-node-client/VNGetAllVnsResponse"; -export * from "./types/validator-node-client/VNArgDef"; -export * from "./types/validator-node-client/GetEpochManagerStatsResponse"; -export * from "./types/validator-node-client/GetBlockResponse"; -export * from "./types/validator-node-client/VNCommitteeShardInfo"; +export * from "./types/validator-node-client/VNGetValidatorFeesRequest"; diff --git a/bindings/dist/wallet-daemon-client.d.ts b/bindings/dist/wallet-daemon-client.d.ts index 6e37656aef..0a00af2bbc 100644 --- a/bindings/dist/wallet-daemon-client.d.ts +++ b/bindings/dist/wallet-daemon-client.d.ts @@ -1,87 +1,87 @@ -export * from "./types/wallet-daemon-client/WalletSubstateRecord"; -export * from "./types/wallet-daemon-client/ProofsFinalizeResponse"; -export * from "./types/wallet-daemon-client/CallInstructionRequest"; -export * from "./types/wallet-daemon-client/AuthLoginAcceptResponse"; -export * from "./types/wallet-daemon-client/AccountsListRequest"; -export * from "./types/wallet-daemon-client/AuthGetAllJwtResponse"; -export * from "./types/wallet-daemon-client/AuthLoginResponse"; -export * from "./types/wallet-daemon-client/GetValidatorFeesRequest"; -export * from "./types/wallet-daemon-client/AccountGetDefaultRequest"; -export * from "./types/wallet-daemon-client/AuthGetAllJwtRequest"; -export * from "./types/wallet-daemon-client/KeyBranch"; -export * from "./types/wallet-daemon-client/AccountGetResponse"; -export * from "./types/wallet-daemon-client/SettingsSetRequest"; -export * from "./types/wallet-daemon-client/GetValidatorFeesResponse"; -export * from "./types/wallet-daemon-client/TransactionGetAllRequest"; +export * from "./types/wallet-daemon-client/AccountsListResponse"; export * from "./types/wallet-daemon-client/RevealFundsRequest"; -export * from "./types/wallet-daemon-client/AuthLoginDenyResponse"; -export * from "./types/wallet-daemon-client/AuthLoginDenyRequest"; -export * from "./types/wallet-daemon-client/TransactionSubmitResponse"; -export * from "./types/wallet-daemon-client/ProofsGenerateResponse"; -export * from "./types/wallet-daemon-client/ClaimValidatorFeesRequest"; -export * from "./types/wallet-daemon-client/AuthLoginRequest"; +export * from "./types/wallet-daemon-client/ClaimBurnResponse"; +export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; +export * from "./types/wallet-daemon-client/WalletSubstateRecord"; +export * from "./types/wallet-daemon-client/AccountsGetBalancesResponse"; +export * from "./types/wallet-daemon-client/SubstatesGetResponse"; export * from "./types/wallet-daemon-client/ListAccountNftRequest"; +export * from "./types/wallet-daemon-client/RevealFundsResponse"; +export * from "./types/wallet-daemon-client/AccountSetDefaultRequest"; +export * from "./types/wallet-daemon-client/GetAccountNftRequest"; +export * from "./types/wallet-daemon-client/TransactionGetAllResponse"; export * from "./types/wallet-daemon-client/TransactionGetRequest"; -export * from "./types/wallet-daemon-client/AuthRevokeTokenResponse"; -export * from "./types/wallet-daemon-client/ProofsFinalizeRequest"; -export * from "./types/wallet-daemon-client/AccountsTransferRequest"; -export * from "./types/wallet-daemon-client/SettingsSetResponse"; -export * from "./types/wallet-daemon-client/KeysListRequest"; +export * from "./types/wallet-daemon-client/KeyBranch"; +export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; +export * from "./types/wallet-daemon-client/AccountSetDefaultResponse"; +export * from "./types/wallet-daemon-client/SubstatesListResponse"; +export * from "./types/wallet-daemon-client/AccountGetDefaultRequest"; +export * from "./types/wallet-daemon-client/KeysListResponse"; +export * from "./types/wallet-daemon-client/ProofsGenerateResponse"; +export * from "./types/wallet-daemon-client/TransactionGetAllRequest"; export * from "./types/wallet-daemon-client/AccountsTransferResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResultResponse"; +export * from "./types/wallet-daemon-client/AuthLoginResponse"; export * from "./types/wallet-daemon-client/ClaimBurnRequest"; -export * from "./types/wallet-daemon-client/KeysListResponse"; +export * from "./types/wallet-daemon-client/BalanceEntry"; +export * from "./types/wallet-daemon-client/KeysListRequest"; export * from "./types/wallet-daemon-client/SubstatesGetRequest"; -export * from "./types/wallet-daemon-client/TransactionSubmitRequest"; -export * from "./types/wallet-daemon-client/AccountsGetBalancesResponse"; -export * from "./types/wallet-daemon-client/TransactionGetAllResponse"; -export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsResponse"; -export * from "./types/wallet-daemon-client/ConfidentialTransferResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResponse"; -export * from "./types/wallet-daemon-client/SubstatesGetResponse"; -export * from "./types/wallet-daemon-client/GetAccountNftRequest"; +export * from "./types/wallet-daemon-client/ProofsFinalizeResponse"; export * from "./types/wallet-daemon-client/AuthRevokeTokenRequest"; -export * from "./types/wallet-daemon-client/ProofsGenerateRequest"; -export * from "./types/wallet-daemon-client/RevealFundsResponse"; -export * from "./types/wallet-daemon-client/AccountsInvokeResponse"; -export * from "./types/wallet-daemon-client/AccountsListResponse"; +export * from "./types/wallet-daemon-client/TransactionWaitResultRequest"; +export * from "./types/wallet-daemon-client/TransactionSubmitResponse"; +export * from "./types/wallet-daemon-client/AccountsCreateRequest"; +export * from "./types/wallet-daemon-client/CallInstructionRequest"; +export * from "./types/wallet-daemon-client/WebRtcStartRequest"; export * from "./types/wallet-daemon-client/WebRtcStartResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResultRequest"; +export * from "./types/wallet-daemon-client/MintAccountNftResponse"; export * from "./types/wallet-daemon-client/ProofsCancelResponse"; -export * from "./types/wallet-daemon-client/AccountsCreateRequest"; -export * from "./types/wallet-daemon-client/MintAccountNftRequest"; -export * from "./types/wallet-daemon-client/TransactionClaimBurnResponse"; -export * from "./types/wallet-daemon-client/TransactionWaitResultRequest"; -export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/AccountGetResponse"; +export * from "./types/wallet-daemon-client/ClaimValidatorFeesRequest"; +export * from "./types/wallet-daemon-client/AuthRevokeTokenResponse"; +export * from "./types/wallet-daemon-client/AccountsInvokeRequest"; +export * from "./types/wallet-daemon-client/WebRtcStart"; +export * from "./types/wallet-daemon-client/TransactionWaitResultResponse"; +export * from "./types/wallet-daemon-client/GetValidatorFeesRequest"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofRequest"; -export * from "./types/wallet-daemon-client/KeysCreateRequest"; +export * from "./types/wallet-daemon-client/KeysCreateResponse"; +export * from "./types/wallet-daemon-client/AuthLoginDenyRequest"; +export * from "./types/wallet-daemon-client/ProofsFinalizeRequest"; +export * from "./types/wallet-daemon-client/ProofsCancelRequest"; export * from "./types/wallet-daemon-client/SettingsGetResponse"; -export * from "./types/wallet-daemon-client/ClaimBurnResponse"; -export * from "./types/wallet-daemon-client/WebRtcStartRequest"; -export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; -export * from "./types/wallet-daemon-client/WebRtcStart"; -export * from "./types/wallet-daemon-client/AccountSetDefaultRequest"; +export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; +export * from "./types/wallet-daemon-client/GetValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/TransactionGetResponse"; +export * from "./types/wallet-daemon-client/TransactionSubmitRequest"; +export * from "./types/wallet-daemon-client/ListAccountNftResponse"; +export * from "./types/wallet-daemon-client/AccountsInvokeResponse"; +export * from "./types/wallet-daemon-client/AccountsListRequest"; +export * from "./types/wallet-daemon-client/AuthLoginAcceptResponse"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofResponse"; +export * from "./types/wallet-daemon-client/TemplatesGetRequest"; +export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; +export * from "./types/wallet-daemon-client/SettingsSetRequest"; +export * from "./types/wallet-daemon-client/MintAccountNftRequest"; +export * from "./types/wallet-daemon-client/TransactionClaimBurnResponse"; +export * from "./types/wallet-daemon-client/AuthGetAllJwtRequest"; +export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/ConfidentialTransferResponse"; export * from "./types/wallet-daemon-client/TemplatesGetResponse"; -export * from "./types/wallet-daemon-client/ListAccountNftResponse"; -export * from "./types/wallet-daemon-client/AccountsGetBalancesRequest"; -export * from "./types/wallet-daemon-client/MintAccountNftResponse"; +export * from "./types/wallet-daemon-client/AccountsTransferRequest"; +export * from "./types/wallet-daemon-client/AuthLoginRequest"; +export * from "./types/wallet-daemon-client/AuthGetAllJwtResponse"; export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsRequest"; -export * from "./types/wallet-daemon-client/KeysCreateResponse"; +export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; +export * from "./types/wallet-daemon-client/SettingsSetResponse"; +export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsResponse"; +export * from "./types/wallet-daemon-client/AccountGetRequest"; +export * from "./types/wallet-daemon-client/KeysCreateRequest"; +export * from "./types/wallet-daemon-client/ProofsGenerateRequest"; +export * from "./types/wallet-daemon-client/TransactionGetResultResponse"; +export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; +export * from "./types/wallet-daemon-client/AuthLoginDenyResponse"; +export * from "./types/wallet-daemon-client/TransactionGetResultRequest"; +export * from "./types/wallet-daemon-client/AccountsCreateResponse"; +export * from "./types/wallet-daemon-client/AccountsGetBalancesRequest"; export * from "./types/wallet-daemon-client/AccountInfo"; export * from "./types/wallet-daemon-client/SubstatesListRequest"; -export * from "./types/wallet-daemon-client/BalanceEntry"; -export * from "./types/wallet-daemon-client/TemplatesGetRequest"; -export * from "./types/wallet-daemon-client/AccountsInvokeRequest"; -export * from "./types/wallet-daemon-client/ProofsCancelRequest"; -export * from "./types/wallet-daemon-client/AccountSetDefaultResponse"; -export * from "./types/wallet-daemon-client/SubstatesListResponse"; -export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; export * from "./types/wallet-daemon-client/ComponentAddressOrName"; -export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; -export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; -export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; -export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; -export * from "./types/wallet-daemon-client/AccountsCreateResponse"; -export * from "./types/wallet-daemon-client/TransactionWaitResultResponse"; -export * from "./types/wallet-daemon-client/AccountGetRequest"; diff --git a/bindings/dist/wallet-daemon-client.js b/bindings/dist/wallet-daemon-client.js index 3c54e676a7..776d38ec12 100644 --- a/bindings/dist/wallet-daemon-client.js +++ b/bindings/dist/wallet-daemon-client.js @@ -1,89 +1,89 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -export * from "./types/wallet-daemon-client/WalletSubstateRecord"; -export * from "./types/wallet-daemon-client/ProofsFinalizeResponse"; -export * from "./types/wallet-daemon-client/CallInstructionRequest"; -export * from "./types/wallet-daemon-client/AuthLoginAcceptResponse"; -export * from "./types/wallet-daemon-client/AccountsListRequest"; -export * from "./types/wallet-daemon-client/AuthGetAllJwtResponse"; -export * from "./types/wallet-daemon-client/AuthLoginResponse"; -export * from "./types/wallet-daemon-client/GetValidatorFeesRequest"; -export * from "./types/wallet-daemon-client/AccountGetDefaultRequest"; -export * from "./types/wallet-daemon-client/AuthGetAllJwtRequest"; -export * from "./types/wallet-daemon-client/KeyBranch"; -export * from "./types/wallet-daemon-client/AccountGetResponse"; -export * from "./types/wallet-daemon-client/SettingsSetRequest"; -export * from "./types/wallet-daemon-client/GetValidatorFeesResponse"; -export * from "./types/wallet-daemon-client/TransactionGetAllRequest"; +export * from "./types/wallet-daemon-client/AccountsListResponse"; export * from "./types/wallet-daemon-client/RevealFundsRequest"; -export * from "./types/wallet-daemon-client/AuthLoginDenyResponse"; -export * from "./types/wallet-daemon-client/AuthLoginDenyRequest"; -export * from "./types/wallet-daemon-client/TransactionSubmitResponse"; -export * from "./types/wallet-daemon-client/ProofsGenerateResponse"; -export * from "./types/wallet-daemon-client/ClaimValidatorFeesRequest"; -export * from "./types/wallet-daemon-client/AuthLoginRequest"; +export * from "./types/wallet-daemon-client/ClaimBurnResponse"; +export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; +export * from "./types/wallet-daemon-client/WalletSubstateRecord"; +export * from "./types/wallet-daemon-client/AccountsGetBalancesResponse"; +export * from "./types/wallet-daemon-client/SubstatesGetResponse"; export * from "./types/wallet-daemon-client/ListAccountNftRequest"; +export * from "./types/wallet-daemon-client/RevealFundsResponse"; +export * from "./types/wallet-daemon-client/AccountSetDefaultRequest"; +export * from "./types/wallet-daemon-client/GetAccountNftRequest"; +export * from "./types/wallet-daemon-client/TransactionGetAllResponse"; export * from "./types/wallet-daemon-client/TransactionGetRequest"; -export * from "./types/wallet-daemon-client/AuthRevokeTokenResponse"; -export * from "./types/wallet-daemon-client/ProofsFinalizeRequest"; -export * from "./types/wallet-daemon-client/AccountsTransferRequest"; -export * from "./types/wallet-daemon-client/SettingsSetResponse"; -export * from "./types/wallet-daemon-client/KeysListRequest"; +export * from "./types/wallet-daemon-client/KeyBranch"; +export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; +export * from "./types/wallet-daemon-client/AccountSetDefaultResponse"; +export * from "./types/wallet-daemon-client/SubstatesListResponse"; +export * from "./types/wallet-daemon-client/AccountGetDefaultRequest"; +export * from "./types/wallet-daemon-client/KeysListResponse"; +export * from "./types/wallet-daemon-client/ProofsGenerateResponse"; +export * from "./types/wallet-daemon-client/TransactionGetAllRequest"; export * from "./types/wallet-daemon-client/AccountsTransferResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResultResponse"; +export * from "./types/wallet-daemon-client/AuthLoginResponse"; export * from "./types/wallet-daemon-client/ClaimBurnRequest"; -export * from "./types/wallet-daemon-client/KeysListResponse"; +export * from "./types/wallet-daemon-client/BalanceEntry"; +export * from "./types/wallet-daemon-client/KeysListRequest"; export * from "./types/wallet-daemon-client/SubstatesGetRequest"; -export * from "./types/wallet-daemon-client/TransactionSubmitRequest"; -export * from "./types/wallet-daemon-client/AccountsGetBalancesResponse"; -export * from "./types/wallet-daemon-client/TransactionGetAllResponse"; -export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsResponse"; -export * from "./types/wallet-daemon-client/ConfidentialTransferResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResponse"; -export * from "./types/wallet-daemon-client/SubstatesGetResponse"; -export * from "./types/wallet-daemon-client/GetAccountNftRequest"; +export * from "./types/wallet-daemon-client/ProofsFinalizeResponse"; export * from "./types/wallet-daemon-client/AuthRevokeTokenRequest"; -export * from "./types/wallet-daemon-client/ProofsGenerateRequest"; -export * from "./types/wallet-daemon-client/RevealFundsResponse"; -export * from "./types/wallet-daemon-client/AccountsInvokeResponse"; -export * from "./types/wallet-daemon-client/AccountsListResponse"; +export * from "./types/wallet-daemon-client/TransactionWaitResultRequest"; +export * from "./types/wallet-daemon-client/TransactionSubmitResponse"; +export * from "./types/wallet-daemon-client/AccountsCreateRequest"; +export * from "./types/wallet-daemon-client/CallInstructionRequest"; +export * from "./types/wallet-daemon-client/WebRtcStartRequest"; export * from "./types/wallet-daemon-client/WebRtcStartResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResultRequest"; +export * from "./types/wallet-daemon-client/MintAccountNftResponse"; export * from "./types/wallet-daemon-client/ProofsCancelResponse"; -export * from "./types/wallet-daemon-client/AccountsCreateRequest"; -export * from "./types/wallet-daemon-client/MintAccountNftRequest"; -export * from "./types/wallet-daemon-client/TransactionClaimBurnResponse"; -export * from "./types/wallet-daemon-client/TransactionWaitResultRequest"; -export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/AccountGetResponse"; +export * from "./types/wallet-daemon-client/ClaimValidatorFeesRequest"; +export * from "./types/wallet-daemon-client/AuthRevokeTokenResponse"; +export * from "./types/wallet-daemon-client/AccountsInvokeRequest"; +export * from "./types/wallet-daemon-client/WebRtcStart"; +export * from "./types/wallet-daemon-client/TransactionWaitResultResponse"; +export * from "./types/wallet-daemon-client/GetValidatorFeesRequest"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofRequest"; -export * from "./types/wallet-daemon-client/KeysCreateRequest"; +export * from "./types/wallet-daemon-client/KeysCreateResponse"; +export * from "./types/wallet-daemon-client/AuthLoginDenyRequest"; +export * from "./types/wallet-daemon-client/ProofsFinalizeRequest"; +export * from "./types/wallet-daemon-client/ProofsCancelRequest"; export * from "./types/wallet-daemon-client/SettingsGetResponse"; -export * from "./types/wallet-daemon-client/ClaimBurnResponse"; -export * from "./types/wallet-daemon-client/WebRtcStartRequest"; -export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; -export * from "./types/wallet-daemon-client/WebRtcStart"; -export * from "./types/wallet-daemon-client/AccountSetDefaultRequest"; +export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; +export * from "./types/wallet-daemon-client/GetValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/TransactionGetResponse"; +export * from "./types/wallet-daemon-client/TransactionSubmitRequest"; +export * from "./types/wallet-daemon-client/ListAccountNftResponse"; +export * from "./types/wallet-daemon-client/AccountsInvokeResponse"; +export * from "./types/wallet-daemon-client/AccountsListRequest"; +export * from "./types/wallet-daemon-client/AuthLoginAcceptResponse"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofResponse"; +export * from "./types/wallet-daemon-client/TemplatesGetRequest"; +export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; +export * from "./types/wallet-daemon-client/SettingsSetRequest"; +export * from "./types/wallet-daemon-client/MintAccountNftRequest"; +export * from "./types/wallet-daemon-client/TransactionClaimBurnResponse"; +export * from "./types/wallet-daemon-client/AuthGetAllJwtRequest"; +export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/ConfidentialTransferResponse"; export * from "./types/wallet-daemon-client/TemplatesGetResponse"; -export * from "./types/wallet-daemon-client/ListAccountNftResponse"; -export * from "./types/wallet-daemon-client/AccountsGetBalancesRequest"; -export * from "./types/wallet-daemon-client/MintAccountNftResponse"; +export * from "./types/wallet-daemon-client/AccountsTransferRequest"; +export * from "./types/wallet-daemon-client/AuthLoginRequest"; +export * from "./types/wallet-daemon-client/AuthGetAllJwtResponse"; export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsRequest"; -export * from "./types/wallet-daemon-client/KeysCreateResponse"; +export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; +export * from "./types/wallet-daemon-client/SettingsSetResponse"; +export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsResponse"; +export * from "./types/wallet-daemon-client/AccountGetRequest"; +export * from "./types/wallet-daemon-client/KeysCreateRequest"; +export * from "./types/wallet-daemon-client/ProofsGenerateRequest"; +export * from "./types/wallet-daemon-client/TransactionGetResultResponse"; +export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; +export * from "./types/wallet-daemon-client/AuthLoginDenyResponse"; +export * from "./types/wallet-daemon-client/TransactionGetResultRequest"; +export * from "./types/wallet-daemon-client/AccountsCreateResponse"; +export * from "./types/wallet-daemon-client/AccountsGetBalancesRequest"; export * from "./types/wallet-daemon-client/AccountInfo"; export * from "./types/wallet-daemon-client/SubstatesListRequest"; -export * from "./types/wallet-daemon-client/BalanceEntry"; -export * from "./types/wallet-daemon-client/TemplatesGetRequest"; -export * from "./types/wallet-daemon-client/AccountsInvokeRequest"; -export * from "./types/wallet-daemon-client/ProofsCancelRequest"; -export * from "./types/wallet-daemon-client/AccountSetDefaultResponse"; -export * from "./types/wallet-daemon-client/SubstatesListResponse"; -export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; export * from "./types/wallet-daemon-client/ComponentAddressOrName"; -export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; -export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; -export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; -export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; -export * from "./types/wallet-daemon-client/AccountsCreateResponse"; -export * from "./types/wallet-daemon-client/TransactionWaitResultResponse"; -export * from "./types/wallet-daemon-client/AccountGetRequest"; diff --git a/bindings/src/index.ts b/bindings/src/index.ts index 20589d2740..453d48483e 100644 --- a/bindings/src/index.ts +++ b/bindings/src/index.ts @@ -4,24 +4,24 @@ export * from "./types/AccessRule"; export * from "./types/Account"; export * from "./types/Amount"; -export * from "./types/ArgDef"; export * from "./types/Arg"; +export * from "./types/ArgDef"; export * from "./types/AuthHook"; export * from "./types/Block"; export * from "./types/BucketId"; export * from "./types/Claims"; export * from "./types/Command"; +export * from "./types/Committee"; export * from "./types/CommitteeInfo"; export * from "./types/CommitteeShardInfo"; -export * from "./types/Committee"; export * from "./types/ComponentAccessRules"; export * from "./types/ComponentAddress"; export * from "./types/ComponentBody"; export * from "./types/ComponentHeader"; export * from "./types/ComponentKey"; export * from "./types/ConfidentialClaim"; -export * from "./types/ConfidentialOutputStatement"; export * from "./types/ConfidentialOutput"; +export * from "./types/ConfidentialOutputStatement"; export * from "./types/ConfidentialStatement"; export * from "./types/ConfidentialTransferInputSelection"; export * from "./types/ConfidentialWithdrawProof"; @@ -31,11 +31,12 @@ export * from "./types/EntityId"; export * from "./types/Epoch"; export * from "./types/Event"; export * from "./types/Evidence"; -export * from "./types/ExecutedTransaction"; export * from "./types/ExecuteResult"; +export * from "./types/ExecutedTransaction"; +export * from "./types/ExtraData"; export * from "./types/FeeBreakdown"; -export * from "./types/FeeClaimAddress"; export * from "./types/FeeClaim"; +export * from "./types/FeeClaimAddress"; export * from "./types/FeeCostBreakdown"; export * from "./types/FeeReceipt"; export * from "./types/FeeSource"; @@ -44,10 +45,10 @@ export * from "./types/ForeignProposalAtom"; export * from "./types/FunctionDef"; export * from "./types/IndexedValue"; export * from "./types/IndexedWellKnownTypes"; -export * from "./types/InstructionResult"; export * from "./types/Instruction"; -export * from "./types/JrpcPermissions"; +export * from "./types/InstructionResult"; export * from "./types/JrpcPermission"; +export * from "./types/JrpcPermissions"; export * from "./types/LeaderFee"; export * from "./types/LockFlag"; export * from "./types/LogEntry"; @@ -56,14 +57,14 @@ export * from "./types/Metadata"; export * from "./types/MintConfidentialOutputAtom"; export * from "./types/NetworkCommitteeInfo"; export * from "./types/NodeHeight"; -export * from "./types/NonFungibleAddressContents"; +export * from "./types/NonFungible"; export * from "./types/NonFungibleAddress"; +export * from "./types/NonFungibleAddressContents"; export * from "./types/NonFungibleContainer"; export * from "./types/NonFungibleId"; -export * from "./types/NonFungibleIndexAddress"; export * from "./types/NonFungibleIndex"; +export * from "./types/NonFungibleIndexAddress"; export * from "./types/NonFungibleToken"; -export * from "./types/NonFungible"; export * from "./types/NumPreshards"; export * from "./types/Ordering"; export * from "./types/OwnerRule"; @@ -73,47 +74,47 @@ export * from "./types/QuorumCertificate"; export * from "./types/QuorumDecision"; export * from "./types/RejectReason"; export * from "./types/RequireRule"; +export * from "./types/Resource"; export * from "./types/ResourceAccessRules"; export * from "./types/ResourceAddress"; export * from "./types/ResourceContainer"; -export * from "./types/Resource"; export * from "./types/ResourceType"; export * from "./types/RestrictedAccessRule"; export * from "./types/RuleRequirement"; +export * from "./types/Shard"; export * from "./types/ShardEvidence"; export * from "./types/ShardGroup"; -export * from "./types/Shard"; +export * from "./types/Substate"; export * from "./types/SubstateAddress"; export * from "./types/SubstateDestroyed"; export * from "./types/SubstateDiff"; export * from "./types/SubstateId"; export * from "./types/SubstateLockType"; export * from "./types/SubstateRecord"; -export * from "./types/SubstateRequirementLockIntent"; export * from "./types/SubstateRequirement"; -export * from "./types/Substate"; +export * from "./types/SubstateRequirementLockIntent"; export * from "./types/SubstateType"; export * from "./types/SubstateValue"; export * from "./types/TemplateDef"; export * from "./types/TemplateDefV1"; +export * from "./types/Transaction"; export * from "./types/TransactionAtom"; export * from "./types/TransactionPoolRecord"; export * from "./types/TransactionPoolStage"; -export * from "./types/TransactionReceiptAddress"; export * from "./types/TransactionReceipt"; +export * from "./types/TransactionReceiptAddress"; export * from "./types/TransactionResult"; export * from "./types/TransactionSignature"; export * from "./types/TransactionStatus"; -export * from "./types/Transaction"; export * from "./types/Type"; -export * from "./types/UnclaimedConfidentialOutputAddress"; export * from "./types/UnclaimedConfidentialOutput"; +export * from "./types/UnclaimedConfidentialOutputAddress"; export * from "./types/UnsignedTransaction"; export * from "./types/ValidatorSignature"; -export * from "./types/VaultId"; export * from "./types/Vault"; -export * from "./types/VersionedSubstateIdLockIntent"; +export * from "./types/VaultId"; export * from "./types/VersionedSubstateId"; +export * from "./types/VersionedSubstateIdLockIntent"; export * from "./types/ViewableBalanceProof"; export * from "./base-node-client"; export * from "./tari-indexer-client"; diff --git a/bindings/src/tari-indexer-client.ts b/bindings/src/tari-indexer-client.ts index 9d60c94165..e9782093f5 100644 --- a/bindings/src/tari-indexer-client.ts +++ b/bindings/src/tari-indexer-client.ts @@ -1,37 +1,37 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -export * from "./types/tari-indexer-client/IndexerSubmitTransactionResponse"; +export * from "./types/tari-indexer-client/IndexerGetEpochManagerStatsResponse"; +export * from "./types/tari-indexer-client/IndexerAddPeerRequest"; +export * from "./types/tari-indexer-client/NonFungibleSubstate"; +export * from "./types/tari-indexer-client/GetNonFungibleCountResponse"; +export * from "./types/tari-indexer-client/ListSubstatesRequest"; +export * from "./types/tari-indexer-client/IndexerGetIdentityResponse"; export * from "./types/tari-indexer-client/GetTemplateDefinitionRequest"; -export * from "./types/tari-indexer-client/ListSubstatesResponse"; -export * from "./types/tari-indexer-client/IndexerAddPeerResponse"; -export * from "./types/tari-indexer-client/InspectSubstateRequest"; -export * from "./types/tari-indexer-client/ListSubstateItem"; +export * from "./types/tari-indexer-client/IndexerGetSubstateRequest"; +export * from "./types/tari-indexer-client/GetTemplateDefinitionResponse"; +export * from "./types/tari-indexer-client/ListTemplatesRequest"; export * from "./types/tari-indexer-client/IndexerGetAllVnsResponse"; -export * from "./types/tari-indexer-client/ListSubstatesRequest"; -export * from "./types/tari-indexer-client/GetRelatedTransactionsResponse"; -export * from "./types/tari-indexer-client/IndexerTransactionFinalizedResult"; +export * from "./types/tari-indexer-client/GetNonFungiblesRequest"; export * from "./types/tari-indexer-client/IndexerGetSubstateResponse"; +export * from "./types/tari-indexer-client/IndexerSubmitTransactionRequest"; +export * from "./types/tari-indexer-client/IndexerConnection"; +export * from "./types/tari-indexer-client/InspectSubstateResponse"; export * from "./types/tari-indexer-client/IndexerGetAllVnsRequest"; +export * from "./types/tari-indexer-client/InspectSubstateRequest"; export * from "./types/tari-indexer-client/IndexerGetCommsStatsResponse"; -export * from "./types/tari-indexer-client/IndexerConnection"; -export * from "./types/tari-indexer-client/GetNonFungiblesRequest"; -export * from "./types/tari-indexer-client/IndexerAddPeerRequest"; -export * from "./types/tari-indexer-client/GetRelatedTransactionsRequest"; -export * from "./types/tari-indexer-client/GetNonFungiblesResponse"; -export * from "./types/tari-indexer-client/GetTemplateDefinitionResponse"; +export * from "./types/tari-indexer-client/GetNonFungibleCountRequest"; +export * from "./types/tari-indexer-client/GetRelatedTransactionsResponse"; +export * from "./types/tari-indexer-client/IndexerAddPeerResponse"; export * from "./types/tari-indexer-client/IndexerGetConnectionsResponse"; -export * from "./types/tari-indexer-client/IndexerSubmitTransactionRequest"; +export * from "./types/tari-indexer-client/GetNonFungiblesResponse"; +export * from "./types/tari-indexer-client/GetRelatedTransactionsRequest"; +export * from "./types/tari-indexer-client/IndexerGetTransactionResultResponse"; export * from "./types/tari-indexer-client/ListTemplatesResponse"; -export * from "./types/tari-indexer-client/InspectSubstateResponse"; +export * from "./types/tari-indexer-client/IndexerGetTransactionResultRequest"; +export * from "./types/tari-indexer-client/ListSubstatesResponse"; +export * from "./types/tari-indexer-client/IndexerTransactionFinalizedResult"; export * from "./types/tari-indexer-client/GetNonFungibleCollectionsResponse"; -export * from "./types/tari-indexer-client/IndexerGetSubstateRequest"; -export * from "./types/tari-indexer-client/IndexerGetEpochManagerStatsResponse"; -export * from "./types/tari-indexer-client/NonFungibleSubstate"; -export * from "./types/tari-indexer-client/GetNonFungibleCountResponse"; -export * from "./types/tari-indexer-client/ListTemplatesRequest"; -export * from "./types/tari-indexer-client/IndexerGetTransactionResultResponse"; +export * from "./types/tari-indexer-client/IndexerSubmitTransactionResponse"; export * from "./types/tari-indexer-client/IndexerConnectionDirection"; -export * from "./types/tari-indexer-client/IndexerGetIdentityResponse"; -export * from "./types/tari-indexer-client/IndexerGetTransactionResultRequest"; -export * from "./types/tari-indexer-client/GetNonFungibleCountRequest"; +export * from "./types/tari-indexer-client/ListSubstateItem"; diff --git a/bindings/src/types/Block.ts b/bindings/src/types/Block.ts index 9495e6faa7..ef612eb0a7 100644 --- a/bindings/src/types/Block.ts +++ b/bindings/src/types/Block.ts @@ -1,6 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Command } from "./Command"; import type { Epoch } from "./Epoch"; +import type { ExtraData } from "./ExtraData"; import type { NodeHeight } from "./NodeHeight"; import type { QuorumCertificate } from "./QuorumCertificate"; import type { Shard } from "./Shard"; @@ -28,4 +29,5 @@ export interface Block { timestamp: number; base_layer_block_height: number; base_layer_block_hash: string; + extra_data: ExtraData | null; } diff --git a/bindings/src/types/ExtraData.ts b/bindings/src/types/ExtraData.ts new file mode 100644 index 0000000000..7854af3abc --- /dev/null +++ b/bindings/src/types/ExtraData.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExtraData = string; diff --git a/bindings/src/validator-node-client.ts b/bindings/src/validator-node-client.ts index 37a2d665c0..989140390d 100644 --- a/bindings/src/validator-node-client.ts +++ b/bindings/src/validator-node-client.ts @@ -1,59 +1,59 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +export * from "./types/validator-node-client/GetCommitteeRequest"; +export * from "./types/validator-node-client/GetRecentTransactionsResponse"; +export * from "./types/validator-node-client/GetRecentTransactionsRequest"; +export * from "./types/validator-node-client/GetTemplatesRequest"; +export * from "./types/validator-node-client/GetBlocksCountResponse"; +export * from "./types/validator-node-client/VNAddPeerRequest"; +export * from "./types/validator-node-client/GetCommitteeResponse"; export * from "./types/validator-node-client/VNSubmitTransactionRequest"; +export * from "./types/validator-node-client/VNConnectionDirection"; +export * from "./types/validator-node-client/VNAddPeerResponse"; +export * from "./types/validator-node-client/GetSubstatesByTransactionResponse"; +export * from "./types/validator-node-client/VNCommitteeShardInfo"; +export * from "./types/validator-node-client/VNFunctionDef"; +export * from "./types/validator-node-client/VNGetValidatorFeesResponse"; +export * from "./types/validator-node-client/VNGetAllVnsResponse"; +export * from "./types/validator-node-client/TemplateAbi"; +export * from "./types/validator-node-client/GetMempoolStatsResponse"; export * from "./types/validator-node-client/TemplateMetadata"; -export * from "./types/validator-node-client/GetFilteredBlocksCountRequest"; +export * from "./types/validator-node-client/GetBlockResponse"; +export * from "./types/validator-node-client/VNLogLevel"; +export * from "./types/validator-node-client/VNGetAllVnsRequest"; export * from "./types/validator-node-client/VNLogEntry"; export * from "./types/validator-node-client/GetShardKeyRequest"; -export * from "./types/validator-node-client/VNGetTransactionResultResponse"; +export * from "./types/validator-node-client/GetBlockRequest"; export * from "./types/validator-node-client/VNSubmitTransactionResponse"; +export * from "./types/validator-node-client/DryRunTransactionFinalizeResult"; +export * from "./types/validator-node-client/GetTransactionResponse"; +export * from "./types/validator-node-client/GetStateResponse"; +export * from "./types/validator-node-client/VNGetSubstateRequest"; +export * from "./types/validator-node-client/VNGetSubstateResponse"; +export * from "./types/validator-node-client/ListBlocksResponse"; +export * from "./types/validator-node-client/SubstateStatus"; +export * from "./types/validator-node-client/ValidatorNode"; +export * from "./types/validator-node-client/GetFilteredBlocksCountRequest"; +export * from "./types/validator-node-client/ListBlocksRequest"; +export * from "./types/validator-node-client/GetEpochManagerStatsResponse"; export * from "./types/validator-node-client/GetTxPoolResponse"; +export * from "./types/validator-node-client/VNConnection"; export * from "./types/validator-node-client/GetTemplateResponse"; -export * from "./types/validator-node-client/GetBlocksCountResponse"; -export * from "./types/validator-node-client/VNGetValidatorFeesRequest"; -export * from "./types/validator-node-client/GetSubstatesByTransactionResponse"; -export * from "./types/validator-node-client/VNConnectionDirection"; -export * from "./types/validator-node-client/VNAddPeerRequest"; -export * from "./types/validator-node-client/GetTemplatesResponse"; -export * from "./types/validator-node-client/GetTransactionRequest"; +export * from "./types/validator-node-client/ValidatorFee"; +export * from "./types/validator-node-client/VNGetConnectionsResponse"; +export * from "./types/validator-node-client/VNGetCommsStatsResponse"; +export * from "./types/validator-node-client/GetTemplateRequest"; export * from "./types/validator-node-client/GetStateRequest"; -export * from "./types/validator-node-client/VNGetIdentityResponse"; -export * from "./types/validator-node-client/VNFunctionDef"; export * from "./types/validator-node-client/GetBlocksRequest"; -export * from "./types/validator-node-client/GetTemplateRequest"; +export * from "./types/validator-node-client/VNGetTransactionResultRequest"; +export * from "./types/validator-node-client/GetTransactionRequest"; +export * from "./types/validator-node-client/GetTemplatesResponse"; +export * from "./types/validator-node-client/VNGetTransactionResultResponse"; +export * from "./types/validator-node-client/VNArgDef"; export * from "./types/validator-node-client/GetShardKeyResponse"; -export * from "./types/validator-node-client/GetSubstatesByTransactionRequest"; -export * from "./types/validator-node-client/VNGetCommsStatsResponse"; -export * from "./types/validator-node-client/GetTransactionResponse"; -export * from "./types/validator-node-client/GetCommitteeResponse"; -export * from "./types/validator-node-client/VNGetAllVnsRequest"; -export * from "./types/validator-node-client/GetCommitteeRequest"; -export * from "./types/validator-node-client/DryRunTransactionFinalizeResult"; -export * from "./types/validator-node-client/VNGetValidatorFeesResponse"; -export * from "./types/validator-node-client/GetStateResponse"; -export * from "./types/validator-node-client/SubstateStatus"; export * from "./types/validator-node-client/GetBlocksResponse"; -export * from "./types/validator-node-client/ListBlocksResponse"; -export * from "./types/validator-node-client/GetBlockRequest"; -export * from "./types/validator-node-client/VNGetTransactionResultRequest"; -export * from "./types/validator-node-client/GetRecentTransactionsRequest"; -export * from "./types/validator-node-client/ValidatorFee"; -export * from "./types/validator-node-client/VNLogLevel"; -export * from "./types/validator-node-client/GetRecentTransactionsResponse"; -export * from "./types/validator-node-client/VNAddPeerResponse"; -export * from "./types/validator-node-client/VNConnection"; -export * from "./types/validator-node-client/ValidatorNode"; -export * from "./types/validator-node-client/VNGetConnectionsResponse"; -export * from "./types/validator-node-client/ListBlocksRequest"; +export * from "./types/validator-node-client/VNGetIdentityResponse"; +export * from "./types/validator-node-client/GetSubstatesByTransactionRequest"; export * from "./types/validator-node-client/GetNetworkCommitteeResponse"; -export * from "./types/validator-node-client/VNGetSubstateRequest"; -export * from "./types/validator-node-client/GetMempoolStatsResponse"; -export * from "./types/validator-node-client/TemplateAbi"; -export * from "./types/validator-node-client/GetTemplatesRequest"; -export * from "./types/validator-node-client/VNGetSubstateResponse"; -export * from "./types/validator-node-client/VNGetAllVnsResponse"; -export * from "./types/validator-node-client/VNArgDef"; -export * from "./types/validator-node-client/GetEpochManagerStatsResponse"; -export * from "./types/validator-node-client/GetBlockResponse"; -export * from "./types/validator-node-client/VNCommitteeShardInfo"; +export * from "./types/validator-node-client/VNGetValidatorFeesRequest"; diff --git a/bindings/src/wallet-daemon-client.ts b/bindings/src/wallet-daemon-client.ts index 140f651419..d23e8f6cd8 100644 --- a/bindings/src/wallet-daemon-client.ts +++ b/bindings/src/wallet-daemon-client.ts @@ -1,90 +1,90 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -export * from "./types/wallet-daemon-client/WalletSubstateRecord"; -export * from "./types/wallet-daemon-client/ProofsFinalizeResponse"; -export * from "./types/wallet-daemon-client/CallInstructionRequest"; -export * from "./types/wallet-daemon-client/AuthLoginAcceptResponse"; -export * from "./types/wallet-daemon-client/AccountsListRequest"; -export * from "./types/wallet-daemon-client/AuthGetAllJwtResponse"; -export * from "./types/wallet-daemon-client/AuthLoginResponse"; -export * from "./types/wallet-daemon-client/GetValidatorFeesRequest"; -export * from "./types/wallet-daemon-client/AccountGetDefaultRequest"; -export * from "./types/wallet-daemon-client/AuthGetAllJwtRequest"; -export * from "./types/wallet-daemon-client/KeyBranch"; -export * from "./types/wallet-daemon-client/AccountGetResponse"; -export * from "./types/wallet-daemon-client/SettingsSetRequest"; -export * from "./types/wallet-daemon-client/GetValidatorFeesResponse"; -export * from "./types/wallet-daemon-client/TransactionGetAllRequest"; +export * from "./types/wallet-daemon-client/AccountsListResponse"; export * from "./types/wallet-daemon-client/RevealFundsRequest"; -export * from "./types/wallet-daemon-client/AuthLoginDenyResponse"; -export * from "./types/wallet-daemon-client/AuthLoginDenyRequest"; -export * from "./types/wallet-daemon-client/TransactionSubmitResponse"; -export * from "./types/wallet-daemon-client/ProofsGenerateResponse"; -export * from "./types/wallet-daemon-client/ClaimValidatorFeesRequest"; -export * from "./types/wallet-daemon-client/AuthLoginRequest"; +export * from "./types/wallet-daemon-client/ClaimBurnResponse"; +export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; +export * from "./types/wallet-daemon-client/WalletSubstateRecord"; +export * from "./types/wallet-daemon-client/AccountsGetBalancesResponse"; +export * from "./types/wallet-daemon-client/SubstatesGetResponse"; export * from "./types/wallet-daemon-client/ListAccountNftRequest"; +export * from "./types/wallet-daemon-client/RevealFundsResponse"; +export * from "./types/wallet-daemon-client/AccountSetDefaultRequest"; +export * from "./types/wallet-daemon-client/GetAccountNftRequest"; +export * from "./types/wallet-daemon-client/TransactionGetAllResponse"; export * from "./types/wallet-daemon-client/TransactionGetRequest"; -export * from "./types/wallet-daemon-client/AuthRevokeTokenResponse"; -export * from "./types/wallet-daemon-client/ProofsFinalizeRequest"; -export * from "./types/wallet-daemon-client/AccountsTransferRequest"; -export * from "./types/wallet-daemon-client/SettingsSetResponse"; -export * from "./types/wallet-daemon-client/KeysListRequest"; +export * from "./types/wallet-daemon-client/KeyBranch"; +export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; +export * from "./types/wallet-daemon-client/AccountSetDefaultResponse"; +export * from "./types/wallet-daemon-client/SubstatesListResponse"; +export * from "./types/wallet-daemon-client/AccountGetDefaultRequest"; +export * from "./types/wallet-daemon-client/KeysListResponse"; +export * from "./types/wallet-daemon-client/ProofsGenerateResponse"; +export * from "./types/wallet-daemon-client/TransactionGetAllRequest"; export * from "./types/wallet-daemon-client/AccountsTransferResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResultResponse"; +export * from "./types/wallet-daemon-client/AuthLoginResponse"; export * from "./types/wallet-daemon-client/ClaimBurnRequest"; -export * from "./types/wallet-daemon-client/KeysListResponse"; +export * from "./types/wallet-daemon-client/BalanceEntry"; +export * from "./types/wallet-daemon-client/KeysListRequest"; export * from "./types/wallet-daemon-client/SubstatesGetRequest"; -export * from "./types/wallet-daemon-client/TransactionSubmitRequest"; -export * from "./types/wallet-daemon-client/AccountsGetBalancesResponse"; -export * from "./types/wallet-daemon-client/TransactionGetAllResponse"; -export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsResponse"; -export * from "./types/wallet-daemon-client/ConfidentialTransferResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResponse"; -export * from "./types/wallet-daemon-client/SubstatesGetResponse"; -export * from "./types/wallet-daemon-client/GetAccountNftRequest"; +export * from "./types/wallet-daemon-client/ProofsFinalizeResponse"; export * from "./types/wallet-daemon-client/AuthRevokeTokenRequest"; -export * from "./types/wallet-daemon-client/ProofsGenerateRequest"; -export * from "./types/wallet-daemon-client/RevealFundsResponse"; -export * from "./types/wallet-daemon-client/AccountsInvokeResponse"; -export * from "./types/wallet-daemon-client/AccountsListResponse"; +export * from "./types/wallet-daemon-client/TransactionWaitResultRequest"; +export * from "./types/wallet-daemon-client/TransactionSubmitResponse"; +export * from "./types/wallet-daemon-client/AccountsCreateRequest"; +export * from "./types/wallet-daemon-client/CallInstructionRequest"; +export * from "./types/wallet-daemon-client/WebRtcStartRequest"; export * from "./types/wallet-daemon-client/WebRtcStartResponse"; -export * from "./types/wallet-daemon-client/TransactionGetResultRequest"; +export * from "./types/wallet-daemon-client/MintAccountNftResponse"; export * from "./types/wallet-daemon-client/ProofsCancelResponse"; -export * from "./types/wallet-daemon-client/AccountsCreateRequest"; -export * from "./types/wallet-daemon-client/MintAccountNftRequest"; -export * from "./types/wallet-daemon-client/TransactionClaimBurnResponse"; -export * from "./types/wallet-daemon-client/TransactionWaitResultRequest"; -export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/AccountGetResponse"; +export * from "./types/wallet-daemon-client/ClaimValidatorFeesRequest"; +export * from "./types/wallet-daemon-client/AuthRevokeTokenResponse"; +export * from "./types/wallet-daemon-client/AccountsInvokeRequest"; +export * from "./types/wallet-daemon-client/WebRtcStart"; +export * from "./types/wallet-daemon-client/TransactionWaitResultResponse"; +export * from "./types/wallet-daemon-client/GetValidatorFeesRequest"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofRequest"; -export * from "./types/wallet-daemon-client/KeysCreateRequest"; +export * from "./types/wallet-daemon-client/KeysCreateResponse"; +export * from "./types/wallet-daemon-client/AuthLoginDenyRequest"; +export * from "./types/wallet-daemon-client/ProofsFinalizeRequest"; +export * from "./types/wallet-daemon-client/ProofsCancelRequest"; export * from "./types/wallet-daemon-client/SettingsGetResponse"; -export * from "./types/wallet-daemon-client/ClaimBurnResponse"; -export * from "./types/wallet-daemon-client/WebRtcStartRequest"; -export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; -export * from "./types/wallet-daemon-client/WebRtcStart"; -export * from "./types/wallet-daemon-client/AccountSetDefaultRequest"; +export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; +export * from "./types/wallet-daemon-client/GetValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/TransactionGetResponse"; +export * from "./types/wallet-daemon-client/TransactionSubmitRequest"; +export * from "./types/wallet-daemon-client/ListAccountNftResponse"; +export * from "./types/wallet-daemon-client/AccountsInvokeResponse"; +export * from "./types/wallet-daemon-client/AccountsListRequest"; +export * from "./types/wallet-daemon-client/AuthLoginAcceptResponse"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofResponse"; +export * from "./types/wallet-daemon-client/TemplatesGetRequest"; +export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; +export * from "./types/wallet-daemon-client/SettingsSetRequest"; +export * from "./types/wallet-daemon-client/MintAccountNftRequest"; +export * from "./types/wallet-daemon-client/TransactionClaimBurnResponse"; +export * from "./types/wallet-daemon-client/AuthGetAllJwtRequest"; +export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; +export * from "./types/wallet-daemon-client/ConfidentialTransferResponse"; export * from "./types/wallet-daemon-client/TemplatesGetResponse"; -export * from "./types/wallet-daemon-client/ListAccountNftResponse"; -export * from "./types/wallet-daemon-client/AccountsGetBalancesRequest"; -export * from "./types/wallet-daemon-client/MintAccountNftResponse"; +export * from "./types/wallet-daemon-client/AccountsTransferRequest"; +export * from "./types/wallet-daemon-client/AuthLoginRequest"; +export * from "./types/wallet-daemon-client/AuthGetAllJwtResponse"; export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsRequest"; -export * from "./types/wallet-daemon-client/KeysCreateResponse"; +export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; +export * from "./types/wallet-daemon-client/SettingsSetResponse"; +export * from "./types/wallet-daemon-client/AccountsCreateFreeTestCoinsResponse"; +export * from "./types/wallet-daemon-client/AccountGetRequest"; +export * from "./types/wallet-daemon-client/KeysCreateRequest"; +export * from "./types/wallet-daemon-client/ProofsGenerateRequest"; +export * from "./types/wallet-daemon-client/TransactionGetResultResponse"; +export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; +export * from "./types/wallet-daemon-client/AuthLoginDenyResponse"; +export * from "./types/wallet-daemon-client/TransactionGetResultRequest"; +export * from "./types/wallet-daemon-client/AccountsCreateResponse"; +export * from "./types/wallet-daemon-client/AccountsGetBalancesRequest"; export * from "./types/wallet-daemon-client/AccountInfo"; export * from "./types/wallet-daemon-client/SubstatesListRequest"; -export * from "./types/wallet-daemon-client/BalanceEntry"; -export * from "./types/wallet-daemon-client/TemplatesGetRequest"; -export * from "./types/wallet-daemon-client/AccountsInvokeRequest"; -export * from "./types/wallet-daemon-client/ProofsCancelRequest"; -export * from "./types/wallet-daemon-client/AccountSetDefaultResponse"; -export * from "./types/wallet-daemon-client/SubstatesListResponse"; -export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; export * from "./types/wallet-daemon-client/ComponentAddressOrName"; -export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; -export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; -export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; -export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; -export * from "./types/wallet-daemon-client/AccountsCreateResponse"; -export * from "./types/wallet-daemon-client/TransactionWaitResultResponse"; -export * from "./types/wallet-daemon-client/AccountGetRequest"; diff --git a/dan_layer/common_types/src/bytes.rs b/dan_layer/common_types/src/bytes.rs new file mode 100644 index 0000000000..e6e5b374ba --- /dev/null +++ b/dan_layer/common_types/src/bytes.rs @@ -0,0 +1,94 @@ +// Copyright 2024, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{cmp, convert::TryFrom, ops::Deref}; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default, Deserialize, Serialize)] +pub struct MaxSizeBytes { + inner: Vec, +} + +impl MaxSizeBytes { + pub fn into_vec(self) -> Vec { + self.inner + } + + pub fn from_bytes_checked>(bytes: T) -> Option { + let b = bytes.as_ref(); + if b.len() > MAX { + None + } else { + Some(Self { inner: b.to_vec() }) + } + } + + pub fn from_bytes_truncate>(bytes: T) -> Self { + let b = bytes.as_ref(); + let len = cmp::min(b.len(), MAX); + Self { + inner: b[..len].to_vec(), + } + } +} + +impl From> for Vec { + fn from(value: MaxSizeBytes) -> Self { + value.inner + } +} + +impl TryFrom> for MaxSizeBytes { + type Error = MaxSizeBytesError; + + fn try_from(value: Vec) -> Result { + if value.len() > MAX { + Err(MaxSizeBytesError::MaxSizeBytesLengthError { + expected: MAX, + actual: value.len(), + }) + } else { + Ok(MaxSizeBytes { inner: value }) + } + } +} + +impl AsRef<[u8]> for MaxSizeBytes { + fn as_ref(&self) -> &[u8] { + &self.inner + } +} + +impl Deref for MaxSizeBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[derive(Debug, thiserror::Error)] +pub enum MaxSizeBytesError { + #[error("Invalid Bytes length: expected {expected}, got {actual}")] + MaxSizeBytesLengthError { expected: usize, actual: usize }, +} diff --git a/dan_layer/common_types/src/extra_data.rs b/dan_layer/common_types/src/extra_data.rs new file mode 100644 index 0000000000..9afe71bec2 --- /dev/null +++ b/dan_layer/common_types/src/extra_data.rs @@ -0,0 +1,69 @@ +// Copyright 2024. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; +use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::ByteArray}; +#[cfg(feature = "ts")] +use ts_rs::TS; + +use crate::{MaxSizeBytes, MaxSizeBytesError}; + +const MAX_DATA_SIZE: usize = 256; +type ExtraFieldValue = MaxSizeBytes; + +#[repr(u8)] +#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] +pub enum ExtraFieldKey { + SidechainId = 0x00, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(feature = "ts", derive(TS), ts(export, export_to = "../../bindings/src/types/"))] +pub struct ExtraData(#[cfg_attr(feature = "ts", ts(type = "string"))] BTreeMap); + +impl ExtraData { + pub const fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn insert>(&mut self, key: ExtraFieldKey, value: V) -> &mut Self { + let value = value.into(); + self.0.insert(key, value); + self + } + + pub fn insert_sidechain_id(&mut self, sidechain_id: RistrettoPublicKey) -> Result<&mut Self, MaxSizeBytesError> { + self.0 + .insert(ExtraFieldKey::SidechainId, sidechain_id.as_bytes().to_vec().try_into()?); + Ok(self) + } + + pub fn get(&self, key: &ExtraFieldKey) -> Option<&ExtraFieldValue> { + self.0.get(key) + } + + pub fn contains_key(&self, key: &ExtraFieldKey) -> bool { + self.0.contains_key(key) + } +} diff --git a/dan_layer/common_types/src/lib.rs b/dan_layer/common_types/src/lib.rs index 688249ba02..2437144a6c 100644 --- a/dan_layer/common_types/src/lib.rs +++ b/dan_layer/common_types/src/lib.rs @@ -1,12 +1,18 @@ // Copyright 2022 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +mod bytes; +pub use bytes::{MaxSizeBytes, MaxSizeBytesError}; + pub mod crypto; mod epoch; pub use epoch::Epoch; +mod extra_data; +pub use extra_data::{ExtraData, ExtraFieldKey}; + pub mod committee; pub mod hasher; pub mod hashing; diff --git a/dan_layer/consensus/src/block_validations.rs b/dan_layer/consensus/src/block_validations.rs index 88ea5f1acc..506682bb91 100644 --- a/dan_layer/consensus/src/block_validations.rs +++ b/dan_layer/consensus/src/block_validations.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: BSD-3-Clause use tari_common::configuration::Network; -use tari_dan_common_types::{committee::Committee, DerivableFromPublicKey}; +use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::ByteArray}; +use tari_dan_common_types::{committee::Committee, DerivableFromPublicKey, ExtraFieldKey}; use tari_dan_storage::consensus_models::Block; use tari_epoch_manager::EpochManagerReader; @@ -23,6 +24,7 @@ pub async fn check_proposal( // tip. Without this, the check has a race condition between the base layer scanner and consensus. // check_base_layer_block_hash::(block, epoch_manager, config).await?; check_network(block, config.network)?; + check_sidechain_id(block, config)?; check_hash_and_height(block)?; let committee_for_block = epoch_manager .get_committee_by_validator_public_key(block.epoch(), block.proposed_by()) @@ -218,3 +220,46 @@ pub async fn check_quorum_certificate( } Ok(()) } + +pub fn check_sidechain_id(candidate_block: &Block, config: &HotstuffConfig) -> Result<(), HotStuffError> { + // We only require the sidechain id on the genesis block + if !candidate_block.is_genesis() { + return Ok(()); + } + + // If we are using a sidechain id in the network, we need to check it matches the candidate block one + if let Some(expected_sidechain_id) = &config.sidechain_id { + // Extract the sidechain id from the candidate block + let extra_data = candidate_block.extra_data().ok_or::( + ProposalValidationError::MissingSidechainId { + block_id: *candidate_block.id(), + } + .into(), + )?; + let sidechain_id_bytes = extra_data.get(&ExtraFieldKey::SidechainId).ok_or::( + ProposalValidationError::InvalidSidechainId { + block_id: *candidate_block.id(), + reason: "SidechainId key not present".to_owned(), + } + .into(), + )?; + let sidechain_id = RistrettoPublicKey::from_canonical_bytes(sidechain_id_bytes).map_err(|e| { + ProposalValidationError::InvalidSidechainId { + block_id: *candidate_block.id(), + reason: e.to_string(), + } + })?; + + // The sidechain id must match the sidechain of the current network + if sidechain_id != *expected_sidechain_id { + return Err(ProposalValidationError::MismatchedSidechainId { + block_id: *candidate_block.id(), + expected_sidechain_id: expected_sidechain_id.clone(), + sidechain_id, + } + .into()); + } + } + + Ok(()) +} diff --git a/dan_layer/consensus/src/hotstuff/config.rs b/dan_layer/consensus/src/hotstuff/config.rs index b51e78d12c..72c7d36912 100644 --- a/dan_layer/consensus/src/hotstuff/config.rs +++ b/dan_layer/consensus/src/hotstuff/config.rs @@ -4,6 +4,7 @@ use std::time::Duration; use tari_common::configuration::Network; +use tari_crypto::ristretto::RistrettoPublicKey; use tari_dan_common_types::NumPreshards; #[derive(Debug, Clone)] @@ -13,4 +14,5 @@ pub struct HotstuffConfig { pub max_base_layer_blocks_behind: u64, pub num_preshards: NumPreshards, pub pacemaker_max_base_time: Duration, + pub sidechain_id: Option, } diff --git a/dan_layer/consensus/src/hotstuff/error.rs b/dan_layer/consensus/src/hotstuff/error.rs index 717f0ddd92..2daa7b677b 100644 --- a/dan_layer/consensus/src/hotstuff/error.rs +++ b/dan_layer/consensus/src/hotstuff/error.rs @@ -2,9 +2,10 @@ // SPDX-License-Identifier: BSD-3-Clause use tari_common_types::types::FixedHash; +use tari_crypto::ristretto::RistrettoPublicKey; use tari_dan_common_types::{Epoch, NodeHeight, VersionedSubstateIdError}; use tari_dan_storage::{ - consensus_models::{BlockId, LeafBlock, LockedBlock, QuorumCertificate, TransactionPoolError}, + consensus_models::{BlockError, BlockId, LeafBlock, LockedBlock, QuorumCertificate, TransactionPoolError}, StorageError, }; use tari_epoch_manager::EpochManagerError; @@ -103,6 +104,8 @@ pub enum HotStuffError { foreign_block_id: BlockId, transaction_id: TransactionId, }, + #[error("Block building error: {0}")] + BlockBuildingError(#[from] BlockError), } impl From for HotStuffError { @@ -235,4 +238,17 @@ pub enum ProposalValidationError { node" )] NoTransactionsInCommittee { block_id: BlockId }, + #[error("Foreign node submitted an foreign proposal {block_id} that did not contain a sidechain ID")] + MissingSidechainId { block_id: BlockId }, + #[error("Foreign node submitted an foreign proposal {block_id} with an invalid sidechain ID: {reason}")] + InvalidSidechainId { block_id: BlockId, reason: String }, + #[error( + "Foreign node submitted an foreign proposal {block_id} with a mistmatched sidechain ID: expected \ + {expected_sidechain_id} but got {sidechain_id}" + )] + MismatchedSidechainId { + block_id: BlockId, + expected_sidechain_id: RistrettoPublicKey, + sidechain_id: RistrettoPublicKey, + }, } diff --git a/dan_layer/consensus/src/hotstuff/on_propose.rs b/dan_layer/consensus/src/hotstuff/on_propose.rs index 0bd6e32c91..81b581160b 100644 --- a/dan_layer/consensus/src/hotstuff/on_propose.rs +++ b/dan_layer/consensus/src/hotstuff/on_propose.rs @@ -587,6 +587,7 @@ where TConsensusSpec: ConsensusSpec EpochTime::now().as_u64(), base_layer_block_height, base_layer_block_hash, + None, ); let signature = self.signing_service.sign(next_block.id()); diff --git a/dan_layer/consensus/src/hotstuff/on_receive_local_proposal.rs b/dan_layer/consensus/src/hotstuff/on_receive_local_proposal.rs index d3c6e41597..91edbd4a9a 100644 --- a/dan_layer/consensus/src/hotstuff/on_receive_local_proposal.rs +++ b/dan_layer/consensus/src/hotstuff/on_receive_local_proposal.rs @@ -256,7 +256,12 @@ impl OnReceiveLocalProposalHandler HotstuffWorker { fn create_zero_block_if_required(&self, epoch: Epoch, shard_group: ShardGroup) -> Result<(), HotStuffError> { self.state_store.with_write_tx(|tx| { // The parent for genesis blocks refer to this zero block - let mut zero_block = Block::zero_block(self.config.network, self.config.num_preshards); + let mut zero_block = Block::zero_block( + self.config.network, + self.config.num_preshards, + self.config.sidechain_id.clone(), + )?; if !zero_block.exists(&**tx)? { debug!(target: LOG_TARGET, "Creating zero block"); zero_block.justify().insert(tx)?; @@ -746,7 +750,12 @@ impl HotstuffWorker { zero_block.commit_diff(tx, BlockDiff::empty(*zero_block.id()))?; } - let mut genesis = Block::genesis(self.config.network, epoch, shard_group); + let mut genesis = Block::genesis( + self.config.network, + epoch, + shard_group, + self.config.sidechain_id.clone(), + )?; if !genesis.exists(&**tx)? { info!(target: LOG_TARGET, "✨Creating genesis block {genesis}"); genesis.justify().insert(tx)?; diff --git a/dan_layer/consensus_tests/src/support/validator/builder.rs b/dan_layer/consensus_tests/src/support/validator/builder.rs index 866580c39a..696c508276 100644 --- a/dan_layer/consensus_tests/src/support/validator/builder.rs +++ b/dan_layer/consensus_tests/src/support/validator/builder.rs @@ -132,6 +132,7 @@ impl ValidatorBuilder { max_base_layer_blocks_behind: 5, network: Network::LocalNet, pacemaker_max_base_time: Duration::from_secs(10), + sidechain_id: None, }, self.address.clone(), inbound_messaging, diff --git a/dan_layer/p2p/proto/consensus.proto b/dan_layer/p2p/proto/consensus.proto index e60fd6021c..e533c2adba 100644 --- a/dan_layer/p2p/proto/consensus.proto +++ b/dan_layer/p2p/proto/consensus.proto @@ -98,6 +98,11 @@ message Block { uint64 base_layer_block_height = 14; bytes base_layer_block_hash = 15; bool is_dummy = 16; + ExtraData extra_data = 17; +} + +message ExtraData { + bytes encoded_extra_data = 1; } message LeaderFee { diff --git a/dan_layer/p2p/src/conversions/consensus.rs b/dan_layer/p2p/src/conversions/consensus.rs index 13bf6e5410..99237365fc 100644 --- a/dan_layer/p2p/src/conversions/consensus.rs +++ b/dan_layer/p2p/src/conversions/consensus.rs @@ -41,6 +41,7 @@ use tari_crypto::tari_utilities::ByteArray; use tari_dan_common_types::{ shard::Shard, Epoch, + ExtraData, NodeHeight, ShardGroup, SubstateLockType, @@ -461,6 +462,7 @@ impl From<&tari_dan_storage::consensus_models::Block> for proto::consensus::Bloc base_layer_block_height: value.base_layer_block_height(), base_layer_block_hash: value.base_layer_block_hash().as_bytes().to_vec(), is_dummy: value.is_dummy(), + extra_data: value.extra_data().map(Into::into), } } } @@ -483,6 +485,8 @@ impl TryFrom for tari_dan_storage::consensus_models::Bl .ok_or_else(|| anyhow!("Block conversion: QC not provided"))? .try_into()?; + let extra_data = value.extra_data.map(TryInto::try_into).transpose()?; + if value.is_dummy { Ok(Self::dummy_block( network, @@ -518,11 +522,30 @@ impl TryFrom for tari_dan_storage::consensus_models::Bl value.timestamp, value.base_layer_block_height, value.base_layer_block_hash.try_into()?, + extra_data, )) } } } +//---------------------------------- Evidence --------------------------------------------// + +impl From<&ExtraData> for proto::consensus::ExtraData { + fn from(value: &ExtraData) -> Self { + Self { + encoded_extra_data: encode(value).unwrap(), + } + } +} + +impl TryFrom for ExtraData { + type Error = anyhow::Error; + + fn try_from(value: proto::consensus::ExtraData) -> Result { + Ok(decode_exact(&value.encoded_extra_data)?) + } +} + //---------------------------------- Command --------------------------------------------// impl From<&Command> for proto::consensus::Command { diff --git a/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql b/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql index d905ef783d..c03eafb836 100644 --- a/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql +++ b/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql @@ -35,6 +35,7 @@ create table blocks timestamp bigint not NULL, base_layer_block_height bigint not NULL, base_layer_block_hash text not NULL, + extra_data text NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (qc_id) REFERENCES quorum_certificates (qc_id) ); @@ -63,6 +64,7 @@ create table parked_blocks base_layer_block_height bigint not NULL, base_layer_block_hash text not NULL, foreign_proposals text not NULL, + extra_data text NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -377,6 +379,7 @@ CREATE TABLE foreign_proposals proposed_in_block text NULL REFERENCES blocks (block_id), proposed_in_block_height bigint NULL, status text not NULL, + extra_data text NULL, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE (block_id) ); diff --git a/dan_layer/state_store_sqlite/src/schema.rs b/dan_layer/state_store_sqlite/src/schema.rs index d800e9eaf0..41c94f6b00 100644 --- a/dan_layer/state_store_sqlite/src/schema.rs +++ b/dan_layer/state_store_sqlite/src/schema.rs @@ -38,6 +38,7 @@ diesel::table! { timestamp -> BigInt, base_layer_block_height -> BigInt, base_layer_block_hash -> Text, + extra_data -> Nullable, created_at -> Timestamp, } } @@ -110,6 +111,7 @@ diesel::table! { proposed_in_block -> Nullable, proposed_in_block_height -> Nullable, status -> Text, + extra_data -> Nullable, created_at -> Timestamp, } } @@ -250,6 +252,7 @@ diesel::table! { base_layer_block_height -> BigInt, base_layer_block_hash -> Text, foreign_proposals -> Text, + extra_data -> Nullable, created_at -> Timestamp, } } diff --git a/dan_layer/state_store_sqlite/src/sql_models/block.rs b/dan_layer/state_store_sqlite/src/sql_models/block.rs index 346750565b..d03394ae4a 100644 --- a/dan_layer/state_store_sqlite/src/sql_models/block.rs +++ b/dan_layer/state_store_sqlite/src/sql_models/block.rs @@ -38,6 +38,7 @@ pub struct Block { pub timestamp: i64, pub base_layer_block_height: i64, pub base_layer_block_hash: String, + pub extra_data: Option, pub created_at: PrimitiveDateTime, } @@ -81,6 +82,7 @@ impl Block { self.timestamp as u64, self.base_layer_block_height as u64, deserialize_hex_try_from(&self.base_layer_block_hash)?, + self.extra_data.map(|val| deserialize_json(&val)).transpose()?, )) } } @@ -106,6 +108,7 @@ pub struct ParkedBlock { pub base_layer_block_height: i64, pub base_layer_block_hash: String, pub foreign_proposals: String, + pub extra_data: Option, pub created_at: PrimitiveDateTime, } @@ -151,6 +154,7 @@ impl TryFrom for (consensus_models::Block, Vec, pub proposed_in_block_height: Option, pub status: String, + pub extra_data: Option, pub created_at: PrimitiveDateTime, } @@ -84,6 +85,7 @@ impl ForeignProposal { self.timestamp as u64, self.base_layer_block_height as u64, deserialize_hex_try_from(&self.base_layer_block_hash)?, + self.extra_data.map(|val| deserialize_json(&val)).transpose()?, ); let status = consensus_models::ForeignProposalStatus::from_str(&self.status)?; diff --git a/dan_layer/state_store_sqlite/src/writer.rs b/dan_layer/state_store_sqlite/src/writer.rs index d8b0d6604f..77195f19ec 100644 --- a/dan_layer/state_store_sqlite/src/writer.rs +++ b/dan_layer/state_store_sqlite/src/writer.rs @@ -186,6 +186,7 @@ impl<'a, TAddr: NodeAddressable> SqliteStateStoreWriteTransaction<'a, TAddr> { parked_blocks::base_layer_block_height.eq(block.base_layer_block_height() as i64), parked_blocks::base_layer_block_hash.eq(serialize_hex(block.base_layer_block_hash())), parked_blocks::foreign_proposals.eq(serialize_json(foreign_proposals)?), + parked_blocks::extra_data.eq(block.extra_data().map(serialize_json).transpose()?), ); diesel::insert_into(parked_blocks::table) @@ -238,6 +239,7 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta blocks::timestamp.eq(block.timestamp() as i64), blocks::base_layer_block_height.eq(block.base_layer_block_height() as i64), blocks::base_layer_block_hash.eq(serialize_hex(block.base_layer_block_hash())), + blocks::extra_data.eq(block.extra_data().map(serialize_json).transpose()?), ); diesel::insert_into(blocks::table) @@ -570,6 +572,7 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta foreign_proposals::justify_qc_id.eq(serialize_hex(foreign_proposal.justify_qc().id())), foreign_proposals::block_pledge.eq(serialize_json(foreign_proposal.block_pledge())?), foreign_proposals::status.eq(ForeignProposalStatus::New.to_string()), + foreign_proposals::extra_data.eq(foreign_proposal.block().extra_data().map(serialize_json).transpose()?), ); diesel::insert_into(foreign_proposals::table) diff --git a/dan_layer/state_store_sqlite/tests/tests.rs b/dan_layer/state_store_sqlite/tests/tests.rs index 93ea0dda3f..7663f195b3 100644 --- a/dan_layer/state_store_sqlite/tests/tests.rs +++ b/dan_layer/state_store_sqlite/tests/tests.rs @@ -47,7 +47,7 @@ mod confirm_all_transitions { let atom3 = create_tx_atom(); let network = Default::default(); - let zero_block = Block::zero_block(network, NumPreshards::P64); + let zero_block = Block::zero_block(network, NumPreshards::P64, None).unwrap(); zero_block.insert(&mut tx).unwrap(); let block1 = Block::new( network, @@ -67,6 +67,7 @@ mod confirm_all_transitions { EpochTime::now().as_u64(), 0, FixedHash::zero(), + None, ); block1.insert(&mut tx).unwrap(); diff --git a/dan_layer/storage/src/consensus_models/block.rs b/dan_layer/storage/src/consensus_models/block.rs index 19dd95e313..f90de5e3c8 100644 --- a/dan_layer/storage/src/consensus_models/block.rs +++ b/dan_layer/storage/src/consensus_models/block.rs @@ -13,7 +13,7 @@ use log::*; use serde::{Deserialize, Serialize}; use tari_common::configuration::Network; use tari_common_types::types::{FixedHash, FixedHashSizeError, PublicKey}; -use tari_crypto::tari_utilities::epoch_time::EpochTime; +use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::epoch_time::EpochTime}; use tari_dan_common_types::{ committee::CommitteeInfo, hashing, @@ -21,6 +21,8 @@ use tari_dan_common_types::{ serde_with, shard::Shard, Epoch, + ExtraData, + MaxSizeBytesError, NodeAddressable, NodeHeight, NumPreshards, @@ -67,6 +69,12 @@ use crate::{ const LOG_TARGET: &str = "tari::dan::storage::consensus_models::block"; +#[derive(Debug, thiserror::Error)] +pub enum BlockError { + #[error("Extra data size error: {0}")] + ExtraDataSizeError(#[from] MaxSizeBytesError), +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "ts", derive(TS), ts(export, export_to = "../../bindings/src/types/"))] pub struct Block { @@ -113,6 +121,7 @@ pub struct Block { base_layer_block_height: u64, #[cfg_attr(feature = "ts", ts(type = "string"))] base_layer_block_hash: FixedHash, + extra_data: Option, } impl Block { @@ -133,6 +142,7 @@ impl Block { timestamp: u64, base_layer_block_height: u64, base_layer_block_hash: FixedHash, + extra_data: Option, ) -> Self { let mut block = Self { id: BlockId::zero(), @@ -156,6 +166,7 @@ impl Block { timestamp, base_layer_block_height, base_layer_block_hash, + extra_data, }; block.id = block.calculate_hash().into(); block @@ -184,6 +195,7 @@ impl Block { timestamp: u64, base_layer_block_height: u64, base_layer_block_hash: FixedHash, + extra_data: Option, ) -> Self { Self { id, @@ -207,11 +219,17 @@ impl Block { timestamp, base_layer_block_height, base_layer_block_hash, + extra_data, } } - pub fn genesis(network: Network, epoch: Epoch, shard_group: ShardGroup) -> Self { - Self::new( + pub fn genesis( + network: Network, + epoch: Epoch, + shard_group: ShardGroup, + sidechain_id: Option, + ) -> Result { + Ok(Self::new( network, BlockId::zero(), QuorumCertificate::genesis(epoch, shard_group), @@ -228,12 +246,17 @@ impl Block { 0, 0, FixedHash::zero(), - ) + Self::extra_data_from_sidechain_id(sidechain_id)?, + )) } /// This is the parent block for all genesis blocks. Its block ID is always zero. - pub fn zero_block(network: Network, num_preshards: NumPreshards) -> Self { - Self { + pub fn zero_block( + network: Network, + num_preshards: NumPreshards, + sidechain_id: Option, + ) -> Result { + Ok(Self { network, id: BlockId::zero(), parent: BlockId::zero(), @@ -255,7 +278,8 @@ impl Block { timestamp: EpochTime::now().as_u64(), base_layer_block_height: 0, base_layer_block_hash: FixedHash::zero(), - } + extra_data: Self::extra_data_from_sidechain_id(sidechain_id)?, + }) } pub fn dummy_block( @@ -293,12 +317,20 @@ impl Block { timestamp: parent_timestamp, base_layer_block_height: parent_base_layer_block_height, base_layer_block_hash: parent_base_layer_block_hash, + extra_data: None, }; block.id = block.calculate_hash().into(); block.is_justified = false; block } + fn extra_data_from_sidechain_id(sidechain_id: Option) -> Result, BlockError> { + let extra_data = sidechain_id + .map(|id| ExtraData::new().insert_sidechain_id(id).cloned()) + .transpose()?; + Ok(extra_data) + } + pub fn calculate_hash(&self) -> FixedHash { // Hash is created from the hash of the "body" and // then hashed with the parent, so that you can @@ -329,6 +361,7 @@ impl Block { .chain(&self.timestamp) .chain(&self.base_layer_block_height) .chain(&self.base_layer_block_hash) + .chain(&self.extra_data) .result(); hashing::block_hasher().chain(&self.parent).chain(&inner_hash).result() @@ -538,6 +571,10 @@ impl Block { pub fn base_layer_block_hash(&self) -> &FixedHash { &self.base_layer_block_hash } + + pub fn extra_data(&self) -> Option<&ExtraData> { + self.extra_data.as_ref() + } } impl Block { diff --git a/dan_layer/storage/src/error.rs b/dan_layer/storage/src/error.rs index 4e50058bd0..fb9fc003f8 100644 --- a/dan_layer/storage/src/error.rs +++ b/dan_layer/storage/src/error.rs @@ -23,6 +23,8 @@ use tari_common_types::types::FixedHashSizeError; use tari_dan_common_types::optional::IsNotFoundError; +use crate::consensus_models::BlockError; + #[derive(Debug, thiserror::Error)] pub enum StorageError { #[error("Could not connect to storage:{reason}")] @@ -60,6 +62,8 @@ pub enum StorageError { DataInconsistency { details: String }, #[error("General storage error: {details}")] General { details: String }, + #[error("Block creation error: {0}")] + BlockError(#[from] BlockError), } impl IsNotFoundError for StorageError { From 183c90f834897dcf74d04ef321fa3c48fa3c1f93 Mon Sep 17 00:00:00 2001 From: dkf Date: Tue, 10 Sep 2024 11:35:39 +0100 Subject: [PATCH 2/6] test(cucumber): enable counter template scenario (#1136) --- integration_tests/src/helpers.rs | 19 +++ integration_tests/src/indexer.rs | 18 +-- integration_tests/src/wallet_daemon_cli.rs | 108 +++++++++++++++++- integration_tests/tests/cucumber.rs | 70 ++++++++++++ .../tests/features/counter.feature | 50 ++++++++ .../tests/features/counter.feature.ignore | 56 --------- 6 files changed, 243 insertions(+), 78 deletions(-) create mode 100644 integration_tests/tests/features/counter.feature delete mode 100644 integration_tests/tests/features/counter.feature.ignore diff --git a/integration_tests/src/helpers.rs b/integration_tests/src/helpers.rs index 890deea812..9b1c2b26c8 100644 --- a/integration_tests/src/helpers.rs +++ b/integration_tests/src/helpers.rs @@ -7,8 +7,11 @@ use std::{ time::Duration, }; +use tari_engine_types::substate::SubstateId; use tokio::{io::AsyncWriteExt, task::JoinHandle}; +use crate::TariWorld; + pub fn get_os_assigned_port() -> u16 { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); listener.local_addr().unwrap().port() @@ -117,3 +120,19 @@ pub async fn check_join_handle( }, } } + +pub fn get_address_from_output(world: &TariWorld, output_ref: String) -> &SubstateId { + world + .outputs + .iter() + .find_map(|(parent_name, outputs)| { + outputs + .iter() + .find(|(child_name, _)| { + let fqn = format!("{}/{}", parent_name, child_name); + fqn == output_ref + }) + .map(|(_, addr)| &addr.substate_id) + }) + .unwrap_or_else(|| panic!("Output not found: {}", output_ref)) +} diff --git a/integration_tests/src/indexer.rs b/integration_tests/src/indexer.rs index 7b3166fb47..1563aaa0b0 100644 --- a/integration_tests/src/indexer.rs +++ b/integration_tests/src/indexer.rs @@ -49,7 +49,7 @@ use tari_template_lib::models::ObjectKey; use tokio::task; use crate::{ - helpers::{check_join_handle, get_os_assigned_ports, wait_listener_on_local_port}, + helpers::{check_join_handle, get_address_from_output, get_os_assigned_ports, wait_listener_on_local_port}, logging::get_base_dir_for_scenario, TariWorld, }; @@ -142,22 +142,6 @@ impl IndexerProcess { } } -fn get_address_from_output(world: &TariWorld, output_ref: String) -> &SubstateId { - world - .outputs - .iter() - .find_map(|(name, outputs)| { - outputs - .iter() - .find(|(child_name, _)| { - let fqn = format!("{}/{}", name, child_name); - fqn == output_ref - }) - .map(|(_, addr)| &addr.substate_id) - }) - .unwrap() -} - pub async fn spawn_indexer(world: &mut TariWorld, indexer_name: String, base_node_name: String) { // each spawned indexer will use different ports let (port, json_rpc_port) = get_os_assigned_ports(); diff --git a/integration_tests/src/wallet_daemon_cli.rs b/integration_tests/src/wallet_daemon_cli.rs index f272906b9b..7250959d4b 100644 --- a/integration_tests/src/wallet_daemon_cli.rs +++ b/integration_tests/src/wallet_daemon_cli.rs @@ -30,7 +30,7 @@ use tari_crypto::{ tari_utilities::ByteArray, }; use tari_dan_common_types::{Epoch, SubstateRequirement}; -use tari_dan_wallet_sdk::apis::confidential_transfer::ConfidentialTransferInputSelection; +use tari_dan_wallet_sdk::{apis::confidential_transfer::ConfidentialTransferInputSelection, models::Account}; use tari_engine_types::instruction::Instruction; use tari_template_lib::{ args, @@ -39,7 +39,7 @@ use tari_template_lib::{ prelude::{ComponentAddress, ResourceAddress}, resource::TOKEN_SYMBOL, }; -use tari_transaction::Transaction; +use tari_transaction::{Transaction, UnsignedTransaction}; use tari_transaction_manifest::{parse_manifest, ManifestValue}; use tari_validator_node_cli::command::transaction::CliArg; use tari_wallet_daemon_client::{ @@ -67,7 +67,7 @@ use tari_wallet_daemon_client::{ }; use tokio::time::timeout; -use crate::{validator_node_cli::add_substate_ids, TariWorld}; +use crate::{helpers::get_address_from_output, validator_node_cli::add_substate_ids, TariWorld}; pub async fn claim_burn( world: &mut TariWorld, @@ -146,7 +146,6 @@ pub async fn reveal_burned_funds(world: &mut TariWorld, account_name: String, am assert!(wait_resp.result.unwrap().result.is_accept()); } -#[allow(clippy::too_many_arguments)] pub async fn transfer_confidential( world: &mut TariWorld, source_account_name: String, @@ -605,7 +604,12 @@ pub async fn create_component( let template_address = world .templates .get(&template_name) - .unwrap_or_else(|| panic!("Template not found with name {}", template_name)) + .unwrap_or_else(|| { + panic!( + "Create component failed, template not found with name {}", + template_name + ) + }) .address; let args = args.iter().map(|a| CliArg::from_str(a).unwrap().into_arg()).collect(); let AccountGetResponse { account, .. } = client @@ -656,6 +660,48 @@ pub async fn create_component( ); } +pub async fn call_component( + world: &mut TariWorld, + account_name: String, + output_ref: String, + wallet_daemon_name: String, + function_call: String, +) -> anyhow::Result { + let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; + + let source_component_address = get_address_from_output(world, output_ref.clone()) + .as_component_address() + .expect("Failed to get component address from output"); + + let account = get_account_from_name(&mut client, account_name).await; + let account_component_address = account + .address + .as_component_address() + .expect("Failed to get account component address"); + + let tx = Transaction::builder() + .fee_transaction_pay_from_component(account_component_address, Amount(1)) + .call_method(source_component_address, &function_call, vec![]) + .build_unsigned_transaction(); + + let resp = submit_unsigned_tx_and_wait_for_response(&mut client, tx, vec![], account).await; + + add_substate_ids( + world, + output_ref, + &resp + .as_ref() + .unwrap() + .clone() + .result + .expect("Call component transaction has timed out") + .result + .expect("Call component transaction has failed"), + ); + + resp +} + pub async fn transfer( world: &mut TariWorld, account_name: String, @@ -721,3 +767,55 @@ pub async fn get_auth_wallet_daemon_client(world: &TariWorld, wallet_daemon_name .get_authed_client() .await } + +async fn get_account_from_name(client: &mut WalletDaemonClient, account_name: String) -> Account { + let source_account_name = ComponentAddressOrName::Name(account_name.clone()); + let AccountGetResponse { account, .. } = + client + .accounts_get(source_account_name.clone()) + .await + .unwrap_or_else(|e| { + panic!( + "Failed to get account with name {}. Error: {:?}", + source_account_name, e + ) + }); + account +} + +async fn submit_unsigned_tx_and_wait_for_response( + client: &mut WalletDaemonClient, + tx: UnsignedTransaction, + inputs: Vec, + account: Account, +) -> anyhow::Result { + let submit_req = TransactionSubmitRequest { + transaction: Some(tx), + signing_key_index: Some(account.key_index), + inputs, + override_inputs: false, + is_dry_run: false, + proof_ids: vec![], + // TODO: remove + fee_instructions: vec![], + instructions: vec![], + min_epoch: None, + max_epoch: None, + }; + + let submit_resp = client.submit_transaction(submit_req).await?; + let wait_req = TransactionWaitResultRequest { + transaction_id: submit_resp.transaction_id, + timeout_secs: Some(120), + }; + let resp = client + .wait_transaction_result(wait_req) + .await + .unwrap_or_else(|_| panic!("Waiting for the transaction when calling component failed")); + + if let Some(reason) = resp.result.clone().and_then(|finalize| finalize.reject().cloned()) { + panic!("Calling component result rejected: {}", reason); + } + + Ok(resp) +} diff --git a/integration_tests/tests/cucumber.rs b/integration_tests/tests/cucumber.rs index f278b96d1f..f789954b49 100644 --- a/integration_tests/tests/cucumber.rs +++ b/integration_tests/tests/cucumber.rs @@ -155,6 +155,30 @@ async fn call_template_constructor_via_wallet_daemon( // tokio::time::sleep(Duration::from_secs(4)).await; } +#[when( + expr = r#"I call function "{word}" on template "{word}" using account {word} to pay fees via wallet daemon {word} named "{word}""# +)] +async fn call_template_constructor_via_wallet_daemon_no_args( + world: &mut TariWorld, + function_call: String, + template_name: String, + account_name: String, + wallet_daemon_name: String, + outputs_name: String, +) { + wallet_daemon_cli::create_component( + world, + outputs_name, + template_name, + account_name, + wallet_daemon_name, + function_call, + vec![], + None, + None, + ) + .await; +} #[when(expr = r#"I call function "{word}" on template "{word}" on {word} with args "{word}" named "{word}""#)] async fn call_template_constructor( world: &mut TariWorld, @@ -302,6 +326,52 @@ async fn call_component_method_and_check_result( // tokio::time::sleep(Duration::from_secs(4)).await; } +#[when( + expr = r#"I invoke on wallet daemon {word} on account {word} on component {word} the method call "{word}" the result is "{word}""# +)] +async fn call_wallet_daemon_method_and_check_result( + world: &mut TariWorld, + wallet_daemon_name: String, + account_name: String, + output_ref: String, + method_call: String, + expected_result: String, +) -> anyhow::Result<()> { + let resp = + wallet_daemon_cli::call_component(world, account_name, output_ref, wallet_daemon_name, method_call).await?; + + let finalize_result = resp + .result + .clone() + .unwrap_or_else(|| panic!("Failed to unwrap result from response: {:?}", resp)); + let result = finalize_result + .execution_results + .first() + .unwrap_or_else(|| panic!("Failed to call first() on results: {:?}", resp)); + match result.return_type { + Type::U32 => { + let u32_result: u32 = result.decode().unwrap(); + assert_eq!(u32_result.to_string(), expected_result); + }, + _ => todo!(), + }; + + Ok(()) +} + +#[when(expr = r#"I invoke on wallet daemon {word} on account {word} on component {word} the method call "{word}""#)] +async fn call_wallet_daemon_method( + world: &mut TariWorld, + wallet_daemon_name: String, + account_name: String, + output_ref: String, + method_call: String, +) -> anyhow::Result<()> { + wallet_daemon_cli::call_component(world, account_name, output_ref, wallet_daemon_name, method_call).await?; + + Ok(()) +} + #[when( expr = "I invoke on all validator nodes on component {word} the method call \"{word}\" the result is \"{word}\"" )] diff --git a/integration_tests/tests/features/counter.feature b/integration_tests/tests/features/counter.feature new file mode 100644 index 0000000000..516a8a3618 --- /dev/null +++ b/integration_tests/tests/features/counter.feature @@ -0,0 +1,50 @@ +# Copyright 2024 The Tari Project +# SPDX-License-Identifier: BSD-3-Clause + +@counter +Feature: Counter template + + @serial + Scenario: Counter template registration and invocation + + # Initialize a base node, wallet, miner and VN + Given fees are disabled + Given a base node BASE + Given a wallet WALLET connected to base node BASE + Given a miner MINER connected to base node BASE and wallet WALLET + + # Initialize a VN + Given a validator node VAL connected to base node BASE and wallet daemon WALLET_D + + # Fund wallet to send VN registration tx + When miner MINER mines 10 new blocks + When wallet WALLET has at least 2000 T + When validator node VAL sends a registration transaction to base wallet WALLET + When miner MINER mines 16 new blocks + Then the validator node VAL is listed as registered + + # Initialize indexer and connect wallet daemon + Given an indexer IDX connected to base node BASE + Given a wallet daemon WALLET_D connected to indexer IDX + + # Register the "counter" template + When base wallet WALLET registers the template "counter" + When miner MINER mines 20 new blocks + Then VAL has scanned to height 43 + + # Create the sender account + When I create an account ACC via the wallet daemon WALLET_D with 10000 free coins + + # The initial value of the counter must be 0 + When I call function "new" on template "counter" using account ACC to pay fees via wallet daemon WALLET_D named "COUNTER" + When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "value" the result is "0" + + # Increase the counter + When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "increase" + + # Check that the counter has been increased + When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "value" the result is "1" + +# Uncomment the following lines to stop execution for manual inspection of the nodes +# When I print the cucumber world +# When I wait 5000 seconds diff --git a/integration_tests/tests/features/counter.feature.ignore b/integration_tests/tests/features/counter.feature.ignore deleted file mode 100644 index 50a42d086e..0000000000 --- a/integration_tests/tests/features/counter.feature.ignore +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2022 The Tari Project -# SPDX-License-Identifier: BSD-3-Clause - - # TODO: Ignored, no inputs - replace with wallet daemon calls -@counter -Feature: Counter template - - @serial - Scenario: Counter template registration and invocation - Given fees are disabled - # Initialize a base node, wallet, miner and VN - Given a base node BASE - Given a wallet WALLET connected to base node BASE - Given a miner MINER connected to base node BASE and wallet WALLET - - # Initialize a VN - Given a validator node VAL_1 connected to base node BASE and wallet daemon WALLET_D - - # The wallet must have some funds before the VN sends transactions - When miner MINER mines 6 new blocks - When wallet WALLET has at least 20 T - - # VN registration - When validator node VAL_1 sends a registration transaction to base wallet WALLET - - # Register the "counter" template - When base wallet WALLET registers the template "counter" - When miner MINER mines 13 new blocks - Then VAL_1 has scanned to height 16 - Then the validator node VAL_1 is listed as registered - Then the template "counter" is listed as registered by the validator node VAL_1 - - # A file-base CLI account must be created to sign future calls - When I use an account key named K1 - - # Create a new Counter component - When I create a component COUNTER_1 of template "counter" on VAL_1 using "new" - When I print the cucumber world - - # The initial value of the counter must be 0 - When I invoke on VAL_1 on component COUNTER_1/components/Counter the method call "value" the result is "0" - When I print the cucumber world - - # Increase the counter - When I invoke on VAL_1 on component COUNTER_1/components/Counter the method call "increase" named "TX1" - When I print the cucumber world - - # Check that the counter has been increased - When I invoke on VAL_1 on component TX1/components/Counter the method call "value" the result is "1" -# When I print the cucumber world - -# Uncomment the following lines to stop execution for manual inspection of the nodes -# When I print the cucumber world -# When I wait 5000 seconds - - From 552b7ed6dbe705b564bcbfb84a2b6deffccc1bc2 Mon Sep 17 00:00:00 2001 From: dkf Date: Thu, 12 Sep 2024 06:56:32 +0100 Subject: [PATCH 3/6] test(cucumber): nft and fungible features (#1137) --- integration_tests/src/miner.rs | 4 - integration_tests/src/wallet_daemon_cli.rs | 35 ++++++- integration_tests/tests/cucumber.rs | 6 ++ .../tests/features/counter.feature | 10 +- .../tests/features/fungible.feature | 68 ++++++++++++++ .../tests/features/fungible.feature.ignore | 63 ------------- .../{nft.feature.ignore => nft.feature} | 92 +++++++++++-------- 7 files changed, 162 insertions(+), 116 deletions(-) create mode 100644 integration_tests/tests/features/fungible.feature delete mode 100644 integration_tests/tests/features/fungible.feature.ignore rename integration_tests/tests/features/{nft.feature.ignore => nft.feature} (58%) diff --git a/integration_tests/src/miner.rs b/integration_tests/src/miner.rs index f190bd942c..99d1b3ba73 100644 --- a/integration_tests/src/miner.rs +++ b/integration_tests/src/miner.rs @@ -115,10 +115,6 @@ async fn mine_block_without_wallet_with_template(base_client: &mut BaseNodeClien // We don't need to mine, as Localnet blocks have difficulty 1s let _submit_res = base_client.submit_block(block).await.unwrap(); - println!( - "Block successfully mined at height {:?}", - block_template.header.unwrap().height - ); } async fn create_block_template_with_coinbase( diff --git a/integration_tests/src/wallet_daemon_cli.rs b/integration_tests/src/wallet_daemon_cli.rs index 7250959d4b..2bf9b7a04c 100644 --- a/integration_tests/src/wallet_daemon_cli.rs +++ b/integration_tests/src/wallet_daemon_cli.rs @@ -30,7 +30,10 @@ use tari_crypto::{ tari_utilities::ByteArray, }; use tari_dan_common_types::{Epoch, SubstateRequirement}; -use tari_dan_wallet_sdk::{apis::confidential_transfer::ConfidentialTransferInputSelection, models::Account}; +use tari_dan_wallet_sdk::{ + apis::confidential_transfer::ConfidentialTransferInputSelection, + models::{Account, NonFungibleToken}, +}; use tari_engine_types::instruction::Instruction; use tari_template_lib::{ args, @@ -55,6 +58,7 @@ use tari_wallet_daemon_client::{ ClaimValidatorFeesRequest, ClaimValidatorFeesResponse, ConfidentialTransferRequest, + ListAccountNftRequest, MintAccountNftRequest, ProofsGenerateRequest, RevealFundsRequest, @@ -317,7 +321,7 @@ pub async fn create_account_with_free_coins( pub async fn mint_new_nft_on_account( world: &mut TariWorld, - _nft_name: String, + nft_name: String, account_name: String, wallet_daemon_name: String, existing_nft_component: Option, @@ -327,7 +331,7 @@ pub async fn mint_new_nft_on_account( let metadata = metadata.unwrap_or_else(|| { serde_json::json!({ - TOKEN_SYMBOL: "MY_NFT", + TOKEN_SYMBOL: nft_name, "name": "TariProject", "departure": "Now", "landing_on": "Moon" @@ -362,6 +366,26 @@ pub async fn mint_new_nft_on_account( ); } +pub async fn list_account_nfts( + world: &mut TariWorld, + account_name: String, + wallet_daemon_name: String, +) -> Vec { + let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; + + let request = ListAccountNftRequest { + account: Some(ComponentAddressOrName::Name(account_name.clone())), + limit: 100, + offset: 0, + }; + let submit_resp = client + .list_account_nfts(request) + .await + .expect("Failed to list account NFTs"); + + submit_resp.nfts +} + pub async fn get_balance(world: &mut TariWorld, account_name: &str, wallet_daemon_name: &str) -> i64 { let account_name = ComponentAddressOrName::Name(account_name.to_string()); let get_balance_req = AccountsGetBalancesRequest { @@ -512,7 +536,8 @@ pub async fn submit_manifest( .map(|(_, addr)| addr.clone()) .collect::>(); - let instructions = parse_manifest(&manifest_content, globals, HashMap::new()).unwrap(); + let instructions = parse_manifest(&manifest_content, globals, HashMap::new()) + .unwrap_or_else(|_| panic!("Attempted to parse manifest but failed")); let transaction_submit_req = TransactionSubmitRequest { transaction: None, @@ -813,7 +838,7 @@ async fn submit_unsigned_tx_and_wait_for_response( .await .unwrap_or_else(|_| panic!("Waiting for the transaction when calling component failed")); - if let Some(reason) = resp.result.clone().and_then(|finalize| finalize.reject().cloned()) { + if let Some(reason) = resp.result.as_ref().and_then(|finalize| finalize.reject()) { panic!("Calling component result rejected: {}", reason); } diff --git a/integration_tests/tests/cucumber.rs b/integration_tests/tests/cucumber.rs index f789954b49..5d947e7721 100644 --- a/integration_tests/tests/cucumber.rs +++ b/integration_tests/tests/cucumber.rs @@ -503,6 +503,12 @@ async fn mint_new_nft_on_account( wallet_daemon_cli::mint_new_nft_on_account(world, nft_name, account_name, wallet_daemon_name, None, None).await; } +#[when(expr = r#"I list all non fungible tokens on {word} using wallet daemon {word} the amount is {word}"#)] +async fn list_nfts_on_account(world: &mut TariWorld, account_name: String, wallet_daemon_name: String, amount: usize) { + let nfts = wallet_daemon_cli::list_account_nfts(world, account_name, wallet_daemon_name).await; + assert_eq!(amount, nfts.len()); +} + #[when(expr = "I mint a new non fungible token {word} on {word} using wallet daemon with metadata {word}")] async fn mint_new_nft_on_account_with_metadata( world: &mut TariWorld, diff --git a/integration_tests/tests/features/counter.feature b/integration_tests/tests/features/counter.feature index 516a8a3618..e93f75ee06 100644 --- a/integration_tests/tests/features/counter.feature +++ b/integration_tests/tests/features/counter.feature @@ -13,15 +13,15 @@ Feature: Counter template Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET - # Initialize a VN - Given a validator node VAL connected to base node BASE and wallet daemon WALLET_D + # Initialize a validator node + Given a validator node VN connected to base node BASE and wallet daemon WALLET_D # Fund wallet to send VN registration tx When miner MINER mines 10 new blocks When wallet WALLET has at least 2000 T - When validator node VAL sends a registration transaction to base wallet WALLET + When validator node VN sends a registration transaction to base wallet WALLET When miner MINER mines 16 new blocks - Then the validator node VAL is listed as registered + Then the validator node VN is listed as registered # Initialize indexer and connect wallet daemon Given an indexer IDX connected to base node BASE @@ -30,7 +30,7 @@ Feature: Counter template # Register the "counter" template When base wallet WALLET registers the template "counter" When miner MINER mines 20 new blocks - Then VAL has scanned to height 43 + Then VN has scanned to height 43 # Create the sender account When I create an account ACC via the wallet daemon WALLET_D with 10000 free coins diff --git a/integration_tests/tests/features/fungible.feature b/integration_tests/tests/features/fungible.feature new file mode 100644 index 0000000000..a9bbbe91fc --- /dev/null +++ b/integration_tests/tests/features/fungible.feature @@ -0,0 +1,68 @@ +# Copyright 2024 The Tari Project +# SPDX-License-Identifier: BSD-3-Clause + +@fungible +Feature: Fungible tokens + + @serial + Scenario: Mint fungible tokens + + ##### Setup + # Initialize a base node, wallet, miner and VN + Given fees are disabled + Given a base node BASE + Given a wallet WALLET connected to base node BASE + Given a miner MINER connected to base node BASE and wallet WALLET + + # Initialize a validator node + Given a validator node VN connected to base node BASE and wallet daemon WALLET_D + + # Fund wallet to send VN registration tx + When miner MINER mines 10 new blocks + When wallet WALLET has at least 2000 T + When validator node VN sends a registration transaction to base wallet WALLET + When miner MINER mines 16 new blocks + Then the validator node VN is listed as registered + + # Initialize indexer and connect wallet daemon + Given an indexer IDX connected to base node BASE + Given a wallet daemon WALLET_D connected to indexer IDX + + # Register the "faucet" template + When base wallet WALLET registers the template "faucet" + When miner MINER mines 20 new blocks + Then VN has scanned to height 43 + Then the template "faucet" is listed as registered by the validator node VN + + ##### Scenario + # Create two accounts to test deposit the tokens + When I create an account ACC1 via the wallet daemon WALLET_D with 10000 free coins + When I create an account ACC2 via the wallet daemon WALLET_D with 10000 free coins + + # Create a new Faucet component + When I call function "mint" on template "faucet" using account ACC1 to pay fees via wallet daemon WALLET_D with args "amount_10000" named "FAUCET" + + # Deposit tokens in first account + When I submit a transaction manifest via wallet daemon WALLET_D with inputs "FAUCET, ACC1" named "TX1" + ``` + let faucet = global!["FAUCET/components/TestFaucet"]; + let mut acc1 = global!["ACC1/components/Account"]; + + // get tokens from the faucet + let faucet_bucket = faucet.take_free_coins(); + acc1.deposit(faucet_bucket); + ``` + + # Move tokens from first to second account + When I submit a transaction manifest via wallet daemon WALLET_D with inputs "FAUCET, TX1, ACC2" named "TX2" + ``` + let mut acc1 = global!["TX1/components/Account"]; + let mut acc2 = global!["ACC2/components/Account"]; + let faucet_resource = global!["FAUCET/resources/0"]; + + // Withdraw 50 of the tokens and send them to acc2 + let tokens = acc1.withdraw(faucet_resource, Amount(50)); + acc2.deposit(tokens); + acc2.balance(faucet_resource); + acc1.balance(faucet_resource); + ``` diff --git a/integration_tests/tests/features/fungible.feature.ignore b/integration_tests/tests/features/fungible.feature.ignore deleted file mode 100644 index 8b19ef0bf0..0000000000 --- a/integration_tests/tests/features/fungible.feature.ignore +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2022 The Tari Project -# SPDX-License-Identifier: BSD-3-Clause - - # TODO: Ignored, no inputs - replace with wallet daemon calls -@fungible -Feature: Fungible tokens - - @serial - Scenario: Mint fungible tokens - Given fees are disabled - # Initialize a base node, wallet, miner and VN - Given a base node BASE - Given a wallet WALLET connected to base node BASE - Given a miner MINER connected to base node BASE and wallet WALLET - - # Initialize a VN - Given a validator node VN connected to base node BASE and wallet daemon WALLET_D - When miner MINER mines 6 new blocks - When wallet WALLET has at least 10000 T - When validator node VN sends a registration transaction to base wallet WALLET - # Register the "faucet" template - When base wallet WALLET registers the template "faucet" - # Mine some blocks until the UTXOs are scanned - When miner MINER mines 14 new blocks - Then VN has scanned to height 17 - Then the validator node VN is listed as registered - Then the template "faucet" is listed as registered by the validator node VN - - # A file-base CLI account must be created to sign future calls - When I use an account key named K1 - - # Create a new Faucet component - When I call function "mint" on template "faucet" on VN with args "amount_10000" named "FAUCET" - - # Create two accounts to test sending the tokens - When I create an account ACC_1 on VN - When I create an account ACC_2 on VN - - # Submit a transaction manifest - # When I print the cucumber world - When I submit a transaction manifest on VN with inputs "FAUCET, ACC_1" named "TX1" signed with key ACC_1 - ``` - let faucet = global!["FAUCET/components/TestFaucet"]; - let mut acc1 = global!["ACC_1/components/Account"]; - - // get tokens from the faucet - let faucet_bucket = faucet.take_free_coins(); - acc1.deposit(faucet_bucket); - ``` - # When I print the cucumber world - # Submit a transaction manifest - When I submit a transaction manifest on VN with inputs "FAUCET, TX1, ACC_2" named "TX2" signed with key ACC_1 -``` -let mut acc1 = global!["TX1/components/Account"]; -let mut acc2 = global!["ACC_2/components/Account"]; -let faucet_resource = global!["FAUCET/resources/0"]; - -// Withdraw 50 of the tokens and send them to acc2 -let tokens = acc1.withdraw(faucet_resource, Amount(50)); -acc2.deposit(tokens); -acc2.balance(faucet_resource); -acc1.balance(faucet_resource); -``` diff --git a/integration_tests/tests/features/nft.feature.ignore b/integration_tests/tests/features/nft.feature similarity index 58% rename from integration_tests/tests/features/nft.feature.ignore rename to integration_tests/tests/features/nft.feature index 17f80a0a49..742f40b68c 100644 --- a/integration_tests/tests/features/nft.feature.ignore +++ b/integration_tests/tests/features/nft.feature @@ -1,4 +1,4 @@ -# Copyright 2022 The Tari Project +# Copyright 2024 The Tari Project # SPDX-License-Identifier: BSD-3-Clause @nft @@ -6,41 +6,51 @@ Feature: NFTs @serial Scenario: Mint, mutate and burn non fungible tokens - Given fees are disabled + + ###### Setup # Initialize a base node, wallet, miner and VN + Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET - # Initialize a VN + # Initialize a validator node Given a validator node VN connected to base node BASE and wallet daemon WALLET_D - # The wallet must have some funds before the VN sends transactions - When miner MINER mines 7 new blocks - When wallet WALLET has at least 10000 T - - # VN registration + # Fund wallet to send VN registration tx + When miner MINER mines 10 new blocks + When wallet WALLET has at least 2000 T When validator node VN sends a registration transaction to base wallet WALLET + When miner MINER mines 16 new blocks + Then the validator node VN is listed as registered + + # Initialize indexer and connect wallet daemon + Given an indexer IDX connected to base node BASE + Given a wallet daemon WALLET_D connected to indexer IDX # Register the "basic_nft" template When base wallet WALLET registers the template "basic_nft" - When miner MINER mines 13 new blocks - Then VN has scanned to height 17 - Then the validator node VN is listed as registered + When miner MINER mines 20 new blocks + Then VN has scanned to height 43 Then the template "basic_nft" is listed as registered by the validator node VN - # A file-base CLI account must be created to sign future calls - When I use an account key named K1 + ###### Scenario + # Create two accounts to deposit the minted NFTs + When I create an account ACC1 via the wallet daemon WALLET_D with 10000 free coins + When I create an account ACC2 via the wallet daemon WALLET_D with 10000 free coins + + # Mint a basic NFT + When I mint a new non fungible token NFT_X on ACC1 using wallet daemon WALLET_D - # Create an account to deposit the minted nfts - When I create an account ACC1 on VN - When I create an account ACC2 on VN + # Check that a new NFT_X has been minted for ACC1 + # TODO: investigate flaky test + #When I list all non fungible tokens on ACC1 using wallet daemon WALLET_D the amount is 1 - # Create a new BasicNft component - When I call function "new" on template "basic_nft" on VN named "NFT" + # Create instance of the basic NFT template + When I call function "new" on template "basic_nft" using account ACC1 to pay fees via wallet daemon WALLET_D named "NFT" # Submit a transaction with NFT operations - When I submit a transaction manifest on VN with inputs "NFT, ACC1, ACC2" named "TX1" signed with key ACC1 + When I submit a transaction manifest via wallet daemon WALLET_D with inputs "NFT, ACC1, ACC2" named "TX1" ``` let sparkle_nft = global!["NFT/components/SparkleNft"]; let sparkle_res = global!["NFT/resources/0"]; @@ -68,42 +78,49 @@ Feature: NFTs let acc_bucket = acc1.withdraw_non_fungible(sparkle_res, NonFungibleId("Burn!")); sparkle_nft.burn(acc_bucket); ``` - When I print the cucumber world @serial Scenario: Create resource and mint in one transaction - Given fees are disabled + + ##### Setup # Initialize a base node, wallet, miner and VN + Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET - # Initialize a VN + # Initialize a validator node Given a validator node VN connected to base node BASE and wallet daemon WALLET_D - # The wallet must have some funds before the VN sends transactions - When miner MINER mines 7 new blocks - When wallet WALLET has at least 10000 T - - # VN registration + # Fund wallet to send VN registration tx + When miner MINER mines 10 new blocks + When wallet WALLET has at least 2000 T When validator node VN sends a registration transaction to base wallet WALLET + When miner MINER mines 16 new blocks + Then the validator node VN is listed as registered + + # Initialize indexer and connect wallet daemon + Given an indexer IDX connected to base node BASE + Given a wallet daemon WALLET_D connected to indexer IDX + # Register the "basic_nft" template When base wallet WALLET registers the template "basic_nft" - When miner MINER mines 13 new blocks - Then VN has scanned to height 17 - Then the validator node VN is listed as registered + When miner MINER mines 20 new blocks + Then VN has scanned to height 43 Then the template "basic_nft" is listed as registered by the validator node VN - # A file-base CLI account must be created to sign future calls - When I use an account key named K1 - # Create a new BasicNft component and mint in the same transaction - When I call function "new_with_initial_nft" on template "basic_nft" on VN with args "nft_str:1000" named "NFT" + ###### Scenario + # Create an account to deposit the minted NFT + When I create an account ACC1 via the wallet daemon WALLET_D with 10000 free coins + + # Create a new BasicNft component and mint in the same transaction. + # Note the updated NFT address format or parsing the manifest will fail. + When I call function "new_with_initial_nft" on template "basic_nft" using account ACC1 to pay fees via wallet daemon WALLET_D with args "nft_str_1000" named "NFT" # Check that the initial NFT was actually minted by trying to deposit it into an account - When I create an account ACC1 on VN - When I submit a transaction manifest on VN with inputs "NFT, ACC1" named "TX1" signed with key ACC1 + When I submit a transaction manifest via wallet daemon WALLET_D with inputs "NFT, ACC1" named "TX1" ``` let sparkle_nft = global!["NFT/components/SparkleNft"]; let mut acc1 = global!["ACC1/components/Account"]; @@ -112,6 +129,3 @@ Feature: NFTs let nft_bucket = sparkle_nft.take_initial_nft(); acc1.deposit(nft_bucket); ``` - - When I print the cucumber world - From 543116273d54842b03160abae88705a24852fccc Mon Sep 17 00:00:00 2001 From: Miguel Naveira <47919901+mrnaveira@users.noreply.github.com> Date: Fri, 13 Sep 2024 05:59:59 +0100 Subject: [PATCH 4/6] feat!: new account constructor for custom access rules (#1138) Description --- * Updated the account template constructor to allow custom owner and access rules * Updated the `CreateAccount` instruction type * Updated the transaction builder for the new account creation fields * Updated the transaction processor to use the new fields * Updated the protobuf definitions with the new fields in the account creation instruction type Motivation and Context --- When creating accounts we want to be able to specify custom access rules. This PR refactors the account template constructor (`create` function) to allow specifying the following fields: * `public_key_token`: mandatory field. Needed always for component creation. If the `owner_rule` is not set, it will be used to set up the default owner rule * `owner_rule`: optional field. Used to specify custom logic for the account component ownership (e.g. update the access rules after creation) * `access_rules`: optional field. Used to specify custom access control rules over the account component methods * `bucket`: optional field. Initial funds of the account How Has This Been Tested? --- * New unit test for accounts: `custom_access_rules` * Manually spawning a network with `tari_spawn` and checking it works * Existing unit and integration tests pass What process can a PR reviewer use to test or verify this change? --- See previous section Breaking Changes --- - [ ] None - [ ] Requires data directory to be deleted - [x] Other - Requires network reset as account creation (including the related instruction type) has changed --- .../src/handlers/accounts.rs | 8 ++- .../src/command/account.rs | 4 +- bindings/src/types/Instruction.ts | 11 +++- dan_layer/engine/src/transaction/processor.rs | 66 ++++++++++++++----- dan_layer/engine/tests/account.rs | 52 ++++++++++++++- dan_layer/engine/tests/airdrop.rs | 4 +- dan_layer/engine_types/src/instruction.rs | 17 +++-- dan_layer/p2p/proto/transaction.proto | 19 ++++-- dan_layer/p2p/src/conversions/transaction.rs | 54 +++++++++++++-- .../templates/account/src/lib.rs | 46 ++++++------- dan_layer/template_lib/src/auth/owner_rule.rs | 2 +- .../src/template_test.rs | 26 +++++++- dan_layer/transaction/src/builder.rs | 25 ++++++- 13 files changed, 269 insertions(+), 65 deletions(-) diff --git a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs index b1e886dc84..0cd6a156a2 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs @@ -676,7 +676,9 @@ async fn finish_claiming( }); } else { instructions.push(Instruction::CreateAccount { - owner_public_key: account_public_key.clone(), + public_key_address: account_public_key.clone(), + owner_rule: None, + access_rules: None, workspace_bucket: Some("bucket".to_string()), }); } @@ -884,7 +886,9 @@ pub async fn handle_transfer( inputs.push(address); } else { instructions.push(Instruction::CreateAccount { - owner_public_key: req.destination_public_key, + public_key_address: req.destination_public_key, + owner_rule: None, + access_rules: None, workspace_bucket: None, }); } diff --git a/applications/tari_validator_node_cli/src/command/account.rs b/applications/tari_validator_node_cli/src/command/account.rs index f5ad33ad56..b8555dac28 100644 --- a/applications/tari_validator_node_cli/src/command/account.rs +++ b/applications/tari_validator_node_cli/src/command/account.rs @@ -69,7 +69,9 @@ pub async fn handle_create( .ok_or_else(|| anyhow::anyhow!("No active key"))?; let instruction = Instruction::CreateAccount { - owner_public_key: key.public_key, + public_key_address: key.public_key, + owner_rule: None, + access_rules: None, workspace_bucket: None, }; diff --git a/bindings/src/types/Instruction.ts b/bindings/src/types/Instruction.ts index 1ef5766829..4a1a0fd752 100644 --- a/bindings/src/types/Instruction.ts +++ b/bindings/src/types/Instruction.ts @@ -1,13 +1,22 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Amount } from "./Amount"; import type { Arg } from "./Arg"; +import type { ComponentAccessRules } from "./ComponentAccessRules"; import type { ComponentAddress } from "./ComponentAddress"; import type { ConfidentialClaim } from "./ConfidentialClaim"; import type { LogLevel } from "./LogLevel"; +import type { OwnerRule } from "./OwnerRule"; import type { ResourceAddress } from "./ResourceAddress"; export type Instruction = - | { CreateAccount: { owner_public_key: string; workspace_bucket: string | null } } + | { + CreateAccount: { + public_key_address: string; + owner_rule: OwnerRule | null; + access_rules: ComponentAccessRules | null; + workspace_bucket: string | null; + }; + } | { CallFunction: { template_address: Uint8Array; function: string; args: Array } } | { CallMethod: { component_address: ComponentAddress; method: string; args: Array } } | { PutLastInstructionOutputOnWorkspace: { key: Array } } diff --git a/dan_layer/engine/src/transaction/processor.rs b/dan_layer/engine/src/transaction/processor.rs index 6df7174ec5..b6bfaa81e7 100644 --- a/dan_layer/engine/src/transaction/processor.rs +++ b/dan_layer/engine/src/transaction/processor.rs @@ -43,10 +43,11 @@ use tari_template_lib::{ arg, args, args::{Arg, WorkspaceAction}, + auth::OwnerRule, crypto::RistrettoPublicKeyBytes, invoke_args, - models::{ComponentAddress, NonFungibleAddress}, - prelude::TemplateAddress, + models::{Bucket, ComponentAddress, NonFungibleAddress}, + prelude::{AccessRules, TemplateAddress}, }; use tari_transaction::Transaction; use tari_utilities::ByteArray; @@ -70,6 +71,7 @@ use crate::{ const LOG_TARGET: &str = "tari::dan::engine::instruction_processor"; pub const MAX_CALL_DEPTH: usize = 10; +const ACCOUNT_CONSTRUCTOR_FUNCTION: &str = "create"; pub struct TransactionProcessor { template_provider: Arc, @@ -240,9 +242,18 @@ impl + 'static> T debug!(target: LOG_TARGET, "instruction = {:?}", instruction); match instruction { Instruction::CreateAccount { - owner_public_key, + public_key_address, + owner_rule, + access_rules, workspace_bucket, - } => Self::create_account(template_provider, runtime, &owner_public_key, workspace_bucket), + } => Self::create_account( + template_provider, + runtime, + &public_key_address, + owner_rule, + access_rules, + workspace_bucket, + ), Instruction::CallFunction { template_address, function, @@ -312,7 +323,9 @@ impl + 'static> T pub fn create_account( template_provider: &TTemplateProvider, runtime: &Runtime, - owner_public_key: &PublicKey, + public_key_address: &PublicKey, + owner_rule: Option, + access_rules: Option, workspace_bucket: Option, ) -> Result { let template = template_provider @@ -325,23 +338,42 @@ impl + 'static> T address: ACCOUNT_TEMPLATE_ADDRESS, })?; - let function = if workspace_bucket.is_some() { - "create_with_bucket" + let function_def = template + .template_def() + .get_function(ACCOUNT_CONSTRUCTOR_FUNCTION) + .cloned() + .ok_or_else(|| TransactionError::FunctionNotFound { + name: ACCOUNT_CONSTRUCTOR_FUNCTION.to_string(), + })?; + + let account_address = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, public_key_address); + + // the publick key is the first argument of the Account template constructor + let public_key = RistrettoPublicKeyBytes::from_bytes(public_key_address.as_bytes()).unwrap(); + let mut args = args![NonFungibleAddress::from_public_key(public_key)]; + + // add the optional owner rule if specified + if let Some(owner_rule) = owner_rule { + args.push(arg![Literal(owner_rule)]); } else { - "create" - }; + let none: Option = None; + args.push(arg![Literal(none)]); + } - let function_def = template.template_def().get_function(function).cloned().ok_or_else(|| { - TransactionError::FunctionNotFound { - name: function.to_string(), - } - })?; - let owner_pk = RistrettoPublicKeyBytes::from_bytes(owner_public_key.as_bytes()).unwrap(); - let account_address = new_component_address_from_public_key(&ACCOUNT_TEMPLATE_ADDRESS, owner_public_key); + // add the optional access rules if specified + if let Some(access_rules) = access_rules { + args.push(arg![Literal(access_rules)]); + } else { + let none: Option = None; + args.push(arg![Literal(none)]); + } - let mut args = args![NonFungibleAddress::from_public_key(owner_pk)]; + // add the optional workspace bucket with the initial funds of the account if let Some(workspace_bucket) = workspace_bucket { args.push(arg![Workspace(workspace_bucket)]); + } else { + let none: Option = None; + args.push(arg![Literal(none)]); } let args = runtime.resolve_args(args)?; diff --git a/dan_layer/engine/tests/account.rs b/dan_layer/engine/tests/account.rs index fad265693d..63cbc415dd 100644 --- a/dan_layer/engine/tests/account.rs +++ b/dan_layer/engine/tests/account.rs @@ -1,16 +1,19 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; +use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey, tari_utilities::ByteArray}; use tari_dan_engine::runtime::{ActionIdent, RuntimeError}; use tari_engine_types::instruction::Instruction; use tari_template_lib::{ args, + auth::AccessRule, constants::XTR, models::{Amount, ComponentAddress, ResourceAddress}, + prelude::AccessRules, }; use tari_template_test_tooling::{ support::assert_error::{assert_access_denied_for_action, assert_reject_reason}, + test_faucet_component, TemplateTest, }; use tari_transaction::Transaction; @@ -252,3 +255,50 @@ fn gasless() { let balance = result.expect_return::>(3); assert_eq!(balance[0].1, 100); } + +#[test] +fn custom_access_rules() { + let mut template_test = TemplateTest::new::<_, &str>([]); + + // First we create a account with a custom rule that anyone can withdraw + let (owner_proof, public_key, secret_key) = template_test.create_owner_proof(); + + let access_rules = AccessRules::new() + .add_method_rule("balance", AccessRule::AllowAll) + .add_method_rule("get_balances", AccessRule::AllowAll) + .add_method_rule("deposit", AccessRule::AllowAll) + .add_method_rule("deposit_all", AccessRule::AllowAll) + .add_method_rule("get_non_fungible_ids", AccessRule::AllowAll) + // We are going to make it so anyone can withdraw + .default(AccessRule::AllowAll); + + let result = template_test.execute_expect_success( + Transaction::builder() + .call_method(test_faucet_component(), "take_free_coins", args![]) + .put_last_instruction_output_on_workspace("bucket") + // Create component with the same ID + .create_account_with_custom_rules( + public_key, + None, + Some(access_rules), + Some("bucket"), + ) + // Signed by source account so that it can pay the fees for the new account creation + .sign(&secret_key) + .build(), + vec![owner_proof], + ); + let user_account = result.finalize.execution_results[2].decode().unwrap(); + + // We create another account and we we will withdraw from the custom one + let (user2_account, user2_account_proof, user2_secret_key) = template_test.create_funded_account(); + template_test.execute_expect_success( + Transaction::builder() + .call_method(user_account, "withdraw", args![XTR, Amount(100)]) + .put_last_instruction_output_on_workspace("b") + .call_method(user2_account, "deposit", args![Workspace("b")]) + .build() + .sign(&user2_secret_key), + vec![user2_account_proof], + ); +} diff --git a/dan_layer/engine/tests/airdrop.rs b/dan_layer/engine/tests/airdrop.rs index 152bc198b0..06ae9aa78b 100644 --- a/dan_layer/engine/tests/airdrop.rs +++ b/dan_layer/engine/tests/airdrop.rs @@ -32,7 +32,9 @@ fn airdrop() { let instructions = iter::repeat_with(|| { let (_, owner_public_key, _) = template_test.create_owner_proof(); Instruction::CreateAccount { - owner_public_key, + public_key_address: owner_public_key, + owner_rule: None, + access_rules: None, workspace_bucket: None, } }) diff --git a/dan_layer/engine_types/src/instruction.rs b/dan_layer/engine_types/src/instruction.rs index 470a8e7e74..d65b5c021c 100644 --- a/dan_layer/engine_types/src/instruction.rs +++ b/dan_layer/engine_types/src/instruction.rs @@ -8,8 +8,9 @@ use tari_common_types::types::PublicKey; use tari_crypto::tari_utilities::hex::Hex; use tari_template_lib::{ args::{Arg, LogLevel}, + auth::OwnerRule, models::{ComponentAddress, ResourceAddress, TemplateAddress}, - prelude::Amount, + prelude::{AccessRules, Amount}, }; #[cfg(feature = "ts")] use ts_rs::TS; @@ -21,7 +22,9 @@ use crate::{confidential::ConfidentialClaim, serde_with}; pub enum Instruction { CreateAccount { #[cfg_attr(feature = "ts", ts(type = "string"))] - owner_public_key: PublicKey, + public_key_address: PublicKey, + owner_rule: Option, + access_rules: Option, #[cfg_attr(feature = "ts", ts(type = "string | null"))] workspace_bucket: Option, }, @@ -71,10 +74,16 @@ impl Display for Instruction { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::CreateAccount { - owner_public_key, + public_key_address, + owner_rule, + access_rules, workspace_bucket, } => { - write!(f, "CreateAccount {{ owner_public_key: {}, bucket: ", owner_public_key,)?; + write!( + f, + "CreateAccount {{ public_key_address: {}, owner_rule: {:?}, acces_rules: {:?}, bucket: ", + public_key_address, owner_rule, access_rules + )?; match workspace_bucket { Some(bucket) => write!(f, "{}", bucket)?, None => write!(f, "None")?, diff --git a/dan_layer/p2p/proto/transaction.proto b/dan_layer/p2p/proto/transaction.proto index fa28d630df..8712942927 100644 --- a/dan_layer/p2p/proto/transaction.proto +++ b/dan_layer/p2p/proto/transaction.proto @@ -72,14 +72,17 @@ message Instruction { bytes claim_validator_fees_validator_public_key = 15; uint64 claim_validator_fees_epoch = 16; - bytes create_account_owner_public_key = 17; - string create_account_workspace_bucket = 18; + bytes create_account_public_key = 17; + OwnerRule create_account_owner_rule = 18; + AccessRules create_account_access_rules = 19; + string create_account_workspace_bucket = 20; // AssertBucketContains - bytes resource_address = 19; - int64 min_amount = 20; + bytes resource_address = 21; + int64 min_amount = 22; } + message Arg { enum ArgType { LITERAL = 0; @@ -136,3 +139,11 @@ message ViewableBalanceProof { bytes s_m = 7; bytes s_r = 8; } + +message OwnerRule { + bytes encoded_owner_rule = 1; +} + +message AccessRules { + bytes encoded_access_rules = 1; +} \ No newline at end of file diff --git a/dan_layer/p2p/src/conversions/transaction.rs b/dan_layer/p2p/src/conversions/transaction.rs index 0246c52aee..251d79d740 100644 --- a/dan_layer/p2p/src/conversions/transaction.rs +++ b/dan_layer/p2p/src/conversions/transaction.rs @@ -23,13 +23,14 @@ use std::convert::{TryFrom, TryInto}; use anyhow::anyhow; -use tari_bor::decode_exact; +use tari_bor::{decode_exact, encode}; use tari_common_types::types::{Commitment, PrivateKey, PublicKey}; use tari_crypto::{ristretto::RistrettoComSig, tari_utilities::ByteArray}; use tari_dan_common_types::{Epoch, SubstateRequirement, VersionedSubstateId}; use tari_engine_types::{confidential::ConfidentialClaim, instruction::Instruction, substate::SubstateId}; use tari_template_lib::{ args::Arg, + auth::OwnerRule, crypto::{BalanceProofSignature, PedersonCommitmentBytes, RistrettoPublicKeyBytes}, models::{ Amount, @@ -40,6 +41,7 @@ use tari_template_lib::{ ObjectKey, ViewableBalanceProof, }, + prelude::AccessRules, }; use tari_transaction::{Transaction, UnsignedTransaction}; @@ -193,8 +195,10 @@ impl TryFrom for Instruction { InstructionType::try_from(request.instruction_type).map_err(|e| anyhow!("invalid instruction_type {e}"))?; let instruction = match instruction_type { InstructionType::CreateAccount => Instruction::CreateAccount { - owner_public_key: PublicKey::from_canonical_bytes(&request.create_account_owner_public_key) - .map_err(|e| anyhow!("create_account_owner_public_key: {}", e))?, + public_key_address: PublicKey::from_canonical_bytes(&request.create_account_public_key) + .map_err(|e| anyhow!("create_account_public_key: {}", e))?, + owner_rule: request.create_account_owner_rule.map(TryInto::try_into).transpose()?, + access_rules: request.create_account_access_rules.map(TryInto::try_into).transpose()?, workspace_bucket: Some(request.create_account_workspace_bucket).filter(|s| !s.is_empty()), }, InstructionType::Function => { @@ -267,11 +271,15 @@ impl From for proto::transaction::Instruction { match instruction { Instruction::CreateAccount { - owner_public_key, + public_key_address, + owner_rule, + access_rules, workspace_bucket, } => { result.instruction_type = InstructionType::CreateAccount as i32; - result.create_account_owner_public_key = owner_public_key.to_vec(); + result.create_account_public_key = public_key_address.to_vec(); + result.create_account_owner_rule = owner_rule.map(Into::into); + result.create_account_access_rules = access_rules.map(Into::into); result.create_account_workspace_bucket = workspace_bucket.unwrap_or_default(); }, Instruction::CallFunction { @@ -597,3 +605,39 @@ impl From for proto::transaction::ViewableBalanceProof { } } } + +// -------------------------------- OwnerRule -------------------------------- // + +impl From for proto::transaction::OwnerRule { + fn from(value: OwnerRule) -> Self { + Self { + encoded_owner_rule: encode(&value).unwrap(), + } + } +} + +impl TryFrom for OwnerRule { + type Error = anyhow::Error; + + fn try_from(value: proto::transaction::OwnerRule) -> Result { + Ok(decode_exact(&value.encoded_owner_rule)?) + } +} + +// -------------------------------- AccessRules -------------------------------- // + +impl From for proto::transaction::AccessRules { + fn from(value: AccessRules) -> Self { + Self { + encoded_access_rules: encode(&value).unwrap(), + } + } +} + +impl TryFrom for AccessRules { + type Error = anyhow::Error; + + fn try_from(value: proto::transaction::AccessRules) -> Result { + Ok(decode_exact(&value.encoded_access_rules)?) + } +} diff --git a/dan_layer/template_builtin/templates/account/src/lib.rs b/dan_layer/template_builtin/templates/account/src/lib.rs index 49a445c685..e349778631 100644 --- a/dan_layer/template_builtin/templates/account/src/lib.rs +++ b/dan_layer/template_builtin/templates/account/src/lib.rs @@ -33,31 +33,27 @@ mod account_template { } impl Account { - pub fn create(owner_token: NonFungibleAddress) -> Component { - Self::internal_create(owner_token, None) - } - - pub fn create_with_bucket(owner_token: NonFungibleAddress, bucket: Bucket) -> Component { - Self::internal_create(owner_token, Some(bucket)) - } - - fn internal_create(owner_token: NonFungibleAddress, bucket: Option) -> Component { + pub fn create(public_key_token: NonFungibleAddress, owner_rule: Option, access_rules: Option, bucket: Option) -> Component { // extract the public key from the token - // we only allow owner tokens that correspond to public keys - let public_key = owner_token + // we only allow tokens that correspond to public keys + let public_key = public_key_token .to_public_key() - .unwrap_or_else(|| panic!("owner_token is not a valid public key: {}", owner_token)); - - // only the owner of the token will be able to withdraw funds from the account - let withdraw_rule = - AccessRule::Restricted(RestrictedAccessRule::Require(RequireRule::Require(owner_token.into()))); - let rules = AccessRules::new() - .add_method_rule("balance", AccessRule::AllowAll) - .add_method_rule("get_balances", AccessRule::AllowAll) - .add_method_rule("deposit", AccessRule::AllowAll) - .add_method_rule("deposit_all", AccessRule::AllowAll) - .add_method_rule("get_non_fungible_ids", AccessRule::AllowAll) - .default(withdraw_rule); + .unwrap_or_else(|| panic!("public_key_token is not a valid public key: {}", public_key_token)); + + let owner_rule = owner_rule.unwrap_or( + OwnerRule::ByPublicKey(public_key) + ); + + let access_rules = access_rules.unwrap_or( + AccessRules::new() + .add_method_rule("balance", AccessRule::AllowAll) + .add_method_rule("get_balances", AccessRule::AllowAll) + .add_method_rule("deposit", AccessRule::AllowAll) + .add_method_rule("deposit_all", AccessRule::AllowAll) + .add_method_rule("get_non_fungible_ids", AccessRule::AllowAll) + // By defaul, only the owner of the token will be able to withdraw funds from the account + .default(AccessRule::Restricted(RestrictedAccessRule::Require(RequireRule::Require(public_key_token.into())))) + ); // add the funds from the (optional) bucket let mut vaults = BTreeMap::new(); @@ -66,9 +62,9 @@ mod account_template { } Component::new(Self { vaults }) - .with_access_rules(rules) + .with_access_rules(access_rules) .with_public_key_address(public_key) - .with_owner_rule(OwnerRule::ByPublicKey(public_key)) + .with_owner_rule(owner_rule) .create() } diff --git a/dan_layer/template_lib/src/auth/owner_rule.rs b/dan_layer/template_lib/src/auth/owner_rule.rs index 70c243fbd2..f5dde459a0 100644 --- a/dan_layer/template_lib/src/auth/owner_rule.rs +++ b/dan_layer/template_lib/src/auth/owner_rule.rs @@ -12,7 +12,7 @@ pub struct Ownership<'a> { } /// An enum for all possible ways to specify ownership of values -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq)] #[cfg_attr( feature = "ts", derive(ts_rs::TS), diff --git a/dan_layer/template_test_tooling/src/template_test.rs b/dan_layer/template_test_tooling/src/template_test.rs index 390ac008bb..8e8d62c3ee 100644 --- a/dan_layer/template_test_tooling/src/template_test.rs +++ b/dan_layer/template_test_tooling/src/template_test.rs @@ -297,7 +297,9 @@ impl TemplateTest { let result = self .execute_and_commit( vec![Instruction::CreateAccount { - owner_public_key, + public_key_address: owner_public_key, + owner_rule: None, + access_rules: None, workspace_bucket, }], proofs, @@ -420,6 +422,28 @@ impl TemplateTest { (component, owner_proof, secret_key) } + pub fn create_custom_funded_account(&mut self) -> (ComponentAddress, NonFungibleAddress, RistrettoSecretKey) { + let (owner_proof, public_key, secret_key) = self.create_owner_proof(); + let old_fail_fees = self.enable_fees; + self.enable_fees = false; + let result = self.execute_expect_success( + Transaction::builder() + .call_method(test_faucet_component(), "take_free_coins", args![]) + .put_last_instruction_output_on_workspace("bucket") + .create_account_with_bucket(public_key, "bucket") + .sign(&secret_key) + .build(), + vec![owner_proof.clone()], + ); + + let component = result.finalize.execution_results[2] + .decode::() + .unwrap(); + + self.enable_fees = old_fail_fees; + (component, owner_proof, secret_key) + } + fn next_key_seed(&mut self) -> u8 { let seed = self.key_seed; self.key_seed += 1; diff --git a/dan_layer/transaction/src/builder.rs b/dan_layer/transaction/src/builder.rs index b61e30e342..d62a9a5a7e 100644 --- a/dan_layer/transaction/src/builder.rs +++ b/dan_layer/transaction/src/builder.rs @@ -7,7 +7,9 @@ use tari_engine_types::{confidential::ConfidentialClaim, instruction::Instructio use tari_template_lib::{ args, args::Arg, + auth::OwnerRule, models::{Amount, ComponentAddress, ConfidentialWithdrawProof, ResourceAddress}, + prelude::AccessRules, }; use crate::{unsigned_transaction::UnsignedTransaction, Transaction, TransactionSignature}; @@ -62,18 +64,37 @@ impl TransactionBuilder { pub fn create_account(self, owner_public_key: PublicKey) -> Self { self.add_instruction(Instruction::CreateAccount { - owner_public_key, + public_key_address: owner_public_key, + owner_rule: None, + access_rules: None, workspace_bucket: None, }) } pub fn create_account_with_bucket>(self, owner_public_key: PublicKey, workspace_bucket: T) -> Self { self.add_instruction(Instruction::CreateAccount { - owner_public_key, + public_key_address: owner_public_key, + owner_rule: None, + access_rules: None, workspace_bucket: Some(workspace_bucket.into()), }) } + pub fn create_account_with_custom_rules>( + self, + public_key_address: PublicKey, + owner_rule: Option, + access_rules: Option, + workspace_bucket: Option, + ) -> Self { + self.add_instruction(Instruction::CreateAccount { + public_key_address, + owner_rule, + access_rules, + workspace_bucket: workspace_bucket.map(|b| b.into()), + }) + } + pub fn call_function(self, template_address: TemplateAddress, function: &str, args: Vec) -> Self { self.add_instruction(Instruction::CallFunction { template_address, From b6033945ccec8c1588cfa09afcbc5c01bf280094 Mon Sep 17 00:00:00 2001 From: dkf Date: Fri, 13 Sep 2024 09:19:54 +0100 Subject: [PATCH 5/6] test(cucumber): add concurrent wallet daemon call (#1140) Description --- * Add concurrent wallet daemon call ability to replicate VN cli test functionality * Update concurrent feature with using the wallet daemon * Concurrent feature is disabled due to lock bug while awaiting engine update --- integration_tests/src/helpers.rs | 19 +++++ integration_tests/src/validator_node_cli.rs | 69 ++----------------- integration_tests/src/wallet_daemon_cli.rs | 60 ++++++++++++++-- integration_tests/tests/cucumber.rs | 23 +++++++ .../tests/features/concurrency.feature.ignore | 51 +++++++------- 5 files changed, 133 insertions(+), 89 deletions(-) diff --git a/integration_tests/src/helpers.rs b/integration_tests/src/helpers.rs index 9b1c2b26c8..411a396461 100644 --- a/integration_tests/src/helpers.rs +++ b/integration_tests/src/helpers.rs @@ -7,6 +7,7 @@ use std::{ time::Duration, }; +use tari_dan_common_types::SubstateRequirement; use tari_engine_types::substate::SubstateId; use tokio::{io::AsyncWriteExt, task::JoinHandle}; @@ -136,3 +137,21 @@ pub fn get_address_from_output(world: &TariWorld, output_ref: String) -> &Substa }) .unwrap_or_else(|| panic!("Output not found: {}", output_ref)) } + +pub fn get_component_from_namespace(world: &TariWorld, fq_component_name: String) -> SubstateRequirement { + let (input_group, component_name) = fq_component_name.split_once('/').unwrap_or_else(|| { + panic!( + "Component name must be in the format '{{group}}/components/{{template_name}}', got {}", + fq_component_name + ) + }); + + world + .outputs + .get(input_group) + .unwrap_or_else(|| panic!("No outputs found with name {}", input_group)) + .iter() + .find(|(name, _)| **name == component_name) + .map(|(_, data)| data.clone()) + .unwrap_or_else(|| panic!("No component named {}", component_name)) +} diff --git a/integration_tests/src/validator_node_cli.rs b/integration_tests/src/validator_node_cli.rs index 68f6074ac4..5fe7d44d40 100644 --- a/integration_tests/src/validator_node_cli.rs +++ b/integration_tests/src/validator_node_cli.rs @@ -19,7 +19,7 @@ use tari_validator_node_cli::{ }; use tari_validator_node_client::{types::SubmitTransactionResponse, ValidatorNodeClient}; -use crate::{logging::get_base_dir_for_scenario, TariWorld}; +use crate::{helpers::get_component_from_namespace, logging::get_base_dir_for_scenario, TariWorld}; fn get_key_manager(world: &mut TariWorld) -> KeyManager { let path = get_cli_data_dir(world); @@ -206,29 +206,12 @@ pub async fn concurrent_call_method( method_call: String, times: usize, ) -> Result { - let vn_data_dir = get_cli_data_dir(world); - let (input_group, component_name) = fq_component_name.split_once('/').unwrap_or_else(|| { - panic!( - "Component name must be in the format '{{group}}/components/{{template_name}}', got {}", - fq_component_name - ) - }); - - let vn_client = world.get_validator_node(&vn_name).get_client(); - - let mut component = world - .outputs - .get(input_group) - .unwrap_or_else(|| panic!("No outputs found with name {}", input_group)) - .iter() - .find(|(name, _)| **name == component_name) - .map(|(_, data)| data.clone()) - .unwrap_or_else(|| panic!("No component named {}", component_name)); + let mut component = get_component_from_namespace(world, fq_component_name); // For concurrent transactions we DO NOT specify the versions component.version = None; - // call_method_inner(vn_client.clone(), vn_data_dir.clone(), component.clone(), method_call.clone()).await - + let vn_data_dir = get_cli_data_dir(world); + let vn_client = world.get_validator_node(&vn_name).get_client(); let mut handles = Vec::new(); for _ in 0..times { let handle = tokio::spawn(call_method_inner( @@ -268,47 +251,9 @@ pub async fn call_method( method_call: String, ) -> Result { let data_dir = get_cli_data_dir(world); - let (input_group, component_name) = fq_component_name.split_once('/').unwrap_or_else(|| { - panic!( - "Component name must be in the format '{{group}}/components/{{template_name}}', got {}", - fq_component_name - ) - }); - let component = world - .outputs - .get(input_group) - .unwrap_or_else(|| panic!("No outputs found with name {}", input_group)) - .iter() - .find(|(name, _)| **name == component_name) - .map(|(_, data)| data.clone()) - .unwrap_or_else(|| panic!("No component named {}", component_name)); - - let instruction = CliInstruction::CallMethod { - component_address: component.substate_id.clone(), - // TODO: actually parse the method call for arguments - method_name: method_call, - args: vec![], - }; - - println!("Inputs: {}", component); - let args = SubmitArgs { - instruction, - common: CommonSubmitArgs { - wait_for_result: true, - wait_for_result_timeout: Some(60), - inputs: vec![component], - version: None, - dump_outputs_into: None, - account_template_address: None, - dry_run: false, - }, - }; - let mut client = world.get_validator_node(&vn_name).get_client(); - let resp = handle_submit(args, data_dir, &mut client).await.unwrap(); - - if let Some(failure) = resp.dry_run_result.as_ref().unwrap().finalize.full_reject() { - return Err(failure.clone()); - } + let component = get_component_from_namespace(world, fq_component_name); + let vn_client = world.get_validator_node(&vn_name).get_client(); + let resp = call_method_inner(vn_client, data_dir, component, method_call).await?; // store the account component address and other substate ids for later reference add_substate_ids( diff --git a/integration_tests/src/wallet_daemon_cli.rs b/integration_tests/src/wallet_daemon_cli.rs index 2bf9b7a04c..a8cdef1149 100644 --- a/integration_tests/src/wallet_daemon_cli.rs +++ b/integration_tests/src/wallet_daemon_cli.rs @@ -22,6 +22,7 @@ use std::{collections::HashMap, str::FromStr, time::Duration}; +use anyhow::bail; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; use serde_json::json; use tari_crypto::{ @@ -709,7 +710,7 @@ pub async fn call_component( .call_method(source_component_address, &function_call, vec![]) .build_unsigned_transaction(); - let resp = submit_unsigned_tx_and_wait_for_response(&mut client, tx, vec![], account).await; + let resp = submit_unsigned_tx_and_wait_for_response(client, tx, vec![], account).await; add_substate_ids( world, @@ -727,6 +728,57 @@ pub async fn call_component( resp } +pub async fn concurrent_call_component( + world: &mut TariWorld, + account_name: String, + output_ref: String, + wallet_daemon_name: String, + function_call: String, + times: usize, +) -> anyhow::Result<()> { + let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; + + let source_component_address = get_address_from_output(world, output_ref.clone()) + .as_component_address() + .expect("Failed to get component address from output"); + + let account = get_account_from_name(&mut client, account_name).await; + let account_component_address = account + .address + .as_component_address() + .expect("Failed to get account component address"); + + let mut handles = Vec::new(); + for _ in 0..times { + let acc = account.clone(); + let clt = client.clone(); + let tx = Transaction::builder() + .fee_transaction_pay_from_component(account_component_address, Amount(1)) + .call_method(source_component_address, &function_call, vec![]) + .build_unsigned_transaction(); + let handle = tokio::spawn(submit_unsigned_tx_and_wait_for_response(clt, tx, vec![], acc)); + handles.push(handle); + } + + let mut last_resp = None; + for handle in handles { + let result = handle.await.map_err(|e| e.to_string()); + if result.is_err() { + bail!("{}", result.as_ref().unwrap_err()); + } + match result { + Ok(response) => last_resp = Some(response), + Err(e) => bail!("Failed to get response from handler: {}", e), + } + } + + if last_resp.is_none() { + bail!("No responses from any of the wallet daemon concurrent calls"); + } + + Ok(()) +} + pub async fn transfer( world: &mut TariWorld, account_name: String, @@ -809,7 +861,7 @@ async fn get_account_from_name(client: &mut WalletDaemonClient, account_name: St } async fn submit_unsigned_tx_and_wait_for_response( - client: &mut WalletDaemonClient, + mut client: WalletDaemonClient, tx: UnsignedTransaction, inputs: Vec, account: Account, @@ -836,10 +888,10 @@ async fn submit_unsigned_tx_and_wait_for_response( let resp = client .wait_transaction_result(wait_req) .await - .unwrap_or_else(|_| panic!("Waiting for the transaction when calling component failed")); + .map_err(|e| anyhow::Error::msg(e.to_string()))?; if let Some(reason) = resp.result.as_ref().and_then(|finalize| finalize.reject()) { - panic!("Calling component result rejected: {}", reason); + bail!("Calling component result rejected: {}", reason); } Ok(resp) diff --git a/integration_tests/tests/cucumber.rs b/integration_tests/tests/cucumber.rs index 5d947e7721..50abc04d5e 100644 --- a/integration_tests/tests/cucumber.rs +++ b/integration_tests/tests/cucumber.rs @@ -372,6 +372,29 @@ async fn call_wallet_daemon_method( Ok(()) } +#[when( + expr = r#"I invoke on wallet daemon {word} on account {word} on component {word} the method call "{word}" concurrently {int} times"# +)] +async fn call_wallet_daemon_method_concurrently( + world: &mut TariWorld, + wallet_daemon_name: String, + account_name: String, + output_ref: String, + method_call: String, + times: usize, +) { + wallet_daemon_cli::concurrent_call_component( + world, + account_name, + output_ref, + wallet_daemon_name, + method_call, + times, + ) + .await + .unwrap_or_else(|e| panic!("Concurrent wallet daemon call failed: {:?}", e)); +} + #[when( expr = "I invoke on all validator nodes on component {word} the method call \"{word}\" the result is \"{word}\"" )] diff --git a/integration_tests/tests/features/concurrency.feature.ignore b/integration_tests/tests/features/concurrency.feature.ignore index 71b6e8483c..5a094f0b0c 100644 --- a/integration_tests/tests/features/concurrency.feature.ignore +++ b/integration_tests/tests/features/concurrency.feature.ignore @@ -1,46 +1,51 @@ # Copyright 2024 The Tari Project # SPDX-License-Identifier: BSD-3-Clause -@concurrency +@concurrency @doit Feature: Concurrency @serial Scenario: Concurrent calls to the Counter template - Given fees are disabled + + ##### Setup # Initialize a base node, wallet, miner and VN + Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET - # Initialize a VN - Given a validator node VAL_1 connected to base node BASE and wallet daemon WALLET_D + # Initialize a validator node + Given a validator node VN connected to base node BASE and wallet daemon WALLET_D - # The wallet must have some funds before the VN sends transactions - When miner MINER mines 6 new blocks - When wallet WALLET has at least 20 T + # Fund wallet to send VN registration tx + When miner MINER mines 10 new blocks + When wallet WALLET has at least 2000 T + When validator node VN sends a registration transaction to base wallet WALLET + When miner MINER mines 16 new blocks + Then the validator node VN is listed as registered - # VN registration - When validator node VAL_1 sends a registration transaction to base wallet WALLET + # Initialize indexer and connect wallet daemon + Given an indexer IDX connected to base node BASE + Given a wallet daemon WALLET_D connected to indexer IDX # Register the "counter" template When base wallet WALLET registers the template "counter" - When miner MINER mines 13 new blocks - Then VAL_1 has scanned to height 16 - Then the validator node VAL_1 is listed as registered - Then the template "counter" is listed as registered by the validator node VAL_1 + When miner MINER mines 20 new blocks + Then VN has scanned to height 43 - # A file-base CLI account must be created to sign future calls - When I use an account key named K1 + # Create the sender account + When I create an account ACC via the wallet daemon WALLET_D with 10000 free coins - # Create a new Counter component - When I create a component COUNTER_1 of template "counter" on VAL_1 using "new" - When I print the cucumber world + ##### Scenario + # The initial value of the counter must be 0 + When I call function "new" on template "counter" using account ACC to pay fees via wallet daemon WALLET_D named "COUNTER" + When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "value" the result is "0" # Send multiple concurrent transactions to increase the counter - # TODO: when concurrency is fully working, call it with "2 times" or higher - When I invoke on VAL_1 on component COUNTER_1/components/Counter the method call "increase" concurrently 1 times - When I print the cucumber world + # Currently there is a lock bug where the subsequent transactions executed are being rejected, should be tested later after engine changes: + # Reject(FailedToLockInputs("Failed to Write lock substate component_459d...4443c:1 due to conflict with existing Write lock")) + When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "increase" concurrently 2 times # Check that the counter has been increased - # TODO: uncomment when concurrency is fully working - # When I invoke on VAL_1 on component TX1/components/Counter the method call "value" the result is "2" \ No newline at end of file + # Note: this is currently not working together with the previous test case when times > 1, only the first transaction is being executed properly + When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "value" the result is "2" From f80d338299f382f88441ab13feb9174dee574bf8 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Mon, 16 Sep 2024 16:06:11 +0400 Subject: [PATCH 6/6] fix(consensus)!: read-only resource + other fixes (#1134) Description --- - Resources are locked as read-only in consensus - Greatly reduce the size of the fee breakdown in transaction receipt - Fix mempool incorrect involvement detection - Fix consensus crash if versioned substate requirement is already DOWN - Fix off-by-one error when sending state transitions in state sync Motivation and Context --- Resources are highly contended and therefore are read-only after creation. Any transaction that mutates the resource will fail however the template API has not changed in this PR to reflect that. Any mints/burns after the initial mint will succeed only in the local-only case, multi-committee mints/burns will fail. Ideas for improvements: 1. Easy way: Allow user to specify read/write for resources (or perhaps substates in general). Cons: poorer UX 2. Complex: initially locked as read-only until AllPrepared phase. Transaction is executed and if the transaction results in a write for the resource. The write lock is queued and no further progress can be made on the transaction until all preceding write/read locks are released. Once this occurs the transaction is re-executed and `LocalAccept` proposed with as a write. 3. To improve concurrency, a resource issuer may lock their resource for a period guaranteeing that no minting/burning or access rule changes can occur until that period expires. 4. Automatically and implicitly read lock a resource for a guaranteed period of time (epochs). A user may submit a transaction that updates the resource, but that transaction will not be sequenced until the lock expires. 5. Add a resource mint/burn instruction which will only involve a single shard. This will naturally "park" the transaction until any multishard locks are released and a local-only mint/burn/access rule update is performed. 6. Eventual consistency: Total supply and access rule changes will eventually reflect. All resource operations are logged and eventually applied in an atomic way across shard groups, perhaps with a special signed cross-shard message that will be locally proposed. 7. Apply point (1) and check access rules for the resource. If the signer is not permitted to write the resource _at locking/pledging time (before execution)_, the transaction is aborted. This is more related to security, not concurrency. How Has This Been Tested? --- Manually What process can a PR reviewer use to test or verify this change? --- Submit transactions that concurrently read from the resource address. Breaking Changes --- - [ ] None - [x] Requires data directory to be deleted - [ ] Other - Please specify --- Cargo.lock | 1 + .../src/transaction_executor.rs | 3 + applications/tari_swarm_daemon/src/logger.rs | 2 +- .../src/p2p/services/mempool/service.rs | 29 +- bindings/dist/types/FeeBreakdown.d.ts | 3 +- bindings/dist/types/FeeCostBreakdown.d.ts | 2 +- bindings/dist/types/FeeReceipt.d.ts | 2 +- .../types/VersionedSubstateIdLockIntent.d.ts | 1 + bindings/src/types/FeeBreakdown.ts | 3 +- bindings/src/types/FeeCostBreakdown.ts | 2 +- bindings/src/types/FeeReceipt.ts | 2 +- .../types/VersionedSubstateIdLockIntent.ts | 1 + dan_layer/consensus/src/hotstuff/common.rs | 7 +- .../hotstuff/foreign_proposal_processor.rs | 11 + .../consensus/src/hotstuff/on_propose.rs | 72 ++-- .../on_ready_to_vote_on_local_block.rs | 2 + .../hotstuff/on_receive_foreign_proposal.rs | 396 ------------------ .../src/hotstuff/substate_store/error.rs | 5 +- .../hotstuff/substate_store/pending_store.rs | 24 +- .../hotstuff/transaction_manager/manager.rs | 11 +- .../hotstuff/transaction_manager/prepared.rs | 9 +- dan_layer/consensus/src/hotstuff/worker.rs | 40 +- .../src/traits/transaction_executor.rs | 8 + dan_layer/consensus_tests/src/consensus.rs | 31 +- .../consensus_tests/src/support/harness.rs | 5 +- .../src/support/transaction.rs | 19 +- dan_layer/engine/src/runtime/fee_state.rs | 4 +- dan_layer/engine/src/runtime/tracker.rs | 4 +- dan_layer/engine/src/runtime/working_state.rs | 45 +- dan_layer/engine/tests/test.rs | 212 +++++----- dan_layer/engine_types/Cargo.toml | 1 + dan_layer/engine_types/src/fees.rs | 44 +- dan_layer/engine_types/src/indexed_value.rs | 13 +- dan_layer/rpc_state_sync/src/manager.rs | 9 - .../up.sql | 15 +- dan_layer/state_store_sqlite/src/reader.rs | 13 +- dan_layer/state_store_sqlite/src/schema.rs | 1 - dan_layer/state_store_sqlite/src/writer.rs | 25 +- .../consensus_models/executed_transaction.rs | 16 +- .../src/consensus_models/lock_intent.rs | 44 +- .../src/consensus_models/substate_lock.rs | 1 + .../src/consensus_models/transaction.rs | 2 +- .../src/template_test.rs | 3 +- dan_layer/transaction/src/transaction.rs | 25 +- dan_layer/wallet/sdk/src/apis/transaction.rs | 10 +- .../tests/steps/validator_node.rs | 7 +- utilities/tariswap_test_bench/src/accounts.rs | 66 ++- utilities/tariswap_test_bench/src/faucet.rs | 32 +- utilities/tariswap_test_bench/src/main.rs | 4 +- utilities/tariswap_test_bench/src/runner.rs | 17 +- utilities/tariswap_test_bench/src/tariswap.rs | 185 ++++++-- 51 files changed, 692 insertions(+), 797 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b351b60399..ad1c8e85a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9382,6 +9382,7 @@ dependencies = [ "blake2", "digest", "hex", + "indexmap 2.2.6", "lazy_static", "rand", "serde", diff --git a/applications/tari_dan_app_utilities/src/transaction_executor.rs b/applications/tari_dan_app_utilities/src/transaction_executor.rs index 3396ac21b9..c06b11fafd 100644 --- a/applications/tari_dan_app_utilities/src/transaction_executor.rs +++ b/applications/tari_dan_app_utilities/src/transaction_executor.rs @@ -54,6 +54,7 @@ impl ExecutionOutput { inputs .iter() .map(|(substate_req, substate)| { + let requested_specific_version = substate_req.version().is_some(); 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 @@ -64,6 +65,7 @@ impl ExecutionOutput { VersionedSubstateIdLockIntent::new( VersionedSubstateId::new(substate_req.substate_id().clone(), substate.version()), lock_flag, + requested_specific_version, ) }) .collect() @@ -76,6 +78,7 @@ impl ExecutionOutput { VersionedSubstateIdLockIntent::new( VersionedSubstateId::new(substate_req.substate_id().clone(), substate.version()), SubstateLockType::Read, + true, ) }) .collect() diff --git a/applications/tari_swarm_daemon/src/logger.rs b/applications/tari_swarm_daemon/src/logger.rs index f18bb0ac17..47132c48cd 100644 --- a/applications/tari_swarm_daemon/src/logger.rs +++ b/applications/tari_swarm_daemon/src/logger.rs @@ -6,7 +6,7 @@ use fern::FormatCallback; pub fn init_logger() -> Result<(), log::SetLoggerError> { fn should_skip(target: &str) -> bool { const SKIP: [&str; 3] = ["hyper::", "h2::", "tower::"]; - SKIP.iter().any(|s| target.starts_with(s)) + target.is_empty() || SKIP.iter().any(|s| target.starts_with(s)) } let colors = fern::colors::ColoredLevelConfig::new().info(fern::colors::Color::Green); 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 314e4f9098..e0799eb746 100644 --- a/applications/tari_validator_node/src/p2p/services/mempool/service.rs +++ b/applications/tari_validator_node/src/p2p/services/mempool/service.rs @@ -56,7 +56,6 @@ const LOG_TARGET: &str = "tari::validator_node::mempool::service"; #[derive(Debug)] pub struct MempoolService { - num_preshards: NumPreshards, transactions: HashSet, mempool_requests: mpsc::Receiver, epoch_manager: EpochManagerHandle, @@ -82,7 +81,6 @@ where TValidator: Validator Self { Self { - num_preshards, gossip: MempoolGossip::new(num_preshards, epoch_manager.clone(), gossip), transactions: Default::default(), mempool_requests, @@ -158,9 +156,7 @@ where TValidator: Validator; } diff --git a/bindings/dist/types/FeeCostBreakdown.d.ts b/bindings/dist/types/FeeCostBreakdown.d.ts index 6a48d0f633..81a71884bd 100644 --- a/bindings/dist/types/FeeCostBreakdown.d.ts +++ b/bindings/dist/types/FeeCostBreakdown.d.ts @@ -2,5 +2,5 @@ import type { Amount } from "./Amount"; import type { FeeBreakdown } from "./FeeBreakdown"; export interface FeeCostBreakdown { total_fees_charged: Amount; - breakdown: Array; + breakdown: FeeBreakdown; } diff --git a/bindings/dist/types/FeeReceipt.d.ts b/bindings/dist/types/FeeReceipt.d.ts index 4af221f96f..0ed2edea85 100644 --- a/bindings/dist/types/FeeReceipt.d.ts +++ b/bindings/dist/types/FeeReceipt.d.ts @@ -3,5 +3,5 @@ import type { FeeBreakdown } from "./FeeBreakdown"; export interface FeeReceipt { total_fee_payment: Amount; total_fees_paid: Amount; - cost_breakdown: Array; + cost_breakdown: FeeBreakdown; } diff --git a/bindings/dist/types/VersionedSubstateIdLockIntent.d.ts b/bindings/dist/types/VersionedSubstateIdLockIntent.d.ts index 808f69223c..bbd14311de 100644 --- a/bindings/dist/types/VersionedSubstateIdLockIntent.d.ts +++ b/bindings/dist/types/VersionedSubstateIdLockIntent.d.ts @@ -3,4 +3,5 @@ import type { VersionedSubstateId } from "./VersionedSubstateId"; export interface VersionedSubstateIdLockIntent { versioned_substate_id: VersionedSubstateId; lock_type: SubstateLockType; + require_version: boolean; } diff --git a/bindings/src/types/FeeBreakdown.ts b/bindings/src/types/FeeBreakdown.ts index f4b3941e64..24327e287b 100644 --- a/bindings/src/types/FeeBreakdown.ts +++ b/bindings/src/types/FeeBreakdown.ts @@ -2,6 +2,5 @@ import type { FeeSource } from "./FeeSource"; export interface FeeBreakdown { - source: FeeSource; - amount: number; + breakdown: Record; } diff --git a/bindings/src/types/FeeCostBreakdown.ts b/bindings/src/types/FeeCostBreakdown.ts index 863caf7ed5..61ecdddd49 100644 --- a/bindings/src/types/FeeCostBreakdown.ts +++ b/bindings/src/types/FeeCostBreakdown.ts @@ -4,5 +4,5 @@ import type { FeeBreakdown } from "./FeeBreakdown"; export interface FeeCostBreakdown { total_fees_charged: Amount; - breakdown: Array; + breakdown: FeeBreakdown; } diff --git a/bindings/src/types/FeeReceipt.ts b/bindings/src/types/FeeReceipt.ts index a4ac5366df..61690500c3 100644 --- a/bindings/src/types/FeeReceipt.ts +++ b/bindings/src/types/FeeReceipt.ts @@ -5,5 +5,5 @@ import type { FeeBreakdown } from "./FeeBreakdown"; export interface FeeReceipt { total_fee_payment: Amount; total_fees_paid: Amount; - cost_breakdown: Array; + cost_breakdown: FeeBreakdown; } diff --git a/bindings/src/types/VersionedSubstateIdLockIntent.ts b/bindings/src/types/VersionedSubstateIdLockIntent.ts index d6029330ca..6eab119e8f 100644 --- a/bindings/src/types/VersionedSubstateIdLockIntent.ts +++ b/bindings/src/types/VersionedSubstateIdLockIntent.ts @@ -5,4 +5,5 @@ import type { VersionedSubstateId } from "./VersionedSubstateId"; export interface VersionedSubstateIdLockIntent { versioned_substate_id: VersionedSubstateId; lock_type: SubstateLockType; + require_version: boolean; } diff --git a/dan_layer/consensus/src/hotstuff/common.rs b/dan_layer/consensus/src/hotstuff/common.rs index 2743181023..89b76ca24b 100644 --- a/dan_layer/consensus/src/hotstuff/common.rs +++ b/dan_layer/consensus/src/hotstuff/common.rs @@ -96,7 +96,7 @@ pub fn calculate_dummy_blocks( let mut parent_block = high_qc.as_leaf_block(); let mut current_height = high_qc.block_height() + NodeHeight(1); if current_height > new_height { - warn!( + error!( target: LOG_TARGET, "BUG: 🍼 no dummy blocks to calculate. current height {} is greater than new height {}", current_height, @@ -152,7 +152,8 @@ fn with_dummy_blocks( debug!( target: LOG_TARGET, - "🍼 calculating dummy blocks from {} to {}", + "🍼 calculating dummy blocks in epoch {} from {} to {}", + epoch, current_height, new_height, ); diff --git a/dan_layer/consensus/src/hotstuff/foreign_proposal_processor.rs b/dan_layer/consensus/src/hotstuff/foreign_proposal_processor.rs index e73176337c..b59d994224 100644 --- a/dan_layer/consensus/src/hotstuff/foreign_proposal_processor.rs +++ b/dan_layer/consensus/src/hotstuff/foreign_proposal_processor.rs @@ -14,9 +14,11 @@ use tari_dan_storage::{ TransactionAtom, TransactionPoolRecord, TransactionPoolStage, + TransactionRecord, }, StateStoreReadTransaction, }; +use tari_engine_types::commit_result::RejectReason; use tari_transaction::TransactionId; use crate::hotstuff::{block_change_set::ProposedBlockChangeSet, error::HotStuffError, ProposalValidationError}; @@ -112,6 +114,15 @@ pub fn process_foreign_block( "⚠️ Foreign committee ABORT transaction {}. Update overall decision to ABORT. Local stage: {}, Leaf: {}", tx_rec.transaction_id(), tx_rec.current_stage(), local_leaf ); + + // Add an abort execution since we previously decided to commit + let mut transaction = TransactionRecord::get(tx, tx_rec.transaction_id())?; + transaction.set_abort_reason(RejectReason::ForeignShardGroupDecidedToAbort(format!( + "Foreign shard group {} decided to abort the transaction", + foreign_committee_info.shard_group() + ))); + let exec = transaction.into_execution().expect("ABORT set above"); + proposed_block_change_set.add_transaction_execution(exec)?; } // We need to add the justify QC to the evidence because the all prepare block could not include it diff --git a/dan_layer/consensus/src/hotstuff/on_propose.rs b/dan_layer/consensus/src/hotstuff/on_propose.rs index 81b581160b..025b928f82 100644 --- a/dan_layer/consensus/src/hotstuff/on_propose.rs +++ b/dan_layer/consensus/src/hotstuff/on_propose.rs @@ -49,6 +49,7 @@ use tari_dan_storage::{ use tari_engine_types::{commit_result::RejectReason, substate::Substate}; use tari_epoch_manager::EpochManagerReader; use tari_transaction::TransactionId; +use tokio::task; use crate::{ hotstuff::{ @@ -78,6 +79,7 @@ type NextBlock = ( HashMap, ); +#[derive(Debug, Clone)] pub struct OnPropose { config: HotstuffConfig, store: TConsensusSpec::StateStore, @@ -119,7 +121,7 @@ where TConsensusSpec: ConsensusSpec &mut self, epoch: Epoch, local_committee: &Committee, - local_committee_info: &CommitteeInfo, + local_committee_info: CommitteeInfo, leaf_block: LeafBlock, is_newview_propose: bool, propose_epoch_end: bool, @@ -168,41 +170,45 @@ where TConsensusSpec: ConsensusSpec let base_layer_block_hash = current_base_layer_block_hash; 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, epoch)?; - let high_qc_cert = high_qc.get_quorum_certificate(&**tx)?; + let on_propose = self.clone(); + let (next_block, foreign_proposals) = task::spawn_blocking(move || { + on_propose.store.with_write_tx(|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, - epoch, - &leaf_block, - high_qc_cert, - validator.public_key, - local_committee_info, - // TODO: This just avoids issues with proposed transactions causing leader failures. Not sure if this - // is a good idea. - is_newview_propose, - base_layer_block_height, - base_layer_block_hash, - propose_epoch_end, - )?; + let (next_block, foreign_proposals, executed_transactions) = on_propose.build_next_block( + tx, + epoch, + &leaf_block, + high_qc_cert, + validator.public_key, + &local_committee_info, + // TODO: This just avoids issues with proposed transactions causing leader failures. Not sure if + // this is a good idea. + is_newview_propose, + base_layer_block_height, + base_layer_block_hash, + propose_epoch_end, + )?; - // Add executions for this block - if !executed_transactions.is_empty() { - debug!( - target: LOG_TARGET, - "Saving {} executed transaction(s) for block {}", - executed_transactions.len(), - next_block.id() - ); - } - for executed in executed_transactions.into_values() { - executed.for_block(*next_block.id()).insert_if_required(tx)?; - } + // Add executions for this block + if !executed_transactions.is_empty() { + debug!( + target: LOG_TARGET, + "Saving {} executed transaction(s) for block {}", + executed_transactions.len(), + next_block.id() + ); + } + for executed in executed_transactions.into_values() { + executed.for_block(*next_block.id()).insert_if_required(tx)?; + } - next_block.as_last_proposed().set(tx)?; - Ok::<_, HotStuffError>((next_block, foreign_proposals)) - })?; + next_block.as_last_proposed().set(tx)?; + Ok::<_, HotStuffError>((next_block, foreign_proposals)) + }) + }) + .await??; info!( target: LOG_TARGET, 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 406ecdeaf8..280b22e0b7 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 @@ -894,6 +894,8 @@ where TConsensusSpec: ConsensusSpec let execution = self.execute_transaction(tx, block.id(), block.epoch(), tx_rec.transaction_id())?; let mut execution = execution.into_transaction_execution(); + // TODO: check the diff is valid against the provided input evidence (correct locks etc). + // 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, and this // block has the correct evidence (TODO: verify the atom) so this should be fine. 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 f74ef6ef99..84e58b5d96 100644 --- a/dan_layer/consensus/src/hotstuff/on_receive_foreign_proposal.rs +++ b/dan_layer/consensus/src/hotstuff/on_receive_foreign_proposal.rs @@ -121,402 +121,6 @@ where TConsensusSpec: ConsensusSpec Ok(()) } - // #[allow(clippy::too_many_lines)] - // fn process_foreign_block( - // &self, - // tx: &mut ::WriteTransaction<'_>, - // foreign_proposal: ForeignProposal, - // foreign_committee_info: &CommitteeInfo, - // local_committee_info: &CommitteeInfo, - // ) -> Result<(), HotStuffError> { - // let ForeignProposal { - // block, - // justify_qc, - // mut block_pledge, - // .. - // } = foreign_proposal; - // let local_leaf = LeafBlock::get(&**tx)?; - // // We only want to save the QC once if applicable - // let mut command_count = 0usize; - // - // for cmd in block.commands() { - // match cmd { - // Command::LocalPrepare(atom) => { - // if !local_committee_info.includes_any_address(atom.evidence.substate_addresses_iter()) { - // continue; - // } - // - // debug!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Command: LocalPrepare({}, {}), block: {}", - // atom.id,atom.decision, block.id(), - // ); - // - // let Some(mut tx_rec) = self.transaction_pool.get(tx, local_leaf, &atom.id).optional()? else { - // // If this happens, it could be a bug in the foreign missing transaction handling - // warn!( - // target: LOG_TARGET, - // "⚠️ NEVER HAPPEN: Foreign proposal received for transaction {} but this transaction is - // not in the pool.", atom.id - // ); - // continue; - // }; - // - // if tx_rec.current_stage() > TransactionPoolStage::LocalPrepared { - // // TODO: This can happen if the foreign shard group is only responsible for outputs (the - // input // SGs have already progressed to LocalAccept) in which case it is safe to ignore - // this command. // However we should not send the proposal in the first place (assuming it - // does not involve any // other shard-applicable transactions). - // warn!( - // target: LOG_TARGET, - // "⚠️ Foreign LocalPrepare proposal ({}) received LOCAL_PREPARE for transaction {} but - // current transaction stage is {}. Ignoring.", block, - // tx_rec.transaction_id(), tx_rec.current_stage() - // ); - // continue; - // } - // - // command_count += 1; - // - // let remote_decision = atom.decision; - // let local_decision = tx_rec.current_decision(); - // if remote_decision.is_abort() && local_decision.is_commit() { - // info!( - // target: LOG_TARGET, - // "⚠️ Foreign committee ABORT transaction {}. Update overall decision to ABORT. Local - // stage: {}, Leaf: {}", tx_rec.transaction_id(), tx_rec.current_stage(), local_leaf - // ); - // } - // - // // We need to add the justify QC to the evidence because the all prepare block could not include - // it // yet - // let mut foreign_evidence = atom.evidence.clone(); - // foreign_evidence.add_qc_evidence(foreign_committee_info, *justify_qc.id()); - // - // // Update the transaction record with any new information provided by this foreign block - // tx_rec.update_remote_data( - // tx, - // remote_decision, - // *justify_qc.id(), - // foreign_committee_info, - // foreign_evidence, - // )?; - // - // self.validate_and_add_pledges( - // tx, - // &tx_rec, - // block.id(), - // atom, - // &mut block_pledge, - // foreign_committee_info, - // )?; - // - // if tx_rec.current_stage().is_new() { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: (Initial sequence from LocalPrepare) Transaction is ready for - // Prepare({}, {}) Local Stage: {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // // If the transaction is New, we're waiting for all foreign pledges. Propose transaction once - // we // have them. - // - // // CASE: One foreign SG is involved in all inputs and executed the transaction, local SG is - // // involved in the outputs - // let transaction = tx_rec.get_transaction(&**tx)?; - // let is_ready = local_committee_info.includes_substate_id(&transaction.to_receipt_id().into()) - // || transaction.has_any_local_inputs(local_committee_info) || - // transaction.has_all_foreign_input_pledges(&**tx, local_committee_info)?; - // - // if is_ready { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: (Initial sequence from LocalPrepare) Transaction is ready for - // Prepare({}, {}) Local Stage: {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // tx_rec.add_pending_status_update(tx, local_leaf, TransactionPoolStage::New, true)?; - // } else { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: (Initial sequence from LocalPrepare) Transaction is NOT ready - // for Prepare({}, {}) Local Stage: {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // } - // } else if tx_rec.current_stage().is_local_prepared() && - // tx_rec.evidence().all_input_addresses_justified() - // { - // // If all shards are complete, and we've already received our LocalPrepared, we can set out - // // LocalPrepared transaction as ready to propose ACCEPT. If we have not received - // // the local LocalPrepared, the transition will happen when we receive the local - // // block. - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Transaction is ready for propose AllPrepared({}, {}) Local Stage: - // {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // - // tx_rec.add_pending_status_update(tx, local_leaf, TransactionPoolStage::LocalPrepared, true)?; - // // TODO: there is a race condition between the local node receiving the foreign LocalPrepare - // and // the leader proposing AllPrepare. If the latter comes first, this node - // // will not vote on this block which leads inevitably to erroneous - // // leader failures. Currently we simply vote ACCEPT on the block, with is ready == false, so - // we // need to handle this here. When we confirm foreign proposals correctly, we can - // remove this. } else { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Transaction is NOT ready for AllPrepared({}, {}) Local Stage: {}, - // All Justified: {}. Waiting for local proposal.", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage(), - // tx_rec.evidence().all_input_addresses_justified() - // ); - // tx_rec.add_pending_status_update(tx, local_leaf, tx_rec.current_stage(), tx_rec.is_ready())?; - // } - // }, - // Command::LocalAccept(atom) => { - // if !local_committee_info.includes_any_address(atom.evidence.substate_addresses_iter()) { - // continue; - // } - // - // debug!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Command: LocalAccept({}, {}), block: {}", - // atom.id, atom.decision, block.id(), - // ); - // - // let Some(mut tx_rec) = self.transaction_pool.get(tx, local_leaf, &atom.id).optional()? else { - // warn!( - // target: LOG_TARGET, - // "⚠️ NEVER HAPPEN: Foreign proposal received for transaction {} but this transaction is - // not in the pool.", atom.id - // ); - // continue; - // }; - // - // if tx_rec.current_stage() > TransactionPoolStage::LocalAccepted { - // warn!( - // target: LOG_TARGET, - // "⚠️ Foreign proposal {} received LOCAL_ACCEPT for transaction {} but current transaction - // stage is {}. Ignoring.", block, - // tx_rec.transaction_id(), - // tx_rec.current_stage(), - // ); - // continue; - // } - // - // command_count += 1; - // - // let remote_decision = atom.decision; - // let local_decision = tx_rec.current_local_decision(); - // if remote_decision.is_abort() && local_decision.is_commit() { - // info!( - // target: LOG_TARGET, - // "⚠️ Foreign ABORT {}. Update overall decision to ABORT. Local stage: {}, Leaf: {}", - // tx_rec.transaction_id(), tx_rec.current_stage(), local_leaf - // ); - // } - // - // // We need to add the justify QC to the evidence because the all prepare block could not include - // it // yet - // let mut foreign_evidence = atom.evidence.clone(); - // foreign_evidence.add_qc_evidence(foreign_committee_info, *justify_qc.id()); - // - // // Update the transaction record with any new information provided by this foreign block - // tx_rec.update_remote_data( - // tx, - // remote_decision, - // *justify_qc.id(), - // foreign_committee_info, - // foreign_evidence, - // )?; - // - // self.validate_and_add_pledges( - // tx, - // &tx_rec, - // block.id(), - // atom, - // &mut block_pledge, - // foreign_committee_info, - // )?; - // - // // Good debug info - // // tx_rec.evidence().iter().for_each(|(addr, ev)| { - // // let includes_local = local_committee_info.includes_substate_address(addr); - // // log::error!( - // // target: LOG_TARGET, - // // "🐞 LOCALACCEPT EVIDENCE (l={}, f={}) {}: {}", includes_local, !includes_local, addr, - // ev // ); - // // }); - // - // if tx_rec.current_stage().is_new() { - // // If the transaction is New, we're waiting for all foreign pledges. Propose transaction once - // we // have them. - // // CASE: Foreign SGs have pledged all inputs and executed the transaction, local SG is - // involved // in the outputs - // let transaction = tx_rec.get_transaction(&**tx)?; - // let is_ready = local_committee_info.includes_substate_id(&transaction.to_receipt_id().into()) - // || transaction.has_any_local_inputs(local_committee_info) || - // transaction.has_all_foreign_input_pledges(&**tx, local_committee_info)?; - // if is_ready { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: (Initial sequence from LocalAccept) Transaction is ready for - // Prepare({}, {}) Local Stage: {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // tx_rec.add_pending_status_update(tx, local_leaf, TransactionPoolStage::New, true)?; - // } else { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: (Initial sequence from LocalAccept) Transaction is NOT ready - // for Prepare({}, {}) Local Stage: {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // } - // } else if tx_rec.current_stage().is_local_accepted() && - // tx_rec.evidence().all_addresses_justified() { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Transaction is ready for propose ALL_ACCEPT({}, {}) Local Stage: - // {}", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage() - // ); - // - // tx_rec.add_pending_status_update(tx, local_leaf, TransactionPoolStage::LocalAccepted, true)?; - // } else { - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Transaction is NOT ready for ALL_ACCEPT({}, {}) Local Stage: {}, - // All Justified: {}. Waiting for local proposal.", tx_rec.transaction_id(), - // tx_rec.current_decision(), - // tx_rec.current_stage(), - // tx_rec.evidence().all_addresses_justified() - // ); - // // 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 - // Command::EndEpoch => { - // warn!( - // target: LOG_TARGET, - // "❓️ NEVER HAPPEN: Foreign proposal received for block {} contains an EndEpoch command. This - // is invalid behaviour.", block.id() - // ); - // continue; - // }, - // // TODO(perf): Can we find a way to exclude these unused commands to reduce message size? - // Command::AllAccept(_) | - // Command::SomeAccept(_) | - // Command::AllPrepare(_) | - // Command::SomePrepare(_) | - // Command::Prepare(_) | - // Command::LocalOnly(_) | - // Command::ForeignProposal(_) | - // Command::MintConfidentialOutput(_) => { - // // Disregard - // continue; - // }, - // } - // } - // - // info!( - // target: LOG_TARGET, - // "🧩 FOREIGN PROPOSAL: Processed {} commands from foreign block {}", - // command_count, - // block.id() - // ); - // if command_count == 0 { - // warn!( - // target: LOG_TARGET, - // "⚠️ FOREIGN PROPOSAL: No commands were applicable for foreign block {}. Ignoring.", - // block.id() - // ); - // } - // - // Ok(()) - // } - // - // fn validate_and_add_pledges( - // &self, - // tx: &mut ::WriteTransaction<'_>, - // tx_rec: &TransactionPoolRecord, - // block_id: &BlockId, - // atom: &TransactionAtom, - // block_pledge: &mut BlockPledge, - // foreign_committee_info: &CommitteeInfo, - // ) -> Result<(), HotStuffError> { - // #[allow(clippy::mutable_key_type)] - // let maybe_pledges = if atom.decision.is_commit() { - // let pledges = block_pledge.remove_transaction_pledges(&atom.id).ok_or_else(|| { - // HotStuffError::ForeignNodeOmittedTransactionPledges { - // foreign_block_id: *block_id, - // transaction_id: atom.id, - // } - // })?; - // - // // Validate that provided evidence is correct - // // TODO: there are a lot of validations to be done on evidence and the foreign block in general, - // // this is here as a sanity check and should change to not be a fatal error in consensus - // for pledge in &pledges { - // let address = pledge.versioned_substate_id().to_substate_address(); - // let evidence = - // atom.evidence - // .get(&address) - // .ok_or_else(|| ProposalValidationError::ForeignInvalidPledge { - // block_id: *block_id, - // transaction_id: atom.id, - // details: format!("Pledge {pledge} for address {address} not found in evidence"), - // })?; - // if evidence.lock.is_output() && pledge.is_input() { - // return Err(ProposalValidationError::ForeignInvalidPledge { - // block_id: *block_id, - // transaction_id: atom.id, - // details: format!("Pledge {pledge} is an input but evidence is an output for address - // {address}"), } - // .into()); - // } - // if !evidence.lock.is_output() && pledge.is_output() { - // return Err(ProposalValidationError::ForeignInvalidPledge { - // block_id: *block_id, - // transaction_id: atom.id, - // details: format!("Pledge {pledge} is an output but evidence is an input for address - // {address}"), } - // .into()); - // } - // } - // Some(pledges) - // } else { - // if block_pledge.remove_transaction_pledges(&atom.id).is_some() { - // return Err(ProposalValidationError::ForeignInvalidPledge { - // block_id: *block_id, - // transaction_id: atom.id, - // details: "Remote decided ABORT but provided pledges".to_string(), - // } - // .into()); - // } - // None - // }; - // - // if let Some(pledges) = maybe_pledges { - // // If the foreign shard has committed the transaction, we can add the pledges to the transaction - // // record - // tx_rec.add_foreign_pledges(tx, foreign_committee_info.shard_group(), pledges)?; - // } - // - // Ok(()) - // } - fn validate_proposed_block( &self, candidate_block: &Block, diff --git a/dan_layer/consensus/src/hotstuff/substate_store/error.rs b/dan_layer/consensus/src/hotstuff/substate_store/error.rs index 223dcc4a91..18604ed0e5 100644 --- a/dan_layer/consensus/src/hotstuff/substate_store/error.rs +++ b/dan_layer/consensus/src/hotstuff/substate_store/error.rs @@ -6,7 +6,7 @@ use tari_dan_storage::StorageError; #[derive(Debug, thiserror::Error)] pub enum SubstateStoreError { - #[error(transparent)] + #[error("Lock failure: {0}")] LockFailed(#[from] LockFailedError), #[error("Substate {id} not found")] SubstateNotFound { id: VersionedSubstateId }, @@ -47,7 +47,8 @@ impl SubstateStoreError { pub enum LockFailedError { #[error("Substate {id} not found")] SubstateNotFound { id: VersionedSubstateId }, - + #[error("Substate {id} is DOWN")] + SubstateIsDown { id: VersionedSubstateId }, #[error( "Failed to {requested_lock} lock substate {substate_id} due to conflict with existing {existing_lock} lock" )] 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 d40dfedac4..3253d6c686 100644 --- a/dan_layer/consensus/src/hotstuff/substate_store/pending_store.rs +++ b/dan_layer/consensus/src/hotstuff/substate_store/pending_store.rs @@ -79,6 +79,9 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> ReadableSubstateStore for PendingSu let Some(substate) = SubstateRecord::get(self.read_transaction(), &id.to_substate_address()).optional()? else { return Err(SubstateStoreError::SubstateNotFound { id: id.clone() }); }; + if substate.is_destroyed() { + return Err(SubstateStoreError::SubstateIsDown { id: id.clone() }); + } Ok(substate.into_substate()) } } @@ -151,6 +154,11 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor } let substate = SubstateRecord::get_latest(self.read_transaction(), id)?; + if substate.is_destroyed() { + return Err(SubstateStoreError::SubstateIsDown { + id: substate.to_versioned_substate_id(), + }); + } Ok(substate.into_substate()) } @@ -171,8 +179,10 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor Err(err) => { let error = err.ok_lock_failed()?; match error { + err @ LockFailedError::SubstateIsDown { .. } | err @ LockFailedError::SubstateNotFound { .. } => { - // If the substate does not exist, the transaction is invalid + // If the substate does not exist or is not UP (unversioned: previously DOWNed and never + // UPed), the transaction is invalid let index = lock_status.add_failed(err); lock_status.hard_conflict_idx = Some(index); }, @@ -215,7 +225,7 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor if requested_lock_type.is_output() { self.assert_not_exist(&versioned_substate_id)?; } else { - self.assert_is_up(&versioned_substate_id)?; + self.lock_assert_is_up(&versioned_substate_id)?; } let version = versioned_substate_id.version(); @@ -422,6 +432,16 @@ impl<'a, 'tx, TStore: StateStore + 'a + 'tx> PendingSubstateStore<'a, 'tx, TStor } } + fn lock_assert_is_up(&self, id: &VersionedSubstateId) -> Result<(), SubstateStoreError> { + match self.assert_is_up(id) { + Ok(_) => Ok(()), + // Converts a substate store error to a LockFailedError (TODO: improve) + Err(SubstateStoreError::SubstateIsDown { id }) => Err(LockFailedError::SubstateIsDown { id }.into()), + Err(SubstateStoreError::SubstateNotFound { id }) => Err(LockFailedError::SubstateNotFound { id }.into()), + Err(err) => Err(err), + } + } + fn assert_is_down(&self, id: &VersionedSubstateId) -> Result<(), SubstateStoreError> { if let Some(change) = self.get_pending(&id.to_substate_address()) { if change.is_up() { diff --git a/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs b/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs index 36dcd6c838..239130008d 100644 --- a/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs +++ b/dan_layer/consensus/src/hotstuff/transaction_manager/manager.rs @@ -134,8 +134,8 @@ impl> Ok(inputs) => inputs, Err(err) => { warn!(target: LOG_TARGET, "⚠️ PREPARE: failed to resolve local inputs: {err}"); - // We only expect not found errors here. If we get any other error, this is fatal. - if !err.is_not_found_error() { + // We only expect not found or down errors here. If we get any other error, this is fatal. + if !err.is_not_found_error() && !err.is_substate_down_error() { return Err(err); } let is_local_only = local_committee_info.includes_all_substate_addresses( @@ -235,7 +235,11 @@ 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)| { - SubstateRequirementLockIntent::write(substate_id.clone(), substate.version()) + if substate_id.substate_id.is_read_only() { + SubstateRequirementLockIntent::read(substate_id.clone(), substate.version()) + } else { + SubstateRequirementLockIntent::write(substate_id.clone(), substate.version()) + } }); store.try_lock_all(transaction_id, requested_locks, false)? } else { @@ -246,6 +250,7 @@ impl> }; if let Some(err) = lock_summary.hard_conflict() { + warn!(target: LOG_TARGET, "⚠️ PREPARE: Hard conflict when locking inputs: {err}"); 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 06c7aed791..2f800cb914 100644 --- a/dan_layer/consensus/src/hotstuff/transaction_manager/prepared.rs +++ b/dan_layer/consensus/src/hotstuff/transaction_manager/prepared.rs @@ -128,11 +128,10 @@ impl MultiShardPreparedTransaction { self.transaction .transaction() .all_inputs_iter() - .map(|input| input.or_zero_version()) - .map(|id| VersionedSubstateIdLockIntent::new(id, SubstateLockType::Read)), + .map(|input| VersionedSubstateIdLockIntent::from_requirement(input, SubstateLockType::Read)), self.outputs .iter() - .map(|id| VersionedSubstateIdLockIntent::new(id.clone(), SubstateLockType::Output)), + .map(|id| VersionedSubstateIdLockIntent::output(id.clone())), ); } @@ -146,13 +145,13 @@ impl MultiShardPreparedTransaction { // 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())) - .map(|id| VersionedSubstateIdLockIntent::new(id, SubstateLockType::Write)); + .map(|id| VersionedSubstateIdLockIntent::write(id, true)); let outputs = self .outputs() .iter() .cloned() - .map(|id| VersionedSubstateIdLockIntent::new(id, SubstateLockType::Output)); + .map(VersionedSubstateIdLockIntent::output); Evidence::from_inputs_and_outputs(inputs, outputs) } diff --git a/dan_layer/consensus/src/hotstuff/worker.rs b/dan_layer/consensus/src/hotstuff/worker.rs index 28c0a2fd09..1e8a629f81 100644 --- a/dan_layer/consensus/src/hotstuff/worker.rs +++ b/dan_layer/consensus/src/hotstuff/worker.rs @@ -267,13 +267,24 @@ impl HotstuffWorker { ); tokio::select! { - Some(result) = self.on_inbound_message.next_message(current_epoch, current_height) => { - if let Err(err) = self.on_unvalidated_message(current_epoch, current_height, result, &local_committee_info).await { - self.hooks.on_error(&err); - error!(target: LOG_TARGET, "🚨Error handling new message: {}", err); + // BIASED: + biased; + + // Epoch manager events are rare, but have priority if they happen + Ok(event) = epoch_manager_events.recv() => { + self.on_epoch_manager_event(event).await?; + }, + + // Proposing is highest priority + maybe_leaf_block = on_force_beat.wait() => { + self.hooks.on_beat(); + if let Err(e) = self.propose_if_leader(current_epoch, maybe_leaf_block, &local_committee_info).await { + self.on_failure("propose_if_leader", &e).await; + return Err(e); } }, + // Sequencing transactions is next highest priority Some((tx_id, pending)) = self.rx_new_transactions.recv() => { if let Err(err) = self.on_new_transaction(tx_id, pending, current_epoch, current_height, &local_committee_info).await { self.hooks.on_error(&err); @@ -281,11 +292,14 @@ impl HotstuffWorker { } }, - Ok(event) = epoch_manager_events.recv() => { - self.on_epoch_manager_event(event).await?; + Some(result) = self.on_inbound_message.next_message(current_epoch, current_height) => { + if let Err(err) = self.on_unvalidated_message(current_epoch, current_height, result, &local_committee_info).await { + self.hooks.on_error(&err); + error!(target: LOG_TARGET, "🚨Error handling new message: {}", err); + } }, - // TODO: This channel is used to work around some design-flaws in missing transactions handling. + // TODO: This channel is used to work around some design-flaws in missing transactions handling. // We cannot simply call check_if_block_can_be_unparked in dispatch_hotstuff_message as that creates a cycle. // One suggestion is to refactor consensus to emit events (kinda like libp2p does) and handle those events. // This should be easy to reason about and avoid a large depth of async calls and "callback channels". @@ -303,14 +317,6 @@ impl HotstuffWorker { } }, - maybe_leaf_block = on_force_beat.wait() => { - self.hooks.on_beat(); - if let Err(e) = self.propose_if_leader(current_epoch, maybe_leaf_block, &local_committee_info).await { - self.on_failure("propose_if_leader", &e).await; - return Err(e); - } - }, - new_height = on_leader_timeout.wait() => { if let Err(e) = self.on_leader_timeout(current_epoch, new_height).await { self.on_failure("on_leader_timeout", &e).await; @@ -642,14 +648,14 @@ impl HotstuffWorker { .handle( epoch, &local_committee, - local_committee_info, + *local_committee_info, leaf_block, is_newview_propose, propose_epoch_end, ) .await?; } else { - // We can make this a warm/error in future, but for now I want to be sure this never happens + // We can make this a warn/error in future, but for now I want to be sure this never happens debug_assert!( !is_newview_propose, "propose_if_leader called with is_newview_propose=true but we're not the leader" diff --git a/dan_layer/consensus/src/traits/transaction_executor.rs b/dan_layer/consensus/src/traits/transaction_executor.rs index 364e5b943f..bb624b644f 100644 --- a/dan_layer/consensus/src/traits/transaction_executor.rs +++ b/dan_layer/consensus/src/traits/transaction_executor.rs @@ -24,6 +24,14 @@ pub enum BlockTransactionExecutorError { #[error("BUG: Invariant error: {0}")] InvariantError(String), } +impl BlockTransactionExecutorError { + pub fn is_substate_down_error(&self) -> bool { + matches!( + self, + BlockTransactionExecutorError::SubstateStoreError(SubstateStoreError::SubstateIsDown { .. }) + ) + } +} impl IsNotFoundError for BlockTransactionExecutorError { fn is_not_found_error(&self) -> bool { diff --git a/dan_layer/consensus_tests/src/consensus.rs b/dan_layer/consensus_tests/src/consensus.rs index ee6a665c69..7c0c4d3d5e 100644 --- a/dan_layer/consensus_tests/src/consensus.rs +++ b/dan_layer/consensus_tests/src/consensus.rs @@ -12,7 +12,7 @@ use std::time::Duration; use tari_common_types::types::PrivateKey; use tari_consensus::hotstuff::HotStuffError; -use tari_dan_common_types::{optional::Optional, Epoch, NodeHeight, SubstateLockType, SubstateRequirement}; +use tari_dan_common_types::{optional::Optional, Epoch, NodeHeight, SubstateRequirement}; use tari_dan_storage::{ consensus_models::{BlockId, Command, Decision, TransactionRecord, VersionedSubstateIdLockIntent}, StateStore, @@ -397,7 +397,10 @@ async fn multishard_local_inputs_foreign_outputs() { .build(), Decision::Commit, 1, - inputs.into_iter().map(VersionedSubstateIdLockIntent::write).collect(), + inputs + .into_iter() + .map(|input| VersionedSubstateIdLockIntent::write(input, true)) + .collect(), outputs, ); test.send_transaction_to_destination(TestVnDestination::All, tx1.clone()) @@ -456,7 +459,7 @@ async fn multishard_local_inputs_and_outputs_foreign_outputs() { inputs_0 .into_iter() .chain(inputs_1) - .map(VersionedSubstateIdLockIntent::write) + .map(|input| VersionedSubstateIdLockIntent::write(input, true)) .collect(), outputs_0.into_iter().chain(outputs_2).collect(), ); @@ -526,7 +529,10 @@ async fn multishard_output_conflict_abort() { tx, Decision::Commit, 1, - inputs.into_iter().map(VersionedSubstateIdLockIntent::write).collect(), + inputs + .into_iter() + .map(|input| VersionedSubstateIdLockIntent::write(input, true)) + .collect(), resulting_outputs, ); assert_ne!(tx1.id(), tx2.id()); @@ -578,9 +584,7 @@ async fn single_shard_inputs_from_previous_outputs() { .resulting_outputs() .unwrap() .iter() - .map(|output| { - VersionedSubstateIdLockIntent::new(output.versioned_substate_id().clone(), SubstateLockType::Write) - }) + .map(|output| VersionedSubstateIdLockIntent::write(output.versioned_substate_id().clone(), true)) .collect::>(); let tx2 = Transaction::builder() @@ -658,9 +662,7 @@ async fn multishard_inputs_from_previous_outputs() { 1, resulting_outputs .into_iter() - .map(|output| { - VersionedSubstateIdLockIntent::new(output.into_versioned_substate_id(), SubstateLockType::Write) - }) + .map(|output| VersionedSubstateIdLockIntent::write(output.into_versioned_substate_id(), true)) .collect(), vec![], ); @@ -723,7 +725,7 @@ async fn single_shard_input_conflict() { *tx1.id(), Decision::Commit, 0, - vec![VersionedSubstateIdLockIntent::read(substate_id.clone())], + vec![VersionedSubstateIdLockIntent::read(substate_id.clone(), true)], vec![], ), ) @@ -733,7 +735,7 @@ async fn single_shard_input_conflict() { *tx2.id(), Decision::Commit, 0, - vec![VersionedSubstateIdLockIntent::write(substate_id)], + vec![VersionedSubstateIdLockIntent::write(substate_id, true)], vec![], ), ); @@ -960,7 +962,10 @@ async fn single_shard_unversioned_inputs() { *tx.id(), Decision::Commit, 0, - inputs.into_iter().map(VersionedSubstateIdLockIntent::write).collect(), + inputs + .into_iter() + .map(|input| VersionedSubstateIdLockIntent::write(input, true)) + .collect(), vec![], ), ); diff --git a/dan_layer/consensus_tests/src/support/harness.rs b/dan_layer/consensus_tests/src/support/harness.rs index 86c48e1c37..555c5823d4 100644 --- a/dan_layer/consensus_tests/src/support/harness.rs +++ b/dan_layer/consensus_tests/src/support/harness.rs @@ -17,7 +17,6 @@ use tari_dan_common_types::{ NodeHeight, NumPreshards, ShardGroup, - SubstateLockType, VersionedSubstateId, }; use tari_dan_storage::{ @@ -98,7 +97,7 @@ impl Test { fee, all_inputs .into_iter() - .map(|i| VersionedSubstateIdLockIntent::new(i, SubstateLockType::Write)) + .map(|i| VersionedSubstateIdLockIntent::write(i, true)) .collect(), vec![], ); @@ -173,7 +172,7 @@ impl Test { fee, all_inputs .into_iter() - .map(|i| VersionedSubstateIdLockIntent::new(i, SubstateLockType::Write)) + .map(|i| VersionedSubstateIdLockIntent::write(i, true)) .collect(), outputs, ) diff --git a/dan_layer/consensus_tests/src/support/transaction.rs b/dan_layer/consensus_tests/src/support/transaction.rs index c9d4858164..91bbe7a6f5 100644 --- a/dan_layer/consensus_tests/src/support/transaction.rs +++ b/dan_layer/consensus_tests/src/support/transaction.rs @@ -5,7 +5,7 @@ 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_common_types::VersionedSubstateId; use tari_dan_storage::consensus_models::{ Decision, ExecutedTransaction, @@ -16,7 +16,7 @@ use tari_dan_storage::consensus_models::{ use tari_engine_types::{ commit_result::{ExecuteResult, FinalizeResult, RejectReason, TransactionResult}, component::{ComponentBody, ComponentHeader}, - fees::FeeReceipt, + fees::{FeeBreakdown, FeeReceipt}, substate::{Substate, SubstateDiff, SubstateId}, transaction_receipt::{TransactionReceipt, TransactionReceiptAddress}, }; @@ -94,7 +94,7 @@ pub fn create_execution_result_for_transaction( fee_receipt: FeeReceipt { total_fee_payment: fee.try_into().unwrap(), total_fees_paid: fee.try_into().unwrap(), - cost_breakdown: vec![], + cost_breakdown: FeeBreakdown::default(), }, }), ); @@ -105,13 +105,10 @@ pub fn create_execution_result_for_transaction( )) }; - resulting_outputs.push(VersionedSubstateIdLockIntent::new( - VersionedSubstateId::new( - SubstateId::TransactionReceipt(TransactionReceiptAddress::from(tx_id)), - 0, - ), - SubstateLockType::Output, - )); + resulting_outputs.push(VersionedSubstateIdLockIntent::output(VersionedSubstateId::new( + SubstateId::TransactionReceipt(TransactionReceiptAddress::from(tx_id)), + 0, + ))); TransactionExecution::new( tx_id, @@ -119,7 +116,7 @@ pub fn create_execution_result_for_transaction( finalize: FinalizeResult::new(tx_id.into_array().into(), vec![], vec![], result, FeeReceipt { total_fee_payment: fee.try_into().unwrap(), total_fees_paid: fee.try_into().unwrap(), - cost_breakdown: vec![], + cost_breakdown: FeeBreakdown::default(), }), execution_time: Duration::from_secs(0), }, diff --git a/dan_layer/engine/src/runtime/fee_state.rs b/dan_layer/engine/src/runtime/fee_state.rs index 853bc8c425..deba5c7826 100644 --- a/dan_layer/engine/src/runtime/fee_state.rs +++ b/dan_layer/engine/src/runtime/fee_state.rs @@ -7,7 +7,7 @@ use tari_template_lib::models::{Amount, VaultId}; #[derive(Debug, Clone, Default)] pub struct FeeState { pub fee_payments: Vec<(ResourceContainer, VaultId)>, - pub fee_charges: Vec, + pub fee_charges: FeeBreakdown, } impl FeeState { @@ -16,7 +16,7 @@ impl FeeState { } pub fn total_charges(&self) -> u64 { - self.fee_charges.iter().map(|breakdown| breakdown.amount).sum() + self.fee_charges.get_total() } pub fn total_payments(&self) -> Amount { diff --git a/dan_layer/engine/src/runtime/tracker.rs b/dan_layer/engine/src/runtime/tracker.rs index e535ba8863..05b411310f 100644 --- a/dan_layer/engine/src/runtime/tracker.rs +++ b/dan_layer/engine/src/runtime/tracker.rs @@ -33,7 +33,7 @@ use tari_engine_types::{ component::{ComponentBody, ComponentHeader}, confidential::UnclaimedConfidentialOutput, events::Event, - fees::{FeeBreakdown, FeeSource}, + fees::FeeSource, indexed_value::{IndexedValue, IndexedWellKnownTypes}, lock::LockFlag, logs::LogEntry, @@ -269,7 +269,7 @@ impl StateTracker { self.write_with(|state| { debug!(target: LOG_TARGET, "Add fee: source: {:?}, amount: {}", source, amount); - state.fee_state_mut().fee_charges.push(FeeBreakdown { source, amount }); + state.fee_state_mut().fee_charges.insert(source, amount); }) } diff --git a/dan_layer/engine/src/runtime/working_state.rs b/dan_layer/engine/src/runtime/working_state.rs index 7e25dc08f1..90d1e9fb45 100644 --- a/dan_layer/engine/src/runtime/working_state.rs +++ b/dan_layer/engine/src/runtime/working_state.rs @@ -23,7 +23,6 @@ use tari_engine_types::{ lock::LockFlag, logs::LogEntry, non_fungible::NonFungibleContainer, - non_fungible_index::NonFungibleIndex, proof::{ContainerRef, LockedResource, Proof}, resource::Resource, resource_container::{ResourceContainer, ResourceError}, @@ -42,7 +41,6 @@ use tari_template_lib::{ BucketId, ComponentAddress, NonFungibleAddress, - NonFungibleIndexAddress, ProofId, UnclaimedConfidentialOutputAddress, VaultId, @@ -505,35 +503,32 @@ impl WorkingState { ); let mut token_ids = BTreeSet::new(); - let resource = self.get_resource(locked_resource)?; + // let resource = self.get_resource(locked_resource)?; // TODO: This isn't correct (assumes tokens are never burnt), we'll need to rethink this - let mut index = resource - .total_supply() - .as_u64_checked() - .ok_or(RuntimeError::InvalidAmount { - amount: resource.total_supply(), - reason: "Could not convert to u64".to_owned(), - })?; + // let mut index = resource + // .total_supply() + // .as_u64_checked() + // .ok_or(RuntimeError::InvalidAmount { + // amount: resource.total_supply(), + // reason: "Could not convert to u64".to_owned(), + // })?; for (id, (data, mut_data)) in tokens { - let nft_address = NonFungibleAddress::new(resource_address, id.clone()); - let addr = SubstateId::NonFungible(nft_address.clone()); + let nft_address = NonFungibleAddress::new(resource_address, id); + let token_id = nft_address.id().clone(); + let addr = SubstateId::NonFungible(nft_address); if self.substate_exists(&addr)? { - return Err(RuntimeError::DuplicateNonFungibleId { - token_id: nft_address.id().clone(), - }); - } - if !token_ids.insert(id.clone()) { - // Can't happen - return Err(RuntimeError::DuplicateNonFungibleId { token_id: id }); + return Err(RuntimeError::DuplicateNonFungibleId { token_id }); + } else { + token_ids.insert(token_id); + self.new_substate(addr.clone(), NonFungibleContainer::new(data, mut_data))?; } - self.new_substate(addr.clone(), NonFungibleContainer::new(data, mut_data))?; // for each new nft we also create an index to be allow resource scanning - let index_address = NonFungibleIndexAddress::new(resource_address, index); - index += 1; - let nft_index = NonFungibleIndex::new(nft_address); - self.new_substate(index_address, nft_index)?; + // let index_address = NonFungibleIndexAddress::new(resource_address, index); + // index += 1; + // let nft_index = NonFungibleIndex::new(nft_address); + // self.new_substate(index_address, nft_index)?; } ResourceContainer::non_fungible(resource_address, token_ids) @@ -900,7 +895,7 @@ impl WorkingState { fee_receipt: FeeReceipt { total_fee_payment, total_fees_paid: fee_resource.amount(), - cost_breakdown: self.fee_state.fee_charges.drain(..).collect(), + cost_breakdown: mem::take(&mut self.fee_state.fee_charges), }, }) } diff --git a/dan_layer/engine/tests/test.rs b/dan_layer/engine/tests/test.rs index dffc786de9..b159233084 100644 --- a/dan_layer/engine/tests/test.rs +++ b/dan_layer/engine/tests/test.rs @@ -1246,112 +1246,112 @@ mod tickets { } } -mod nft_indexes { - use super::*; - - fn setup() -> ( - TemplateTest, - (ComponentAddress, NonFungibleAddress), - ComponentAddress, - SubstateId, - ) { - let mut template_test = TemplateTest::new(vec!["tests/templates/nft/nft_list"]); - - let (account_address, owner_token, _) = template_test.create_funded_account(); - let nft_component: ComponentAddress = template_test.call_function("SparkleNft", "new", args![], vec![]); - - let nft_resx = template_test.get_previous_output_address(SubstateType::Resource); - - // TODO: cleanup - (template_test, (account_address, owner_token), nft_component, nft_resx) - } - - #[test] - #[allow(clippy::too_many_lines)] - fn new_nft_index() { - let (mut template_test, (account_address, owner_proof), nft_component, nft_resx) = setup(); - - let vars = vec![ - ("account", account_address.into()), - ("nft", nft_component.into()), - ("nft_resx", nft_resx.clone().into()), - ]; - - let total_supply: Amount = - template_test.call_method(nft_component, "total_supply", args![], vec![owner_proof.clone()]); - assert_eq!(total_supply, Amount(0)); - - let result = template_test - .execute_and_commit_manifest( - r#" - let account = var!["account"]; - let sparkle_nft = var!["nft"]; - - let nft_bucket = sparkle_nft.mint(); - account.deposit(nft_bucket); - "#, - vars.clone(), - vec![owner_proof.clone()], - ) - .unwrap(); - - let diff = result.finalize.result.expect("execution failed"); - - // Resource is changed - assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_resource()).count(), 1); - assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_resource()).count(), 1); - - // NFT component changed - assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_component()).count(), 1); - assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_component()).count(), 1); - - // One new vault created - assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_vault()).count(), 0); - assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_vault()).count(), 1); - - // One new NFT minted - assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 0); - assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 1); - - // One new NFT minted - assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 0); - assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 1); - let (nft_addr, _) = diff.up_iter().find(|(addr, _)| addr.is_non_fungible()).unwrap(); - - // One new NFT index - assert_eq!( - diff.down_iter() - .filter(|(addr, _)| addr.is_non_fungible_index()) - .count(), - 0 - ); - assert_eq!( - diff.up_iter().filter(|(addr, _)| addr.is_non_fungible_index()).count(), - 1 - ); - let (index_addr, index) = diff.up_iter().find(|(addr, _)| addr.is_non_fungible_index()).unwrap(); - // The nft index address is composed of the resource address - assert_eq!( - nft_resx.as_resource_address().unwrap(), - index_addr - .as_non_fungible_index_address() - .unwrap() - .resource_address() - .to_owned(), - ); - // The index references the newly minted nft - let referenced_address = index - .substate_value() - .non_fungible_index() - .unwrap() - .referenced_address(); - assert_eq!(nft_addr.to_address_string(), referenced_address.to_string()); - - // The total supply of the resource is increased - let total_supply: Amount = template_test.call_method(nft_component, "total_supply", args![], vec![owner_proof]); - assert_eq!(total_supply, Amount(1)); - } -} +// mod nft_indexes { +// use super::*; +// +// fn setup() -> ( +// TemplateTest, +// (ComponentAddress, NonFungibleAddress), +// ComponentAddress, +// SubstateId, +// ) { +// let mut template_test = TemplateTest::new(vec!["tests/templates/nft/nft_list"]); +// +// let (account_address, owner_token, _) = template_test.create_funded_account(); +// let nft_component: ComponentAddress = template_test.call_function("SparkleNft", "new", args![], vec![]); +// +// let nft_resx = template_test.get_previous_output_address(SubstateType::Resource); +// +// // TODO: cleanup +// (template_test, (account_address, owner_token), nft_component, nft_resx) +// } +// +// #[test] +// #[allow(clippy::too_many_lines)] +// fn new_nft_index() { +// let (mut template_test, (account_address, owner_proof), nft_component, nft_resx) = setup(); +// +// let vars = vec![ +// ("account", account_address.into()), +// ("nft", nft_component.into()), +// ("nft_resx", nft_resx.clone().into()), +// ]; +// +// let total_supply: Amount = +// template_test.call_method(nft_component, "total_supply", args![], vec![owner_proof.clone()]); +// assert_eq!(total_supply, Amount(0)); +// +// let result = template_test +// .execute_and_commit_manifest( +// r#" +// let account = var!["account"]; +// let sparkle_nft = var!["nft"]; +// +// let nft_bucket = sparkle_nft.mint(); +// account.deposit(nft_bucket); +// "#, +// vars.clone(), +// vec![owner_proof.clone()], +// ) +// .unwrap(); +// +// let diff = result.finalize.result.expect("execution failed"); +// +// // Resource is changed +// assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_resource()).count(), 1); +// assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_resource()).count(), 1); +// +// // NFT component changed +// assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_component()).count(), 1); +// assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_component()).count(), 1); +// +// // One new vault created +// assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_vault()).count(), 0); +// assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_vault()).count(), 1); +// +// // One new NFT minted +// assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 0); +// assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 1); +// +// // One new NFT minted +// assert_eq!(diff.down_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 0); +// assert_eq!(diff.up_iter().filter(|(addr, _)| addr.is_non_fungible()).count(), 1); +// let (nft_addr, _) = diff.up_iter().find(|(addr, _)| addr.is_non_fungible()).unwrap(); +// +// // One new NFT index +// assert_eq!( +// diff.down_iter() +// .filter(|(addr, _)| addr.is_non_fungible_index()) +// .count(), +// 0 +// ); +// assert_eq!( +// diff.up_iter().filter(|(addr, _)| addr.is_non_fungible_index()).count(), +// 1 +// ); +// let (index_addr, index) = diff.up_iter().find(|(addr, _)| addr.is_non_fungible_index()).unwrap(); +// // The nft index address is composed of the resource address +// assert_eq!( +// nft_resx.as_resource_address().unwrap(), +// index_addr +// .as_non_fungible_index_address() +// .unwrap() +// .resource_address() +// .to_owned(), +// ); +// // The index references the newly minted nft +// let referenced_address = index +// .substate_value() +// .non_fungible_index() +// .unwrap() +// .referenced_address(); +// assert_eq!(nft_addr.to_address_string(), referenced_address.to_string()); +// +// // The total supply of the resource is increased +// let total_supply: Amount = template_test.call_method(nft_component, "total_supply", args![], +// vec![owner_proof]); assert_eq!(total_supply, Amount(1)); +// } +// } #[test] fn test_builtin_templates() { diff --git a/dan_layer/engine_types/Cargo.toml b/dan_layer/engine_types/Cargo.toml index 4fb2397730..b5fdc09443 100644 --- a/dan_layer/engine_types/Cargo.toml +++ b/dan_layer/engine_types/Cargo.toml @@ -23,6 +23,7 @@ blake2 = { workspace = true } rand = { workspace = true } digest = { workspace = true } hex = { workspace = true, features = ["serde"] } +indexmap = { workspace = true } lazy_static = { workspace = true } serde = { workspace = true, default-features = true } serde_json = { workspace = true } diff --git a/dan_layer/engine_types/src/fees.rs b/dan_layer/engine_types/src/fees.rs index 7c50322d24..4f472916a3 100644 --- a/dan_layer/engine_types/src/fees.rs +++ b/dan_layer/engine_types/src/fees.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; +use indexmap::{map::Entry, IndexMap}; use serde::{Deserialize, Serialize}; use tari_template_lib::models::{Amount, VaultId}; #[cfg(feature = "ts")] @@ -18,7 +19,7 @@ pub struct FeeReceipt { /// Total fees paid after refunds pub total_fees_paid: Amount, /// Breakdown of fee costs - pub cost_breakdown: Vec, + pub cost_breakdown: FeeBreakdown, } impl FeeReceipt { @@ -31,13 +32,7 @@ impl FeeReceipt { /// The total amount of fees charged. This may be more than total_fees_paid if the user paid an insufficient amount. pub fn total_fees_charged(&self) -> Amount { - Amount::try_from( - self.cost_breakdown - .iter() - .map(|breakdown| breakdown.amount) - .sum::(), - ) - .unwrap() + Amount::try_from(self.cost_breakdown.get_total()).unwrap() } pub fn total_refunded(&self) -> Amount { @@ -69,7 +64,7 @@ impl FeeReceipt { } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord)] #[cfg_attr(feature = "ts", derive(TS), ts(export, export_to = "../../bindings/src/types/"))] pub enum FeeSource { Initial, @@ -79,19 +74,40 @@ pub enum FeeSource { Logs, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[cfg_attr(feature = "ts", derive(TS), ts(export, export_to = "../../bindings/src/types/"))] pub struct FeeBreakdown { - pub source: FeeSource, - #[cfg_attr(feature = "ts", ts(type = "number"))] - pub amount: u64, + breakdown: IndexMap, +} + +impl FeeBreakdown { + pub fn insert(&mut self, source: FeeSource, amount: u64) { + match self.breakdown.entry(source) { + Entry::Occupied(entry) => { + *entry.into_mut() += amount; + }, + Entry::Vacant(entry) => { + entry.insert(amount); + self.breakdown.sort_keys(); + }, + } + } + + /// Returns an iterator over the fee breakdown in a canonical order. + pub fn iter(&self) -> impl Iterator { + self.breakdown.iter() + } + + pub fn get_total(&self) -> u64 { + self.breakdown.values().sum() + } } #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "ts", derive(TS), ts(export, export_to = "../../bindings/src/types/"))] pub struct FeeCostBreakdown { pub total_fees_charged: Amount, - pub breakdown: Vec, + pub breakdown: FeeBreakdown, } #[derive(Debug)] diff --git a/dan_layer/engine_types/src/indexed_value.rs b/dan_layer/engine_types/src/indexed_value.rs index 7843def771..cc06982224 100644 --- a/dan_layer/engine_types/src/indexed_value.rs +++ b/dan_layer/engine_types/src/indexed_value.rs @@ -111,10 +111,7 @@ impl IndexedValue { pub fn get_value(&self, path: &str) -> Result, IndexedValueError> where for<'a> T: serde::Deserialize<'a> { - get_value_by_path(&self.value, path) - .map(tari_bor::from_value) - .transpose() - .map_err(Into::into) + decode_value_at_path(&self.value, path) } } @@ -459,6 +456,14 @@ impl From<&str> for IndexedValueError { } } +pub fn decode_value_at_path(value: &tari_bor::Value, path: &str) -> Result, IndexedValueError> +where for<'a> T: serde::Deserialize<'a> { + get_value_by_path(value, path) + .map(tari_bor::from_value) + .transpose() + .map_err(Into::into) +} + fn get_value_by_path<'a>(value: &'a tari_bor::Value, path: &str) -> Option<&'a tari_bor::Value> { let mut value = value; for part in path.split('.') { diff --git a/dan_layer/rpc_state_sync/src/manager.rs b/dan_layer/rpc_state_sync/src/manager.rs index bc48f8c871..e55b5f1636 100644 --- a/dan_layer/rpc_state_sync/src/manager.rs +++ b/dan_layer/rpc_state_sync/src/manager.rs @@ -1,8 +1,6 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use std::cmp; - use anyhow::anyhow; use async_trait::async_trait; use futures::StreamExt; @@ -138,13 +136,6 @@ where TConsensusSpec: ConsensusSpec let mut current_version = persisted_version; - // Minimum epoch we should request is 1 since Epoch(0) is the genesis epoch. - let last_state_transition_id = StateTransitionId::new( - cmp::max(last_state_transition_id.epoch(), Epoch(1)), - last_state_transition_id.shard(), - last_state_transition_id.seq(), - ); - info!( target: LOG_TARGET, "🛜Syncing from state transition {last_state_transition_id}" diff --git a/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql b/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql index c03eafb836..80b2f76566 100644 --- a/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql +++ b/dan_layer/state_store_sqlite/migrations/2023-06-08-091819_create_state_store/up.sql @@ -413,19 +413,16 @@ CREATE TABLE burnt_utxos CREATE TABLE state_tree ( - id integer not NULL primary key AUTOINCREMENT, - shard int not NULL, - key text not NULL, - node text not NULL, - is_stale boolean not null default '0' + id integer not NULL primary key AUTOINCREMENT, + shard int not NULL, + key text not NULL, + node text not NULL ); -- Scoping by shard -CREATE INDEX state_tree_idx_shard_key on state_tree (shard) WHERE is_stale = false; +CREATE INDEX state_tree_idx_shard_key on state_tree (shard); -- Duplicate keys are not allowed -CREATE UNIQUE INDEX state_tree_uniq_idx_key on state_tree (shard, key) WHERE is_stale = false; --- filtering out or by is_stale is used in every query -CREATE INDEX state_tree_idx_is_stale on state_tree (is_stale); +CREATE UNIQUE INDEX state_tree_uniq_idx_key on state_tree (shard, key); create table state_tree_shard_versions ( diff --git a/dan_layer/state_store_sqlite/src/reader.rs b/dan_layer/state_store_sqlite/src/reader.rs index 89b3f76218..fb515b37e8 100644 --- a/dan_layer/state_store_sqlite/src/reader.rs +++ b/dan_layer/state_store_sqlite/src/reader.rs @@ -777,6 +777,7 @@ impl<'tx, TAddr: NodeAddressable + Serialize + DeserializeOwned + 'tx> StateStor let execution = transaction_executions::table .filter(transaction_executions::transaction_id.eq(serialize_hex(tx_id))) .filter(transaction_executions::block_id.eq_any(block_ids)) + // Get last execution .order_by(transaction_executions::id.desc()) .first::(self.connection()) .map_err(|e| SqliteStorageError::DieselError { @@ -2088,15 +2089,14 @@ impl<'tx, TAddr: NodeAddressable + Serialize + DeserializeOwned + 'tx> StateStor ) -> Result, StorageError> { use crate::schema::{state_transitions, substates}; - // Never return epoch 0 state transitions - let min_epoch = Some(id.epoch().as_u64()).filter(|e| *e > 0).unwrap_or(1) as i64; + debug!(target: LOG_TARGET, "state_transitions_get_n_after: {id}, end_epoch:{end_epoch}"); + let transitions = state_transitions::table .left_join(substates::table.on(state_transitions::substate_address.eq(substates::address))) .select((state_transitions::all_columns, substates::all_columns.nullable())) - .filter(state_transitions::seq.ge(id.seq() as i64)) - .filter(state_transitions::epoch.ge(min_epoch)) - .filter(state_transitions::epoch.lt(end_epoch.as_u64() as i64)) + .filter(state_transitions::seq.gt(id.seq() as i64)) .filter(state_transitions::shard.eq(id.shard().as_u32() as i32)) + .filter(state_transitions::epoch.lt(end_epoch.as_u64() as i64)) .limit(n as i64) .get_results::<(sql_models::StateTransition, Option)>(self.connection()) .map_err(|e| SqliteStorageError::DieselError { @@ -2143,7 +2143,6 @@ impl<'tx, TAddr: NodeAddressable + Serialize + DeserializeOwned + 'tx> StateStor .select(state_tree::node) .filter(state_tree::shard.eq(shard.as_u32() as i32)) .filter(state_tree::key.eq(key.to_string())) - .filter(state_tree::is_stale.eq(false)) .first::(self.connection()) .map_err(|e| SqliteStorageError::DieselError { operation: "state_tree_nodes_get", @@ -2231,7 +2230,7 @@ impl<'tx, TAddr: NodeAddressable + Serialize + DeserializeOwned + 'tx> StateStor let version = pledge.version as u32; let id = VersionedSubstateId::new(substate_id, version); let lock_type = parse_from_string(&pledge.lock_type)?; - let lock_intent = VersionedSubstateIdLockIntent::new(id, lock_type); + let lock_intent = VersionedSubstateIdLockIntent::new(id, lock_type, true); let substate_value = pledge.substate_value.as_deref().map(deserialize_json).transpose()?; let pledge = SubstatePledge::try_create(lock_intent.clone(), substate_value).ok_or_else(|| { StorageError::DataInconsistency { diff --git a/dan_layer/state_store_sqlite/src/schema.rs b/dan_layer/state_store_sqlite/src/schema.rs index 41c94f6b00..b2e9b72939 100644 --- a/dan_layer/state_store_sqlite/src/schema.rs +++ b/dan_layer/state_store_sqlite/src/schema.rs @@ -302,7 +302,6 @@ diesel::table! { shard -> Integer, key -> Text, node -> Text, - is_stale -> Bool, } } diff --git a/dan_layer/state_store_sqlite/src/writer.rs b/dan_layer/state_store_sqlite/src/writer.rs index 77195f19ec..6e1cd7a228 100644 --- a/dan_layer/state_store_sqlite/src/writer.rs +++ b/dan_layer/state_store_sqlite/src/writer.rs @@ -1457,7 +1457,7 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta operation: "substates_create", source: e, })?; - let next_seq = seq.map(|s| s + 1).unwrap_or(0); + let next_seq = seq.map(|s| s + 1).unwrap_or(1); // This means that we MUST do the state tree updates before inserting substates let version = self.state_tree_versions_get_latest(substate.created_by_shard)?; @@ -1511,7 +1511,7 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta .set(changes) .execute(self.connection()) .map_err(|e| SqliteStorageError::DieselError { - operation: "substates_down", + operation: "substates_down (update substates)", source: e, })?; @@ -1520,10 +1520,10 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta .filter(state_transitions::shard.eq(shard.as_u32() as i32)) .first::>(self.connection()) .map_err(|e| SqliteStorageError::DieselError { - operation: "substates_create", + operation: "substates_down (get max seq)", source: e, })?; - let next_seq = seq.map(|s| s + 1).unwrap_or(0); + let next_seq = seq.map(|s| s + 1).unwrap_or(1); let version = self.state_tree_versions_get_latest(shard)?; let values = ( @@ -1541,7 +1541,7 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta .values(values) .execute(self.connection()) .map_err(|e| SqliteStorageError::DieselError { - operation: "substates_down", + operation: "substates_down(insert into state_transitions)", source: e, })?; @@ -1701,7 +1701,7 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta let values = ( state_tree::shard.eq(shard.as_u32() as i32), state_tree::key.eq(key.to_string()), - state_tree::node.eq(node), + state_tree::node.eq(&node), ); diesel::insert_into(state_tree::table) .values(&values) @@ -1721,11 +1721,20 @@ impl<'tx, TAddr: NodeAddressable + 'tx> StateStoreWriteTransaction for SqliteSta ) -> Result<(), StorageError> { use crate::schema::state_tree; + // let num_effected = diesel::update(state_tree::table) + // .filter(state_tree::shard.eq(shard.as_u32() as i32)) + // .filter(state_tree::key.eq(key.to_string())) + // .set(state_tree::is_stale.eq(true)) + // .execute(self.connection()) + // .map_err(|e| SqliteStorageError::DieselError { + // operation: "state_tree_nodes_mark_stale_tree_node", + // source: e, + // })?; + let key = node.as_node_key(); - let num_effected = diesel::update(state_tree::table) + let num_effected = diesel::delete(state_tree::table) .filter(state_tree::shard.eq(shard.as_u32() as i32)) .filter(state_tree::key.eq(key.to_string())) - .set(state_tree::is_stale.eq(true)) .execute(self.connection()) .map_err(|e| SqliteStorageError::DieselError { operation: "state_tree_nodes_mark_stale_tree_node", diff --git a/dan_layer/storage/src/consensus_models/executed_transaction.rs b/dan_layer/storage/src/consensus_models/executed_transaction.rs index 31ab688cfb..4e36287435 100644 --- a/dan_layer/storage/src/consensus_models/executed_transaction.rs +++ b/dan_layer/storage/src/consensus_models/executed_transaction.rs @@ -9,13 +9,7 @@ use std::{ }; use serde::{Deserialize, Serialize}; -use tari_dan_common_types::{ - optional::Optional, - SubstateAddress, - SubstateLockType, - ToSubstateAddress, - VersionedSubstateId, -}; +use tari_dan_common_types::{optional::Optional, SubstateAddress, ToSubstateAddress, VersionedSubstateId}; use tari_engine_types::commit_result::{ExecuteResult, RejectReason}; use tari_transaction::{Transaction, TransactionId}; @@ -65,10 +59,10 @@ impl ExecutedTransaction { .map(|diff| { diff.up_iter() .map(|(addr, substate)| { - VersionedSubstateIdLockIntent::new( - VersionedSubstateId::new(addr.clone(), substate.version()), - SubstateLockType::Output, - ) + VersionedSubstateIdLockIntent::output(VersionedSubstateId::new( + addr.clone(), + substate.version(), + )) }) .collect::>() }) diff --git a/dan_layer/storage/src/consensus_models/lock_intent.rs b/dan_layer/storage/src/consensus_models/lock_intent.rs index 789b0852b2..49680460d2 100644 --- a/dan_layer/storage/src/consensus_models/lock_intent.rs +++ b/dan_layer/storage/src/consensus_models/lock_intent.rs @@ -16,26 +16,38 @@ use tari_engine_types::substate::SubstateId; pub struct VersionedSubstateIdLockIntent { versioned_substate_id: VersionedSubstateId, lock_type: SubstateLockType, + require_version: bool, } impl VersionedSubstateIdLockIntent { - pub fn new(versioned_substate_id: VersionedSubstateId, lock: SubstateLockType) -> Self { + pub fn new(versioned_substate_id: VersionedSubstateId, lock: SubstateLockType, require_version: bool) -> Self { Self { versioned_substate_id, lock_type: lock, + require_version, } } - pub fn read(versioned_substate_id: VersionedSubstateId) -> Self { - Self::new(versioned_substate_id, SubstateLockType::Read) + pub fn from_requirement(substate_requirement: SubstateRequirement, lock: SubstateLockType) -> Self { + let version = substate_requirement.version(); + Self::new( + VersionedSubstateId::new(substate_requirement.into_substate_id(), version.unwrap_or(0)), + lock, + version.is_some(), + ) + } + + pub fn read(versioned_substate_id: VersionedSubstateId, require_version: bool) -> Self { + Self::new(versioned_substate_id, SubstateLockType::Read, require_version) } - pub fn write(versioned_substate_id: VersionedSubstateId) -> Self { - Self::new(versioned_substate_id, SubstateLockType::Write) + pub fn write(versioned_substate_id: VersionedSubstateId, require_version: bool) -> Self { + Self::new(versioned_substate_id, SubstateLockType::Write, require_version) } pub fn output(versioned_substate_id: VersionedSubstateId) -> Self { - Self::new(versioned_substate_id, SubstateLockType::Output) + // Once we lock outputs we always require the provided version + Self::new(versioned_substate_id, SubstateLockType::Output, true) } pub fn versioned_substate_id(&self) -> &VersionedSubstateId { @@ -57,6 +69,15 @@ impl VersionedSubstateIdLockIntent { pub fn lock_type(&self) -> SubstateLockType { self.lock_type } + + pub fn to_substate_requirement(&self) -> SubstateRequirement { + let version = if self.require_version { + Some(self.version()) + } else { + None + }; + SubstateRequirement::new(self.substate_id().clone(), version) + } } impl Borrow for VersionedSubstateIdLockIntent { @@ -88,11 +109,15 @@ impl<'a> LockIntent for &'a VersionedSubstateIdLockIntent { } fn version_to_lock(&self) -> u32 { - self.versioned_substate_id.version() + self.version() } fn requested_version(&self) -> Option { - Some(self.versioned_substate_id.version()) + if self.require_version { + Some(self.version()) + } else { + None + } } } @@ -152,6 +177,7 @@ impl SubstateRequirementLockIntent { VersionedSubstateIdLockIntent::new( VersionedSubstateId::new(self.substate_id().clone(), self.version_to_lock), self.lock_type, + self.substate_requirement.version().is_some(), ) } } @@ -195,7 +221,7 @@ impl LockIntent for SubstateRequirementLockIntent { 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) + Self::new(intent.to_substate_requirement(), version, intent.lock_type) } } diff --git a/dan_layer/storage/src/consensus_models/substate_lock.rs b/dan_layer/storage/src/consensus_models/substate_lock.rs index 083c97a3b2..c2531bb98a 100644 --- a/dan_layer/storage/src/consensus_models/substate_lock.rs +++ b/dan_layer/storage/src/consensus_models/substate_lock.rs @@ -80,6 +80,7 @@ impl LockedSubstateValue { VersionedSubstateIdLockIntent::new( VersionedSubstateId::new(self.substate_id.clone(), self.lock.version()), self.lock.substate_lock(), + true, ) } diff --git a/dan_layer/storage/src/consensus_models/transaction.rs b/dan_layer/storage/src/consensus_models/transaction.rs index 2a58f48468..c208debb13 100644 --- a/dan_layer/storage/src/consensus_models/transaction.rs +++ b/dan_layer/storage/src/consensus_models/transaction.rs @@ -171,7 +171,7 @@ impl TransactionRecord { let resolved_inputs = self.resolved_inputs.take().unwrap_or_else(|| { self.transaction .all_inputs_iter() - .map(|i| VersionedSubstateIdLockIntent::new(i.or_zero_version(), SubstateLockType::Write)) + .map(|i| VersionedSubstateIdLockIntent::from_requirement(i, SubstateLockType::Write)) .collect() }); let resulting_outputs = self.resulting_outputs.take().unwrap_or_default(); diff --git a/dan_layer/template_test_tooling/src/template_test.rs b/dan_layer/template_test_tooling/src/template_test.rs index 8e8d62c3ee..c1f5098bad 100644 --- a/dan_layer/template_test_tooling/src/template_test.rs +++ b/dan_layer/template_test_tooling/src/template_test.rs @@ -35,7 +35,6 @@ use tari_dan_engine::{ use tari_engine_types::{ commit_result::{ExecuteResult, RejectReason}, component::{ComponentBody, ComponentHeader}, - fees::FeeBreakdown, id_provider::{IdProvider, ObjectIds}, instruction::Instruction, resource_container::ResourceContainer, @@ -517,7 +516,7 @@ impl TemplateTest { eprintln!("Paid: {}", fee.total_fees_paid()); eprintln!("Refund: {}", fee.total_refunded()); eprintln!("Unpaid: {}", fee.unpaid_debt()); - for FeeBreakdown { source, amount } in &fee.cost_breakdown { + for (source, amount) in fee.cost_breakdown.iter() { eprintln!("- {:?} {}", source, amount); } } diff --git a/dan_layer/transaction/src/transaction.rs b/dan_layer/transaction/src/transaction.rs index a674530dfb..38ce5912b2 100644 --- a/dan_layer/transaction/src/transaction.rs +++ b/dan_layer/transaction/src/transaction.rs @@ -1,13 +1,13 @@ // Copyright 2023 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use std::collections::HashSet; +use std::{collections::HashSet, fmt::Display}; use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use tari_common_types::types::PublicKey; use tari_crypto::ristretto::RistrettoSecretKey; -use tari_dan_common_types::{Epoch, SubstateRequirement, VersionedSubstateId}; +use tari_dan_common_types::{committee::CommitteeInfo, Epoch, SubstateRequirement, VersionedSubstateId}; use tari_engine_types::{ hashing::{hasher32, EngineHashDomainLabel}, indexed_value::{IndexedValue, IndexedValueError}, @@ -144,6 +144,12 @@ impl Transaction { .chain(self.filled_inputs().iter().map(|fi| fi.substate_id())) } + /// Returns true if the provided committee is involved in at least one input of this transaction. + pub fn is_involved_inputs(&self, committee_info: &CommitteeInfo) -> bool { + self.all_inputs_iter() + .any(|id| committee_info.includes_substate_id(id.substate_id())) + } + pub fn num_unique_inputs(&self) -> usize { self.all_inputs_substate_ids_iter().count() } @@ -231,3 +237,18 @@ impl Transaction { self.inputs().iter().any(|i| i.version().is_none()) } } + +impl Display for Transaction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Transaction[{}, Inputs: {}, Fee Instructions: {}, Instructions: {}, Signatures: {}, Filled Inputs: {}]", + self.id, + self.transaction.inputs.len(), + self.transaction.fee_instructions.len(), + self.transaction.instructions.len(), + self.signatures.len(), + self.filled_inputs.len(), + ) + } +} diff --git a/dan_layer/wallet/sdk/src/apis/transaction.rs b/dan_layer/wallet/sdk/src/apis/transaction.rs index a93319c8be..74ff6c2a06 100644 --- a/dan_layer/wallet/sdk/src/apis/transaction.rs +++ b/dan_layer/wallet/sdk/src/apis/transaction.rs @@ -286,14 +286,14 @@ where diff: &SubstateDiff, ) -> Result<(), TransactionApiError> { let mut downed_substates_with_parents = HashMap::with_capacity(diff.down_len()); - for (addr, _) in diff.down_iter() { - if addr.is_layer1_commitment() { - info!(target: LOG_TARGET, "Layer 1 commitment {} downed", addr); + for (id, _) in diff.down_iter() { + if id.is_layer1_commitment() { + info!(target: LOG_TARGET, "Layer 1 commitment {} downed", id); continue; } - let Some(downed) = tx.substates_remove(addr).optional()? else { - warn!(target: LOG_TARGET, "Downed substate {} not found", addr); + let Some(downed) = tx.substates_remove(id).optional()? else { + warn!(target: LOG_TARGET, "Downed substate {} not found", id); continue; }; diff --git a/integration_tests/tests/steps/validator_node.rs b/integration_tests/tests/steps/validator_node.rs index 1ab7370dd8..dbb13df2be 100644 --- a/integration_tests/tests/steps/validator_node.rs +++ b/integration_tests/tests/steps/validator_node.rs @@ -468,7 +468,12 @@ async fn when_i_wait_for_validator_leaf_block_at_least(world: &mut TariWorld, na }) .await .unwrap(); - let actual_height = resp.blocks.first().unwrap().height().as_u64(); + let actual_height = resp + .blocks + .first() + .unwrap_or_else(|| panic!("Validator {name} has no blocks")) + .height() + .as_u64(); if actual_height < height { panic!( "Validator {} leaf block height {} is less than {}", diff --git a/utilities/tariswap_test_bench/src/accounts.rs b/utilities/tariswap_test_bench/src/accounts.rs index d75415ede0..4da66806b0 100644 --- a/utilities/tariswap_test_bench/src/accounts.rs +++ b/utilities/tariswap_test_bench/src/accounts.rs @@ -6,7 +6,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_engine_types::{component::new_component_address_from_public_key, indexed_value::IndexedWellKnownTypes}; use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::{ args, @@ -43,7 +43,10 @@ impl Runner { let finalize = self.submit_transaction_and_wait(transaction).await?; let diff = finalize.result.accept().unwrap(); let (account, _) = diff.up_iter().find(|(addr, _)| addr.is_component()).unwrap(); - let (vault, _) = diff.up_iter().find(|(addr, _)| addr.is_vault()).unwrap(); + let (vault, _) = diff + .up_iter() + .find(|(addr, _)| *addr != XTR_FAUCET_VAULT_ADDRESS && addr.is_vault()) + .unwrap(); self.sdk.accounts_api().add_account(None, account, 0, true)?; self.sdk.accounts_api().add_vault( @@ -80,8 +83,18 @@ impl Runner { for owner in &owners { builder = builder.create_account(RistrettoPublicKey::from_secret_key(&owner.key)); } + + let pay_fee_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&pay_fee_account.address, &XTR)?; + let transaction = builder - .with_inputs([SubstateRequirement::unversioned(pay_fee_account.address.clone())]) + .with_inputs([ + SubstateRequirement::unversioned(pay_fee_account.address.clone()), + SubstateRequirement::unversioned(pay_fee_vault.address), + SubstateRequirement::unversioned(pay_fee_vault.resource_address), + ]) .sign(&key.key) .build(); @@ -90,7 +103,7 @@ impl Runner { let mut accounts = Vec::with_capacity(num_accounts); for owner in owners { - let account = diff + let account_addr = diff .up_iter() .map(|(addr, _)| addr) .filter(|addr| addr.is_component()) @@ -105,8 +118,8 @@ impl Runner { self.sdk .accounts_api() - .add_account(None, account, owner.key_index, false)?; - let account = self.sdk.accounts_api().get_account_by_address(account)?; + .add_account(None, account_addr, owner.key_index, false)?; + let account = self.sdk.accounts_api().get_account_by_address(account_addr)?; accounts.push(account); } @@ -120,6 +133,10 @@ impl Runner { accounts: &[Account], ) -> anyhow::Result<()> { let key = self.sdk.key_manager_api().derive_key(TRANSACTION_BRANCH, 0)?; + let fee_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&fee_account.address, &XTR)?; let mut builder = Transaction::builder().fee_transaction_pay_from_component( fee_account.address.as_component_address().unwrap(), Amount(1000 * accounts.len() as i64), @@ -135,18 +152,51 @@ impl Runner { .put_last_instruction_output_on_workspace("xtr") .call_method(account.address.as_component_address().unwrap(), "deposit", args![ Workspace("xtr") - ]); + ]) + .add_input(SubstateRequirement::unversioned(account.address.clone())); } let transaction = builder .with_inputs([ + SubstateRequirement::unversioned(XTR), + SubstateRequirement::unversioned(XTR_FAUCET_COMPONENT_ADDRESS), + SubstateRequirement::unversioned(XTR_FAUCET_VAULT_ADDRESS), SubstateRequirement::unversioned(faucet.component_address), SubstateRequirement::unversioned(faucet.resource_address), + SubstateRequirement::unversioned(faucet.vault_address), + SubstateRequirement::unversioned(fee_vault.account_address), + SubstateRequirement::unversioned(fee_vault.address), ]) .sign(&key.key) .build(); - self.submit_transaction_and_wait(transaction).await?; + let result = self.submit_transaction_and_wait(transaction).await?; + + let accounts_and_state = result + .result + .accept() + .unwrap() + .up_iter() + .filter(|(addr, _)| { + *addr != XTR_FAUCET_COMPONENT_ADDRESS && + *addr != faucet.component_address && + *addr != fee_account.address + }) + .filter_map(|(addr, substate)| Some((addr, substate.substate_value().component()?))) + .map(|(addr, component)| (addr, IndexedWellKnownTypes::from_value(&component.body.state).unwrap())); + + for (account, indexed) in accounts_and_state { + for vault_id in indexed.vault_ids() { + log::info!("Adding vault {} to account {}", vault_id, account); + self.sdk.accounts_api().add_vault( + account.clone(), + (*vault_id).into(), + faucet.resource_address, + ResourceType::Fungible, + Some("FAUCET".to_string()), + )?; + } + } Ok(()) } diff --git a/utilities/tariswap_test_bench/src/faucet.rs b/utilities/tariswap_test_bench/src/faucet.rs index 78fdcf58a7..a3c53690ea 100644 --- a/utilities/tariswap_test_bench/src/faucet.rs +++ b/utilities/tariswap_test_bench/src/faucet.rs @@ -1,10 +1,13 @@ // Copyright 2024 The Tari Project // 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, ResourceAddress}, + constants::XTR, + models::{Amount, ComponentAddress, ResourceAddress, VaultId}, }; use tari_transaction::Transaction; @@ -13,15 +16,26 @@ use crate::runner::Runner; pub struct Faucet { pub component_address: ComponentAddress, pub resource_address: ResourceAddress, + pub vault_address: VaultId, } impl Runner { pub async fn create_faucet(&mut self, in_account: &Account) -> anyhow::Result { let key = self.sdk.key_manager_api().derive_key(TRANSACTION_BRANCH, 0)?; + let fee_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&in_account.address, &XTR)?; + let transaction = Transaction::builder() .fee_transaction_pay_from_component(in_account.address.as_component_address().unwrap(), Amount(1000)) - .call_function(self._faucet_template.address, "mint", args![Amount(1_000_000_000)]) + .call_function(self.faucet_template.address, "mint", args![Amount(1_000_000_000)]) + .with_inputs([ + SubstateRequirement::unversioned(in_account.address.clone()), + SubstateRequirement::unversioned(fee_vault.address.clone()), + SubstateRequirement::unversioned(fee_vault.resource_address), + ]) .sign(&key.key) .build(); @@ -31,18 +45,28 @@ impl Runner { let component_address = diff .up_iter() .find_map(|(addr, s)| { - addr.as_component_address() - .filter(|_| s.substate_value().component().unwrap().module_name == "TestFaucet") + addr.as_component_address().filter(|_| { + s.substate_value().component().unwrap().template_address == self.faucet_template.address + }) }) .ok_or_else(|| anyhow::anyhow!("Faucet Component address not found"))?; let resource_address = diff .up_iter() + .filter(|(addr, _)| *addr != XTR) .find_map(|(addr, _)| addr.as_resource_address()) .ok_or_else(|| anyhow::anyhow!("Faucet Resource address not found"))?; + let vault_address = diff + .up_iter() + .filter_map(|(addr, _)| addr.as_vault_id()) + .find(|addr| *addr != fee_vault.address) + .ok_or_else(|| anyhow::anyhow!("Faucet Resource address not found"))?; + + info!("✅ Faucet {component_address} created with {resource_address} and {vault_address}"); Ok(Faucet { component_address, resource_address, + vault_address, }) } } diff --git a/utilities/tariswap_test_bench/src/main.rs b/utilities/tariswap_test_bench/src/main.rs index 9bbe9a81fb..146addb2fa 100644 --- a/utilities/tariswap_test_bench/src/main.rs +++ b/utilities/tariswap_test_bench/src/main.rs @@ -58,9 +58,9 @@ async fn run(cli: cli::CommonArgs, _args: cli::RunArgs) -> anyhow::Result<()> { info!("✅ Created faucet {}", faucet.component_address); info!("⏳️ Funding accounts ..."); - for batch in accounts.chunks(500) { + for batch in accounts.chunks(250) { runner.fund_accounts(&faucet, &primary_account, batch).await?; - info!("✅ Funded 500 accounts"); + info!("✅ Funded 250 accounts"); } info!("⏳️ Creating 1000 tariswap components..."); diff --git a/utilities/tariswap_test_bench/src/runner.rs b/utilities/tariswap_test_bench/src/runner.rs index 3a4ceffe06..c05fe760d6 100644 --- a/utilities/tariswap_test_bench/src/runner.rs +++ b/utilities/tariswap_test_bench/src/runner.rs @@ -4,7 +4,6 @@ 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; @@ -20,7 +19,7 @@ type WalletSdk = DanWalletSdk pub struct Runner { pub(crate) sdk: WalletSdk, pub(crate) _cli: CommonArgs, - pub(crate) _faucet_template: TemplateMetadata, + pub(crate) faucet_template: TemplateMetadata, pub(crate) tariswap_template: TemplateMetadata, pub(crate) stats: Stats, } @@ -32,7 +31,7 @@ impl Runner { Ok(Self { sdk, _cli: cli, - _faucet_template: faucet_template, + faucet_template, tariswap_template, stats: Stats::default(), }) @@ -46,16 +45,16 @@ impl Runner { pub async fn submit_transaction(&mut self, transaction: Transaction) -> anyhow::Result { // TODO: remove the filled inputs here and allow consensus to figure out input versions - let inputs = transaction - .to_referenced_substates()? - .into_iter() - .map(|s| SubstateRequirement::new(s, None)) - .collect(); + // let inputs = transaction + // .to_referenced_substates()? + // .into_iter() + // .map(|s| SubstateRequirement::new(s, None)) + // .collect(); let tx_id = self .sdk .transaction_api() - .insert_new_transaction(transaction, inputs, None, false) + .insert_new_transaction(transaction, vec![], None, false) .await?; self.sdk.transaction_api().submit_transaction(tx_id).await?; diff --git a/utilities/tariswap_test_bench/src/tariswap.rs b/utilities/tariswap_test_bench/src/tariswap.rs index 5a657ba5ca..27529c181f 100644 --- a/utilities/tariswap_test_bench/src/tariswap.rs +++ b/utilities/tariswap_test_bench/src/tariswap.rs @@ -1,13 +1,16 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause +use std::collections::HashMap; + use log::info; -use tari_dan_common_types::SubstateRequirement; +use tari_dan_common_types::{optional::Optional, SubstateRequirement}; use tari_dan_wallet_sdk::{apis::key_manager::TRANSACTION_BRANCH, models::Account}; +use tari_engine_types::indexed_value::decode_value_at_path; use tari_template_lib::{ args, - models::{Amount, ComponentAddress}, - prelude::XTR, + models::{Amount, ComponentAddress, VaultId}, + prelude::{ResourceAddress, ResourceType, XTR}, }; use tari_transaction::Transaction; @@ -15,6 +18,8 @@ use crate::{faucet::Faucet, runner::Runner}; pub struct TariSwap { pub component_address: ComponentAddress, + pub vaults: HashMap, + pub lp_resource_address: ResourceAddress, } impl Runner { @@ -36,17 +41,43 @@ impl Runner { Amount(1) ]); } - let transaction = builder.sign(&key.key).build(); + + let fee_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&in_account.address, &XTR)?; + + let transaction = builder + .with_inputs([ + SubstateRequirement::unversioned(in_account.address.clone()), + SubstateRequirement::unversioned(fee_vault.address.clone()), + SubstateRequirement::unversioned(XTR), + SubstateRequirement::unversioned(faucet.resource_address), + ]) + .sign(&key.key) + .build(); let finalize = self.submit_transaction_and_wait(transaction).await?; let diff = finalize.result.accept().unwrap(); Ok(diff .up_iter() .filter_map(|(addr, value)| { - addr.as_component_address() - .filter(|_| value.substate_value().component().unwrap().module_name == "TariSwapPool") + let addr = addr + .as_component_address() + .filter(|_| value.substate_value().component().unwrap().module_name == "TariSwapPool")?; + let vaults = decode_value_at_path(value.substate_value().component().unwrap().state(), "$.pools") + .unwrap() + .unwrap(); + let lp_resource_address = + decode_value_at_path(value.substate_value().component().unwrap().state(), "$.lp_resource") + .unwrap() + .unwrap(); + Some(TariSwap { + component_address: addr, + vaults, + lp_resource_address, + }) }) - .map(|component_address| TariSwap { component_address }) .collect()) } @@ -63,8 +94,8 @@ impl Runner { .sdk .key_manager_api() .derive_key(TRANSACTION_BRANCH, primary_account.key_index)?; - let mut tx_ids = vec![]; - // Batch these otherwise we can break consensus (proposed with locked object) + let mut tx_ids = Vec::with_capacity(200); + for i in 0..5 { for (i, tariswap) in tariswaps.iter().enumerate().skip(i * 200).take(200) { let account = &accounts[i % accounts.len()]; @@ -72,11 +103,28 @@ impl Runner { .sdk .key_manager_api() .derive_key(TRANSACTION_BRANCH, account.key_index)?; + let xtr_vault = self.sdk.accounts_api().get_vault_by_resource(&account.address, &XTR)?; + let faucet_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&account.address, &faucet.resource_address)?; + let maybe_lp_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&account.address, &tariswap.lp_resource_address) + .optional()?; + let transaction = Transaction::builder() - .with_inputs(vec![ - SubstateRequirement::new(faucet.resource_address.into(), Some(0)), - SubstateRequirement::new(XTR.into(), Some(0)), + .with_inputs(maybe_lp_vault.map(|v| SubstateRequirement::unversioned(v.address))) + .with_inputs([ + SubstateRequirement::unversioned(account.address.clone()), + SubstateRequirement::unversioned(xtr_vault.address), + SubstateRequirement::unversioned(faucet_vault.address), + SubstateRequirement::unversioned(tariswap.component_address), + SubstateRequirement::unversioned(faucet.resource_address), + SubstateRequirement::unversioned(XTR), ]) + .with_inputs(tariswap.vaults.values().map(|v| SubstateRequirement::unversioned(*v))) .fee_transaction_pay_from_component(account.address.as_component_address().unwrap(), Amount(1000)) .call_method(account.address.as_component_address().unwrap(), "withdraw", args![ XTR, amount_a @@ -99,11 +147,30 @@ impl Runner { .sign(&key.key) .build(); - tx_ids.push(self.submit_transaction(transaction).await?); + tx_ids.push((account.address.clone(), self.submit_transaction(transaction).await?)); } - for tx_id in tx_ids.drain(..) { - self.wait_for_transaction(tx_id).await?; + for (account, tx_id) in tx_ids.drain(..) { + let result = self.wait_for_transaction(tx_id).await?; + let diff = result.result.accept().unwrap(); + let lp_vault = diff + .up_iter() + .find_map(|(addr, s)| { + let addr = addr.as_vault_id()?; + if *s.substate_value().vault().unwrap().resource_address() == tariswaps[0].lp_resource_address { + Some(addr) + } else { + None + } + }) + .ok_or_else(|| anyhow::anyhow!("LP Vault not found"))?; + self.sdk.accounts_api().add_vault( + account, + lp_vault.into(), + tariswaps[0].lp_resource_address, + ResourceType::NonFungible, + Some("LP".to_string()), + )?; } info!("⏳️ Added liquidity to pools {}-{}", i * 200, (i + 1) * 200); } @@ -128,37 +195,58 @@ impl Runner { let mut tx_ids = vec![]; // Swap XTR for faucet - // Batch these otherwise we can break consensus (proposed with locked object) for i in 0..5 { for (i, account) in accounts.iter().enumerate().skip(i * 200).take(200) { + let tariswap = &tariswaps[i % tariswaps.len()]; let key = self .sdk .key_manager_api() .derive_key(TRANSACTION_BRANCH, account.key_index)?; - let tariswap = &tariswaps[i % tariswaps.len()]; + let xtr_vault = self.sdk.accounts_api().get_vault_by_resource(&account.address, &XTR)?; + let faucet_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&account.address, &faucet.resource_address)?; + let maybe_lp_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&account.address, &tariswap.lp_resource_address) + .optional()?; let transaction = Transaction::builder() - // Use resources as input refs to allow concurrent access. - .with_inputs(vec![ - SubstateRequirement::new(faucet.resource_address.into(), Some(0)), - SubstateRequirement::new(XTR.into(), Some(0)), + .with_inputs(maybe_lp_vault.map(|v| SubstateRequirement::unversioned(v.address))) + .with_inputs([ + SubstateRequirement::unversioned(account.address.clone()), + SubstateRequirement::unversioned(xtr_vault.address), + SubstateRequirement::unversioned(faucet_vault.address), + SubstateRequirement::unversioned(tariswap.component_address), + SubstateRequirement::unversioned(faucet.resource_address), + SubstateRequirement::unversioned(XTR), + SubstateRequirement::unversioned(tariswap.lp_resource_address), ]) + .with_inputs(tariswap.vaults.values().map(|v| SubstateRequirement::unversioned(*v))) .fee_transaction_pay_from_component(account.address.as_component_address().unwrap(), Amount(1000)) - .call_method(tariswap.component_address, "get_pool_balance", args![ XTR, ]) - .call_method(tariswap.component_address, "get_pool_balance", args![ faucet.resource_address, ]) + .call_method(tariswap.component_address, "get_pool_balance", args![XTR,]) + .call_method(tariswap.component_address, "get_pool_balance", args![ + faucet.resource_address, + ]) .call_method(tariswap.component_address, "get_pool_ratio", args![XTR, Amount(1000)]) - .call_method(tariswap.component_address, "get_pool_ratio", args![faucet.resource_address, Amount(1000)]) + .call_method(tariswap.component_address, "get_pool_ratio", args![ + faucet.resource_address, + Amount(1000) + ]) .call_method(account.address.as_component_address().unwrap(), "withdraw", args![ - XTR, amount_a_for_b - ]) + XTR, + amount_a_for_b + ]) .put_last_instruction_output_on_workspace("a") .call_method(tariswap.component_address, "swap", args![ - Workspace("a"), - faucet.resource_address, - ]) + Workspace("a"), + faucet.resource_address, + ]) .put_last_instruction_output_on_workspace("swapped") .call_method(account.address.as_component_address().unwrap(), "deposit", args![ - Workspace("swapped") - ]) + Workspace("swapped") + ]) .sign(&primary_account_key.key) .sign(&key.key) .build(); @@ -188,26 +276,39 @@ impl Runner { .sdk .key_manager_api() .derive_key(TRANSACTION_BRANCH, account.key_index)?; + let xtr_vault = self.sdk.accounts_api().get_vault_by_resource(&account.address, &XTR)?; + let faucet_vault = self + .sdk + .accounts_api() + .get_vault_by_resource(&account.address, &faucet.resource_address)?; let tariswap = &tariswaps[i % tariswaps.len()]; let transaction = Transaction::builder() - // Use resources as input refs to allow concurrent access. - .with_inputs(vec![ - SubstateRequirement::new(faucet.resource_address.into(), Some(0)), - SubstateRequirement::new(XTR.into(), Some(0)), + .with_inputs([ + SubstateRequirement::unversioned(account.address.clone()), + SubstateRequirement::unversioned(xtr_vault.address), + SubstateRequirement::unversioned(faucet_vault.address), + SubstateRequirement::unversioned(tariswap.component_address), + SubstateRequirement::unversioned(faucet.resource_address), + SubstateRequirement::unversioned(XTR), + SubstateRequirement::unversioned(tariswap.lp_resource_address), ]) + .with_inputs(tariswap.vaults.values().map(|v| SubstateRequirement::unversioned(*v))) .fee_transaction_pay_from_component(account.address.as_component_address().unwrap(), Amount(1000)) .call_method(tariswap.component_address, "get_pool_balance", args![XTR]) - .call_method(tariswap.component_address, "get_pool_balance", args![faucet.resource_address]) + .call_method(tariswap.component_address, "get_pool_balance", args![ + faucet.resource_address + ]) .call_method(tariswap.component_address, "get_pool_ratio", args![XTR, Amount(1000)]) - .call_method(tariswap.component_address, "get_pool_ratio", args![faucet.resource_address, Amount(1000)]) + .call_method(tariswap.component_address, "get_pool_ratio", args![ + faucet.resource_address, + Amount(1000) + ]) .call_method(account.address.as_component_address().unwrap(), "withdraw", args![ - faucet.resource_address, amount_b_for_a + faucet.resource_address, + amount_b_for_a ]) .put_last_instruction_output_on_workspace("b") - .call_method(tariswap.component_address, "swap", args![ - Workspace("b"), - XTR, - ]) + .call_method(tariswap.component_address, "swap", args![Workspace("b"), XTR,]) .put_last_instruction_output_on_workspace("swapped") .call_method(account.address.as_component_address().unwrap(), "deposit", args![ Workspace("swapped")