From 756d2af6a4558f86ce5fa692d0ff7c7762907374 Mon Sep 17 00:00:00 2001 From: Cifko Date: Wed, 22 Nov 2023 17:54:18 +0100 Subject: [PATCH 1/4] feat: validator registration timelock --- .../minotari_app_grpc/proto/types.proto | 177 +++++++++--------- .../minotari_app_grpc/proto/wallet.proto | 132 +++++++------ .../src/conversions/consensus_constants.rs | 1 - .../src/automation/commands.rs | 3 + .../minotari_console_wallet/src/cli.rs | 1 + .../src/grpc/wallet_grpc_server.rs | 6 + .../tests/blockchain_database.rs | 1 + .../core/src/consensus/consensus_constants.rs | 17 +- .../transaction_components/output_features.rs | 2 + .../aggregate_body_chain_validator.rs | 45 ++++- .../aggregate_body_internal_validator.rs | 9 +- .../src/output_manager_service/service.rs | 6 +- .../wallet/src/transaction_service/handle.rs | 3 + .../wallet/src/transaction_service/service.rs | 9 + 14 files changed, 248 insertions(+), 164 deletions(-) diff --git a/applications/minotari_app_grpc/proto/types.proto b/applications/minotari_app_grpc/proto/types.proto index c652dbd6bf..c3dad06f31 100644 --- a/applications/minotari_app_grpc/proto/types.proto +++ b/applications/minotari_app_grpc/proto/types.proto @@ -1,32 +1,38 @@ // Copyright 2020. The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// 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. +// 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. +// 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. +// 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. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; package tari.rpc; /// An unsigned range interface to more accurately represent Rust native Range's message Range { - uint64 min = 1; - uint64 max = 2; + uint64 min = 1; + uint64 max = 2; } /// An Empty placeholder for endpoints without request parameters @@ -34,113 +40,112 @@ message Empty {} /// Define an interface for block height message BlockHeight { - uint64 block_height = 1; + uint64 block_height = 1; } -// Define the explicit Signature implementation for the Minotari base layer. A different signature scheme can be -// employed by redefining this type. +// Define the explicit Signature implementation for the Minotari base layer. A +// different signature scheme can be employed by redefining this type. message Signature { - bytes public_nonce = 1; - bytes signature = 2; + bytes public_nonce = 1; + bytes signature = 2; } -// Define the explicit ComAndPubSignature implementation for the Minotari base layer. A different signature scheme can be -// employed by redefining this type. +// Define the explicit ComAndPubSignature implementation for the Minotari base +// layer. A different signature scheme can be employed by redefining this type. message ComAndPubSignature { - bytes ephemeral_commitment = 1; - bytes ephemeral_pubkey = 2; - bytes u_a = 3; - bytes u_x = 4; - bytes u_y = 5; + bytes ephemeral_commitment = 1; + bytes ephemeral_pubkey = 2; + bytes u_a = 3; + bytes u_x = 4; + bytes u_y = 5; } -// Define the explicit CommitmentSignature implementation for the Minotari base layer. A different signature scheme can be -// employed by redefining this type +// Define the explicit CommitmentSignature implementation for the Minotari base +// layer. A different signature scheme can be employed by redefining this type message CommitmentSignature { - bytes public_nonce = 1; - bytes u = 2; - bytes v = 3; + bytes public_nonce = 1; + bytes u = 2; + bytes v = 3; } /// PoW Algorithm constants message PowAlgorithmConstants { - uint64 min_difficulty = 2; - uint64 max_difficulty = 3; - uint64 target_time = 4; + uint64 min_difficulty = 2; + uint64 max_difficulty = 3; + uint64 target_time = 4; } /// Weight params message WeightParams { - uint64 kernel_weight = 1; - uint64 input_weight = 2; - uint64 output_weight = 3; - uint64 features_and_scripts_bytes_per_gram = 4; + uint64 kernel_weight = 1; + uint64 input_weight = 2; + uint64 output_weight = 3; + uint64 features_and_scripts_bytes_per_gram = 4; } /// Output version message OutputsVersion { - Range outputs = 1; - Range features = 2; + Range outputs = 1; + Range features = 2; } /// Output types enum OutputType { - STANDARD = 0; - COINBASE = 1; - BURN = 2; - VALIDATOR_NODE_REGISTRATION = 3; - CODE_TEMPLATE_REGISTRATION = 4; + STANDARD = 0; + COINBASE = 1; + BURN = 2; + VALIDATOR_NODE_REGISTRATION = 3; + CODE_TEMPLATE_REGISTRATION = 4; } /// Range proof types enum RangeProofType { - BULLETPROOF_PLUS = 0; - REVEALED_VALUE = 1; + BULLETPROOF_PLUS = 0; + REVEALED_VALUE = 1; } message PermittedRangeProofs { - OutputType output_type = 1; - repeated RangeProofType range_proof_types = 2; + OutputType output_type = 1; + repeated RangeProofType range_proof_types = 2; } /// Range proof message RangeProof { - bytes proof_bytes = 1; + bytes proof_bytes = 1; } /// Consensus Constants response message ConsensusConstants { - uint64 coinbase_min_maturity = 1; - uint32 blockchain_version = 2; - uint64 future_time_limit = 3; - uint64 difficulty_block_window = 5; - uint64 max_block_transaction_weight = 7; - uint64 pow_algo_count = 8; - uint64 median_timestamp_count = 9; - uint64 emission_initial = 10; - repeated uint64 emission_decay = 11; - uint64 emission_tail = 12 [deprecated=true]; - uint64 min_sha3x_pow_difficulty = 13; - uint64 block_weight_inputs = 14; - uint64 block_weight_outputs = 15; - uint64 block_weight_kernels = 16; - uint64 faucet_value = 17; - uint64 max_script_byte_size = 18; - uint64 validator_node_validity_period = 19; - uint64 effective_from_height = 20; - Range valid_blockchain_version_range = 21; - uint64 max_randomx_seed_height = 22; - map proof_of_work = 23; - WeightParams transaction_weight = 24; - Range input_version_range = 26; - OutputsVersion output_version_range = 27; - Range kernel_version_range = 28; - repeated OutputType permitted_output_types = 29; - uint64 epoch_length = 30; - uint64 validator_node_registration_min_deposit_amount = 31; - uint64 validator_node_registration_min_lock_height = 32; - uint64 validator_node_registration_shuffle_interval_epoch = 33; - repeated PermittedRangeProofs permitted_range_proof_types = 34; - uint64 inflation_bips = 35; - uint64 tail_epoch_length = 36; + uint64 coinbase_min_maturity = 1; + uint32 blockchain_version = 2; + uint64 future_time_limit = 3; + uint64 difficulty_block_window = 5; + uint64 max_block_transaction_weight = 7; + uint64 pow_algo_count = 8; + uint64 median_timestamp_count = 9; + uint64 emission_initial = 10; + repeated uint64 emission_decay = 11; + uint64 emission_tail = 12 [deprecated = true]; + uint64 min_sha3x_pow_difficulty = 13; + uint64 block_weight_inputs = 14; + uint64 block_weight_outputs = 15; + uint64 block_weight_kernels = 16; + uint64 faucet_value = 17; + uint64 max_script_byte_size = 18; + uint64 validator_node_validity_period = 19; + uint64 effective_from_height = 20; + Range valid_blockchain_version_range = 21; + uint64 max_randomx_seed_height = 22; + map proof_of_work = 23; + WeightParams transaction_weight = 24; + Range input_version_range = 26; + OutputsVersion output_version_range = 27; + Range kernel_version_range = 28; + repeated OutputType permitted_output_types = 29; + uint64 epoch_length = 30; + uint64 validator_node_registration_min_deposit_amount = 31; + uint64 validator_node_registration_shuffle_interval_epoch = 33; + repeated PermittedRangeProofs permitted_range_proof_types = 34; + uint64 inflation_bips = 35; + uint64 tail_epoch_length = 36; } diff --git a/applications/minotari_app_grpc/proto/wallet.proto b/applications/minotari_app_grpc/proto/wallet.proto index ae049f99dc..07b83dc93a 100644 --- a/applications/minotari_app_grpc/proto/wallet.proto +++ b/applications/minotari_app_grpc/proto/wallet.proto @@ -1,24 +1,30 @@ // Copyright 2020. The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// 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. +// 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. +// 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. +// 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. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; package tari.rpc; @@ -32,59 +38,69 @@ import "network.proto"; // The gRPC interface for interacting with the wallet. service Wallet { // This returns the current version - rpc GetVersion (GetVersionRequest) returns (GetVersionResponse); + rpc GetVersion(GetVersionRequest) returns (GetVersionResponse); // This checks if the wallet is healthy and running - rpc CheckConnectivity(GetConnectivityRequest) returns (CheckConnectivityResponse); + rpc CheckConnectivity(GetConnectivityRequest) + returns (CheckConnectivityResponse); // Check for new updates - rpc CheckForUpdates (Empty) returns (SoftwareUpdate); + rpc CheckForUpdates(Empty) returns (SoftwareUpdate); // This returns the identity information - rpc Identify (GetIdentityRequest) returns (GetIdentityResponse); + rpc Identify(GetIdentityRequest) returns (GetIdentityResponse); // This returns the tari address - rpc GetAddress (Empty) returns (GetAddressResponse); + rpc GetAddress(Empty) returns (GetAddressResponse); // Send Minotari to a number of recipients - rpc Transfer (TransferRequest) returns (TransferResponse); + rpc Transfer(TransferRequest) returns (TransferResponse); // Returns the transaction details for the given transaction IDs - rpc GetTransactionInfo (GetTransactionInfoRequest) returns (GetTransactionInfoResponse); + rpc GetTransactionInfo(GetTransactionInfoRequest) + returns (GetTransactionInfoResponse); // Returns all transactions' details - rpc GetCompletedTransactions (GetCompletedTransactionsRequest) returns (stream GetCompletedTransactionsResponse); + rpc GetCompletedTransactions(GetCompletedTransactionsRequest) + returns (stream GetCompletedTransactionsResponse); // Returns the balance - rpc GetBalance (GetBalanceRequest) returns (GetBalanceResponse); + rpc GetBalance(GetBalanceRequest) returns (GetBalanceResponse); // Returns unspent amounts - rpc GetUnspentAmounts (Empty) returns (GetUnspentAmountsResponse); + rpc GetUnspentAmounts(Empty) returns (GetUnspentAmountsResponse); // Request the wallet perform a coinsplit - rpc CoinSplit (CoinSplitRequest) returns (CoinSplitResponse); + rpc CoinSplit(CoinSplitRequest) returns (CoinSplitResponse); // Import Utxo to wallet - rpc ImportUtxos (ImportUtxosRequest) returns (ImportUtxosResponse); + rpc ImportUtxos(ImportUtxosRequest) returns (ImportUtxosResponse); // Get Base Node network connectivity status rpc GetNetworkStatus(Empty) returns (NetworkStatusResponse); // List currently connected peers rpc ListConnectedPeers(Empty) returns (ListConnectedPeersResponse); // Cancel pending transaction - rpc CancelTransaction (CancelTransactionRequest) returns (CancelTransactionResponse); + rpc CancelTransaction(CancelTransactionRequest) + returns (CancelTransactionResponse); // Will trigger a complete revalidation of all wallet outputs. - rpc RevalidateAllTransactions (RevalidateRequest) returns (RevalidateResponse); + rpc RevalidateAllTransactions(RevalidateRequest) returns (RevalidateResponse); // Will trigger a validation of all wallet outputs. - rpc ValidateAllTransactions (ValidateRequest) returns (ValidateResponse); + rpc ValidateAllTransactions(ValidateRequest) returns (ValidateResponse); // This will send a XTR SHA Atomic swap transaction - rpc SendShaAtomicSwapTransaction(SendShaAtomicSwapRequest) returns (SendShaAtomicSwapResponse); + rpc SendShaAtomicSwapTransaction(SendShaAtomicSwapRequest) + returns (SendShaAtomicSwapResponse); // This will create a burn transaction - rpc CreateBurnTransaction(CreateBurnTransactionRequest) returns (CreateBurnTransactionResponse); + rpc CreateBurnTransaction(CreateBurnTransactionRequest) + returns (CreateBurnTransactionResponse); // This will claim a XTR SHA Atomic swap transaction - rpc ClaimShaAtomicSwapTransaction(ClaimShaAtomicSwapRequest) returns (ClaimShaAtomicSwapResponse); + rpc ClaimShaAtomicSwapTransaction(ClaimShaAtomicSwapRequest) + returns (ClaimShaAtomicSwapResponse); // This will claim a HTLC refund transaction - rpc ClaimHtlcRefundTransaction(ClaimHtlcRefundRequest) returns (ClaimHtlcRefundResponse); + rpc ClaimHtlcRefundTransaction(ClaimHtlcRefundRequest) + returns (ClaimHtlcRefundResponse); // Creates a transaction with a template registration output - rpc CreateTemplateRegistration(CreateTemplateRegistrationRequest) returns (CreateTemplateRegistrationResponse); + rpc CreateTemplateRegistration(CreateTemplateRegistrationRequest) + returns (CreateTemplateRegistrationResponse); rpc SetBaseNode(SetBaseNodeRequest) returns (SetBaseNodeResponse); - rpc StreamTransactionEvents(TransactionEventRequest) returns (stream TransactionEventResponse); + rpc StreamTransactionEvents(TransactionEventRequest) + returns (stream TransactionEventResponse); - rpc RegisterValidatorNode(RegisterValidatorNodeRequest) returns (RegisterValidatorNodeResponse); + rpc RegisterValidatorNode(RegisterValidatorNodeRequest) + returns (RegisterValidatorNodeResponse); } message GetVersionRequest {} - message GetVersionResponse { string version = 1; } @@ -101,7 +117,7 @@ message SendShaAtomicSwapRequest { PaymentRecipient recipient = 1; } -message CreateBurnTransactionRequest{ +message CreateBurnTransactionRequest { uint64 amount = 1; uint64 fee_per_gram = 2; string message = 3; @@ -109,7 +125,6 @@ message CreateBurnTransactionRequest{ bytes sidechain_deployment_key = 5; } - message PaymentRecipient { string address = 1; uint64 amount = 2; @@ -135,7 +150,7 @@ message SendShaAtomicSwapResponse { string failure_message = 5; } -message CreateBurnTransactionResponse{ +message CreateBurnTransactionResponse { uint64 transaction_id = 1; bool is_success = 2; string failure_message = 3; @@ -152,7 +167,7 @@ message TransferResult { string failure_message = 4; } -message ClaimShaAtomicSwapRequest{ +message ClaimShaAtomicSwapRequest { string output = 1; string pre_image = 2; uint64 fee_per_gram = 3; @@ -162,7 +177,7 @@ message ClaimShaAtomicSwapResponse { TransferResult results = 1; } -message ClaimHtlcRefundRequest{ +message ClaimHtlcRefundRequest { string output_hash = 1; uint64 fee_per_gram = 2; } @@ -200,9 +215,11 @@ enum TransactionDirection { } enum TransactionStatus { - // This transaction has been completed between the parties but has not been broadcast to the base layer network. + // This transaction has been completed between the parties but has not been + // broadcast to the base layer network. TRANSACTION_STATUS_COMPLETED = 0; - // This transaction has been broadcast to the base layer network and is currently in one or more base node mempools. + // This transaction has been broadcast to the base layer network and is + // currently in one or more base node mempools. TRANSACTION_STATUS_BROADCAST = 1; // This transaction has been mined and included in a block. TRANSACTION_STATUS_MINED_UNCONFIRMED = 2; @@ -216,9 +233,11 @@ enum TransactionStatus { TRANSACTION_STATUS_MINED_CONFIRMED = 6; // The transaction was rejected by the mempool TRANSACTION_STATUS_REJECTED = 7; - // This is faux transaction mainly for one-sided transaction outputs or wallet recovery outputs have been found + // This is faux transaction mainly for one-sided transaction outputs or wallet + // recovery outputs have been found TRANSACTION_STATUS_ONE_SIDED_UNCONFIRMED = 8; - // All Imported and FauxUnconfirmed transactions will end up with this status when the outputs have been confirmed + // All Imported and FauxUnconfirmed transactions will end up with this status + // when the outputs have been confirmed TRANSACTION_STATUS_ONE_SIDED_CONFIRMED = 9; // This transaction is still being queued for sending TRANSACTION_STATUS_QUEUED = 10; @@ -296,24 +315,24 @@ message CancelTransactionResponse { string failure_message = 2; } -message RevalidateRequest{} +message RevalidateRequest {} -message RevalidateResponse{} +message RevalidateResponse {} -message ValidateRequest{} +message ValidateRequest {} -message ValidateResponse{} +message ValidateResponse {} message SetBaseNodeRequest { string public_key_hex = 1; string net_address = 2; } -message SetBaseNodeResponse{} +message SetBaseNodeResponse {} -message GetConnectivityRequest{} +message GetConnectivityRequest {} -message CheckConnectivityResponse{ +message CheckConnectivityResponse { enum OnlineStatus { Connecting = 0; Online = 1; @@ -322,9 +341,7 @@ message CheckConnectivityResponse{ OnlineStatus status = 1; } -message TransactionEventRequest{ - -} +message TransactionEventRequest {} message TransactionEvent { string event = 1; @@ -348,6 +365,7 @@ message RegisterValidatorNodeRequest { uint64 fee_per_gram = 4; string message = 5; bytes sidechain_deployment_key = 6; + uint64 validator_maturity = 7; } message RegisterValidatorNodeResponse { diff --git a/applications/minotari_app_grpc/src/conversions/consensus_constants.rs b/applications/minotari_app_grpc/src/conversions/consensus_constants.rs index 74818c8cb0..96e28736b9 100644 --- a/applications/minotari_app_grpc/src/conversions/consensus_constants.rs +++ b/applications/minotari_app_grpc/src/conversions/consensus_constants.rs @@ -135,7 +135,6 @@ impl From for grpc::ConsensusConstants { validator_node_registration_min_deposit_amount: cc .validator_node_registration_min_deposit_amount() .as_u64(), - validator_node_registration_min_lock_height: cc.validator_node_registration_min_lock_height(), validator_node_registration_shuffle_interval_epoch: cc .validator_node_registration_shuffle_interval() .as_u64(), diff --git a/applications/minotari_console_wallet/src/automation/commands.rs b/applications/minotari_console_wallet/src/automation/commands.rs index c3980cb23e..827152837e 100644 --- a/applications/minotari_console_wallet/src/automation/commands.rs +++ b/applications/minotari_console_wallet/src/automation/commands.rs @@ -211,6 +211,7 @@ pub async fn register_validator_node( validator_node_signature: Signature, validator_node_claim_public_key: PublicKey, sidechain_deployment_key: Option, + validator_maturity: Option, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -222,6 +223,7 @@ pub async fn register_validator_node( validator_node_signature, validator_node_claim_public_key, sidechain_deployment_key, + validator_maturity, selection_criteria, fee_per_gram, message, @@ -1037,6 +1039,7 @@ pub async fn command_runner( &args.sidechain_deployment_key[0], )?) }, + args.validator_maturity, UtxoSelectionCriteria::default(), config.fee_per_gram * uT, args.message, diff --git a/applications/minotari_console_wallet/src/cli.rs b/applications/minotari_console_wallet/src/cli.rs index fe0bb377ac..0c5aeab3ca 100644 --- a/applications/minotari_console_wallet/src/cli.rs +++ b/applications/minotari_console_wallet/src/cli.rs @@ -306,6 +306,7 @@ pub struct RegisterValidatorNodeArgs { pub validator_node_claim_public_key: UniPublicKey, #[clap(long, parse(try_from_str = parse_hex), required = false)] pub sidechain_deployment_key: Vec>, + pub validator_maturity: Option, #[clap(short, long, default_value = "Registering VN")] pub message: String, } diff --git a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs index 3ded0ee040..6b2128c8c0 100644 --- a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -1005,6 +1005,11 @@ impl wallet_server::Wallet for WalletGrpcServer { .map_err(|_| Status::invalid_argument("Validator node signature is malformed!"))?; let validator_node_claim_public_key = PublicKey::from_canonical_bytes(&request.validator_node_claim_public_key) .map_err(|_| Status::invalid_argument("Claim public key is malformed"))?; + let validator_maturity = if request.validator_maturity > 0 { + Some(request.validator_maturity) + } else { + None + }; let sidechain_key = if request.sidechain_deployment_key.is_empty() { None @@ -1027,6 +1032,7 @@ impl wallet_server::Wallet for WalletGrpcServer { validator_node_signature, validator_node_claim_public_key, sidechain_key, + validator_maturity, UtxoSelectionCriteria::default(), request.fee_per_gram.into(), request.message, diff --git a/base_layer/core/src/chain_storage/tests/blockchain_database.rs b/base_layer/core/src/chain_storage/tests/blockchain_database.rs index a7fa5e4c01..3cf9fef173 100644 --- a/base_layer/core/src/chain_storage/tests/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/tests/blockchain_database.rs @@ -624,6 +624,7 @@ mod validator_node_merkle_root { public_key.clone(), None, None, + 10000, ); let (tx, _outputs) = schema_to_transaction( &[txn_schema!( diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 530c9aa9f9..097d16397d 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -117,8 +117,6 @@ pub struct ConsensusConstants { vn_validity_period_epochs: VnEpoch, /// The min amount of micro Minotari to deposit for a registration transaction to be allowed onto the blockchain vn_registration_min_deposit_amount: MicroMinotari, - /// The period that the registration funds are required to be locked up. - vn_registration_lock_height: u64, /// The period after which the VNs will be reshuffled. vn_registration_shuffle_interval: VnEpoch, } @@ -346,8 +344,11 @@ impl ConsensusConstants { self.vn_registration_min_deposit_amount } - pub fn validator_node_registration_min_lock_height(&self) -> u64 { - self.vn_registration_lock_height + pub fn validator_node_registration_min_lock_height(&self, height: u64) -> u64 { + // This calculates the height of the block in which the VN is not valid anymore. + self.epoch_to_block_height(VnEpoch( + self.block_height_to_epoch(height).as_u64() + 1 + self.vn_validity_period_epochs.as_u64(), + )) } /// Returns the current epoch from the given height @@ -405,8 +406,7 @@ impl ConsensusConstants { max_covenant_length: 100, vn_epoch_length: 10, vn_validity_period_epochs: VnEpoch(100), - vn_registration_min_deposit_amount: MicroMinotari(0), - vn_registration_lock_height: 0, + vn_registration_min_deposit_amount: MicroMinotari(1000000), vn_registration_shuffle_interval: VnEpoch(100), coinbase_output_features_extra_max_length: 64, }]; @@ -471,7 +471,6 @@ impl ConsensusConstants { vn_epoch_length: 10, vn_validity_period_epochs: VnEpoch(3), vn_registration_min_deposit_amount: MicroMinotari(0), - vn_registration_lock_height: 0, vn_registration_shuffle_interval: VnEpoch(100), coinbase_output_features_extra_max_length: 64, }]; @@ -527,7 +526,6 @@ impl ConsensusConstants { vn_epoch_length: 60, vn_validity_period_epochs: VnEpoch(100), vn_registration_min_deposit_amount: MicroMinotari(0), - vn_registration_lock_height: 0, vn_registration_shuffle_interval: VnEpoch(100), coinbase_output_features_extra_max_length: 64, }]; @@ -583,7 +581,6 @@ impl ConsensusConstants { vn_epoch_length: 60, vn_validity_period_epochs: VnEpoch(100), vn_registration_min_deposit_amount: MicroMinotari(0), - vn_registration_lock_height: 0, vn_registration_shuffle_interval: VnEpoch(100), coinbase_output_features_extra_max_length: 64, }]; @@ -633,7 +630,6 @@ impl ConsensusConstants { vn_epoch_length: 60, vn_validity_period_epochs: VnEpoch(100), vn_registration_min_deposit_amount: MicroMinotari(0), - vn_registration_lock_height: 0, vn_registration_shuffle_interval: VnEpoch(100), coinbase_output_features_extra_max_length: 64, }]; @@ -685,7 +681,6 @@ impl ConsensusConstants { vn_epoch_length: 60, vn_validity_period_epochs: VnEpoch(100), vn_registration_min_deposit_amount: MicroMinotari(0), - vn_registration_lock_height: 0, vn_registration_shuffle_interval: VnEpoch(100), coinbase_output_features_extra_max_length: 64, }]; diff --git a/base_layer/core/src/transactions/transaction_components/output_features.rs b/base_layer/core/src/transactions/transaction_components/output_features.rs index 3f525da34a..0b5a8fa23c 100644 --- a/base_layer/core/src/transactions/transaction_components/output_features.rs +++ b/base_layer/core/src/transactions/transaction_components/output_features.rs @@ -158,6 +158,7 @@ impl OutputFeatures { claim_public_key: PublicKey, sidechain_id: Option, sidechain_id_knowledge_proof: Option, + maturity: u64, ) -> OutputFeatures { OutputFeatures { output_type: OutputType::ValidatorNodeRegistration, @@ -169,6 +170,7 @@ impl OutputFeatures { sidechain_id_knowledge_proof, ), )), + maturity, ..Default::default() } } diff --git a/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs b/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs index 1ffdb5be0b..bcccc592ca 100644 --- a/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs +++ b/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs @@ -60,14 +60,25 @@ impl AggregateBodyChainLinkedValidator { ) -> Result { let constants = self.consensus_manager.consensus_constants(height); - self.validate_consensus(body, db)?; + self.validate_consensus(body, db, constants, height)?; let body = self.validate_input_and_maturity(body, db, constants, height)?; Ok(body) } - fn validate_consensus(&self, body: &AggregateBody, db: &B) -> Result<(), ValidationError> { + fn validate_consensus( + &self, + body: &AggregateBody, + db: &B, + constants: &ConsensusConstants, + height: u64, + ) -> Result<(), ValidationError> { validate_excess_sig_not_in_db(body, db)?; + + for output in body.outputs() { + check_validator_node_registration_utxo(constants, output, height)?; + } + Ok(()) } @@ -86,7 +97,7 @@ impl AggregateBodyChainLinkedValidator { validate_input_maturity(&body, height)?; check_inputs_are_utxos(db, &body)?; - check_outputs(db, constants, &body)?; + check_outputs(db, constants, &body, height)?; verify_no_duplicated_inputs_outputs(&body)?; check_total_burned(&body)?; verify_timelocks(&body, height)?; @@ -172,6 +183,32 @@ fn validate_excess_sig_not_in_db(body: &AggregateBody, db: Ok(()) } +fn check_validator_node_registration_utxo( + consensus_constants: &ConsensusConstants, + utxo: &TransactionOutput, + height: u64, +) -> Result<(), ValidationError> { + if let Some(reg) = utxo.features.validator_node_registration() { + if utxo.minimum_value_promise < consensus_constants.validator_node_registration_min_deposit_amount() { + return Err(ValidationError::ValidatorNodeRegistrationMinDepositAmount { + min: consensus_constants.validator_node_registration_min_deposit_amount(), + actual: utxo.minimum_value_promise, + }); + } + if utxo.features.maturity < consensus_constants.validator_node_registration_min_lock_height(height) { + return Err(ValidationError::ValidatorNodeRegistrationMinLockHeight { + min: consensus_constants.validator_node_registration_min_lock_height(height), + actual: utxo.features.maturity, + }); + } + + if !reg.is_valid_signature_for(&[]) { + return Err(ValidationError::InvalidValidatorNodeSignature); + } + } + Ok(()) +} + /// This function checks that all inputs in the blocks are valid UTXO's to be spent fn check_inputs_are_utxos(db: &B, body: &AggregateBody) -> Result<(), ValidationError> { let mut not_found_inputs = Vec::new(); @@ -220,11 +257,13 @@ pub fn check_outputs( db: &B, constants: &ConsensusConstants, body: &AggregateBody, + height: u64, ) -> Result<(), ValidationError> { let max_script_size = constants.max_script_byte_size(); for output in body.outputs() { check_tari_script_byte_size(&output.script, max_script_size)?; check_not_duplicate_txo(db, output)?; + check_validator_node_registration_utxo(constants, output, height)?; } Ok(()) } diff --git a/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs b/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs index 6437cd5efb..dd03f97a7d 100644 --- a/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs +++ b/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs @@ -115,9 +115,7 @@ impl AggregateBodyInternalConsistencyValidator { check_script_size(output, constants.max_script_byte_size())?; check_covenant_length(&output.covenant, constants.max_covenant_length())?; check_permitted_range_proof_types(constants, output)?; - check_validator_node_registration_utxo(constants, output)?; - check_template_registration_utxo(output)?; - check_confidential_output_utxo(output)?; + check_validator_node_registration_utxo(constants, output, height)?; } check_weight(body, height, constants)?; @@ -436,6 +434,7 @@ fn check_total_burned(body: &AggregateBody) -> Result<(), ValidationError> { fn check_validator_node_registration_utxo( consensus_constants: &ConsensusConstants, utxo: &TransactionOutput, + height: u64, ) -> Result<(), ValidationError> { if let Some(reg) = utxo.features.validator_node_registration() { if utxo.minimum_value_promise < consensus_constants.validator_node_registration_min_deposit_amount() { @@ -444,9 +443,9 @@ fn check_validator_node_registration_utxo( actual: utxo.minimum_value_promise, }); } - if utxo.features.maturity < consensus_constants.validator_node_registration_min_lock_height() { + if utxo.features.maturity < consensus_constants.validator_node_registration_min_lock_height(height) { return Err(ValidationError::ValidatorNodeRegistrationMinLockHeight { - min: consensus_constants.validator_node_registration_min_lock_height(), + min: consensus_constants.validator_node_registration_min_lock_height(height), actual: utxo.features.maturity, }); } diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 674f4e3742..7718edf415 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1891,7 +1891,11 @@ where .key_manager .encrypt_data_for_recovery(&spending_key_id, None, amount.as_u64()) .await?; - let minimum_value_promise = MicroMinotari::zero(); + let minimum_value_promise = if output_features.validator_node_registration().is_none() { + MicroMinotari::zero() + } else { + amount + }; let metadata_message = TransactionOutput::metadata_signature_message_from_parts( &TransactionOutputVersion::get_current_version(), &script, diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index 856d5dc56c..3ff5e355de 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -104,6 +104,7 @@ pub enum TransactionServiceRequest { validator_node_signature: Signature, validator_node_claim_public_key: CommsPublicKey, sidechain_deployment_key: Option, + validator_maturity: Option, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -486,6 +487,7 @@ impl TransactionServiceHandle { validator_node_signature: Signature, validator_node_claim_public_key: PublicKey, sidechain_deployment_key: Option, + validator_maturity: Option, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -498,6 +500,7 @@ impl TransactionServiceHandle { validator_node_signature, validator_node_claim_public_key, sidechain_deployment_key, + validator_maturity, selection_criteria, fee_per_gram, message, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 2b757d679e..b02e493992 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -685,6 +685,7 @@ where validator_node_signature, validator_node_claim_public_key, sidechain_deployment_key, + validator_maturity, selection_criteria, fee_per_gram, message, @@ -696,6 +697,7 @@ where validator_node_signature, validator_node_claim_public_key, sidechain_deployment_key, + validator_maturity, selection_criteria, fee_per_gram, message, @@ -1778,6 +1780,7 @@ where validator_node_signature: Signature, validator_node_claim_public_key: PublicKey, sidechain_deployment_key: Option, + validator_maturity: Option, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -1805,6 +1808,12 @@ where validator_node_claim_public_key, sidechain_id, sidechain_id_knowledge_proof, + validator_maturity.unwrap_or_else(|| { + let next_height = self.last_seen_tip_height.unwrap_or(0) + 1; + self.consensus_manager + .consensus_constants(next_height) + .validator_node_registration_min_lock_height(next_height) + }), ); self.send_transaction( self.resources.wallet_identity.address.clone(), From 4c2a78ea7da42e170e1d4c8928d38e097f2ad207 Mon Sep 17 00:00:00 2001 From: Martin Stefcek Date: Mon, 15 Apr 2024 17:13:43 +0400 Subject: [PATCH 2/4] fix clippy --- base_layer/core/src/chain_storage/tests/blockchain_database.rs | 1 + .../validation/aggregate_body/aggregate_body_chain_validator.rs | 2 +- .../aggregate_body/aggregate_body_internal_validator.rs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base_layer/core/src/chain_storage/tests/blockchain_database.rs b/base_layer/core/src/chain_storage/tests/blockchain_database.rs index 3cf9fef173..1279b785b7 100644 --- a/base_layer/core/src/chain_storage/tests/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/tests/blockchain_database.rs @@ -682,6 +682,7 @@ mod validator_node_merkle_root { public_key.clone(), Some(sidechain_public.clone()), Some(sidechain_signature), + 10000, ); let (tx, _outputs) = schema_to_transaction( &[txn_schema!( diff --git a/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs b/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs index bcccc592ca..6d977ff503 100644 --- a/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs +++ b/base_layer/core/src/validation/aggregate_body/aggregate_body_chain_validator.rs @@ -31,7 +31,7 @@ use crate::{ consensus::{ConsensusConstants, ConsensusManager}, transactions::{ aggregated_body::AggregateBody, - transaction_components::{TransactionError, TransactionInput}, + transaction_components::{TransactionError, TransactionInput, TransactionOutput}, }, validation::{ helpers::{check_input_is_utxo, check_not_duplicate_txo, check_tari_script_byte_size}, diff --git a/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs b/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs index dd03f97a7d..bcc937e868 100644 --- a/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs +++ b/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs @@ -115,6 +115,8 @@ impl AggregateBodyInternalConsistencyValidator { check_script_size(output, constants.max_script_byte_size())?; check_covenant_length(&output.covenant, constants.max_covenant_length())?; check_permitted_range_proof_types(constants, output)?; + check_template_registration_utxo(output)?; + check_confidential_output_utxo(output)?; check_validator_node_registration_utxo(constants, output, height)?; } From 714c69c6cc9eca36c31acc0cba5e81579e3b94f3 Mon Sep 17 00:00:00 2001 From: Martin Stefcek Date: Tue, 16 Apr 2024 23:28:01 +0400 Subject: [PATCH 3/4] update comment --- base_layer/core/src/consensus/consensus_constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 097d16397d..09c3ffb61a 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -345,7 +345,7 @@ impl ConsensusConstants { } pub fn validator_node_registration_min_lock_height(&self, height: u64) -> u64 { - // This calculates the height of the block in which the VN is not valid anymore. + // This calculates the minimum height of the block for which the VN registration maturity must be valid for. self.epoch_to_block_height(VnEpoch( self.block_height_to_epoch(height).as_u64() + 1 + self.vn_validity_period_epochs.as_u64(), )) From 86f4d2d34b5fd8322af981cc187a417313c121e1 Mon Sep 17 00:00:00 2001 From: Martin Stefcek Date: Fri, 19 Apr 2024 10:49:50 +0400 Subject: [PATCH 4/4] add suggestion --- base_layer/core/src/consensus/consensus_constants.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 09c3ffb61a..5a9791190e 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -362,6 +362,8 @@ impl ConsensusConstants { } pub fn epoch_length(&self) -> u64 { + #[cfg(any(test, debug_assertions))] + assert_ne!(self.vn_epoch_length, 0, "Epoch length must be greater than 0"); self.vn_epoch_length }