Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow zero fees for pre-mine #6595

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions applications/minotari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,11 @@ pub async fn command_runner(

match encumber_aggregate_utxo(
transaction_service.clone(),
session_info.fee_per_gram,
if session_info.use_pre_mine_input_file {
MicroMinotari::zero()
} else {
session_info.fee_per_gram
},
embedded_output.commitment.clone(),
input_shares,
script_signature_public_nonces,
Expand Down Expand Up @@ -1576,6 +1580,7 @@ pub async fn command_runner(
}

// Create finalized spend transactions
let mut inputs = Vec::new();
let mut outputs = Vec::new();
let mut kernels = Vec::new();
for (indexed_info, leader_self) in party_info_per_index.iter().zip(leader_info.outputs_for_self.iter())
Expand Down Expand Up @@ -1605,22 +1610,17 @@ pub async fn command_runner(
break;
}

if args.print_to_console || args.save_to_file {
if session_info.use_pre_mine_input_file {
match transaction_service.get_any_transaction(leader_self.tx_id).await {
Ok(Some(WalletTransaction::Completed(tx))) => {
if args.save_to_file {
for output in tx.transaction.body.outputs() {
outputs.push(output.clone());
}
for kernel in tx.transaction.body.kernels() {
kernels.push(kernel.clone());
}
for input in tx.transaction.body.inputs() {
inputs.push(input.clone());
}
for output in tx.transaction.body.outputs() {
outputs.push(output.clone());
}
if args.print_to_console {
let tx_console = serde_json::to_string(&tx.transaction).unwrap_or_else(|_| {
format!("Transaction to json conversion error! ('{}')", leader_self.tx_id)
});
println!("Tx_Id: {}, Tx: {}", leader_self.tx_id, tx_console);
for kernel in tx.transaction.body.kernels() {
kernels.push(kernel.clone());
}
},
Ok(_) => {
Expand All @@ -1638,7 +1638,7 @@ pub async fn command_runner(
}
}

if args.save_to_file {
if session_info.use_pre_mine_input_file {
let file_name = get_pre_mine_addition_file_name();
let out_dir_path = out_dir(&args.session_id)?;
let out_file = out_dir_path.join(&file_name);
Expand All @@ -1651,6 +1651,24 @@ pub async fn command_runner(
};

let mut error = false;
for input in inputs {
let input_s = match serde_json::to_string(&input) {
Ok(val) => val,
Err(e) => {
eprintln!("\nError: Could not serialize UTXO ({})\n", e);
error = true;
break;
},
};
if let Err(e) = file_stream.write_all(format!("{}\n", input_s).as_bytes()) {
eprintln!("\nError: Could not write UTXO to file ({})\n", e);
error = true;
break;
}
}
if error {
break;
}
for output in outputs {
let utxo_s = match serde_json::to_string(&output) {
Ok(val) => val,
Expand Down Expand Up @@ -2405,7 +2423,7 @@ fn read_genesis_file_outputs(
}
file
} else {
return Err("Missing pre-mine file!".to_string());
return Err("Missing pre-mine file! Need '--pre-mine-file-path <path_to_file>.'".to_string());
};

let file = File::open(file_path.clone())
Expand Down
6 changes: 2 additions & 4 deletions applications/minotari_console_wallet/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,6 @@ pub struct PreMineSpendAggregateTransactionArgs {
pub session_id: String,
#[clap(long)]
pub input_file_names: Vec<String>,
#[clap(long)]
pub save_to_file: bool,
#[clap(long)]
pub print_to_console: bool,
}

#[derive(Debug, Args, Clone)]
Expand All @@ -295,6 +291,8 @@ pub struct PreMineSpendBackupUtxoArgs {
pub output_index: usize,
#[clap(long)]
pub recipient_address: TariAddress,
#[clap(long)]
pub pre_mine_file_path: Option<PathBuf>,
}

#[derive(Debug, Args, Clone)]
Expand Down
24 changes: 21 additions & 3 deletions base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use crate::{
proof_of_work::{AccumulatedDifficulty, Difficulty, PowAlgorithm, PowData, ProofOfWork},
transactions::{
aggregated_body::AggregateBody,
transaction_components::{TransactionKernel, TransactionOutput},
transaction_components::{TransactionInput, TransactionKernel, TransactionOutput},
},
OutputSmt,
};
Expand All @@ -54,9 +54,12 @@ pub fn get_genesis_block(network: Network) -> ChainBlock {

fn add_pre_mine_utxos_to_genesis_block(file: &str, block: &mut Block) {
let mut utxos = Vec::new();
let mut inputs = Vec::new();
for line in file.lines() {
if let Ok(utxo) = serde_json::from_str::<TransactionOutput>(line) {
utxos.push(utxo);
} else if let Ok(input) = serde_json::from_str::<TransactionInput>(line) {
inputs.push(input);
} else if let Ok(kernel) = serde_json::from_str::<TransactionKernel>(line) {
block.body.add_kernel(kernel);
block.header.kernel_mmr_size += 1;
Expand All @@ -66,6 +69,7 @@ fn add_pre_mine_utxos_to_genesis_block(file: &str, block: &mut Block) {
}
block.header.output_smt_size += utxos.len() as u64;
block.body.add_outputs(utxos);
block.body.add_inputs(inputs);
block.body.sort();
}

Expand Down Expand Up @@ -647,14 +651,28 @@ mod test {
// Check that the pre_mine UTXOs balance (the pre_mine_value consensus constant is set correctly and pre_mine
// kernel is correct)

let utxo_sum = block.block().body.outputs().iter().map(|o| &o.commitment).sum();
let input_sum = block
.block()
.body
.inputs()
.iter()
.map(|o| o.commitment().unwrap())
.sum::<Commitment>();
let utxo_sum = block
.block()
.body
.outputs()
.iter()
.map(|o| &o.commitment)
.sum::<Commitment>();
let total_utxo_sum = &utxo_sum - &input_sum;
let kernel_sum = block.block().body.kernels().iter().map(|k| &k.excess).sum();

let db = create_new_blockchain_with_network(network);

let lock = db.db_read_access().unwrap();
ChainBalanceValidator::new(ConsensusManager::builder(network).build().unwrap(), Default::default())
.validate(&*lock, 0, &utxo_sum, &kernel_sum, &Commitment::default())
.validate(&*lock, 0, &total_utxo_sum, &kernel_sum, &Commitment::default())
.unwrap();
}

Expand Down
9 changes: 0 additions & 9 deletions base_layer/core/src/transactions/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,13 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::cmp::max;

use super::{tari_amount::MicroMinotari, weight::TransactionWeight};
use crate::transactions::aggregated_body::AggregateBody;

#[derive(Debug, Clone, Copy)]
pub struct Fee(TransactionWeight);

impl Fee {
pub(crate) const MINIMUM_TRANSACTION_FEE: MicroMinotari = MicroMinotari(101);

pub fn new(weight: TransactionWeight) -> Self {
Self(weight)
}
Expand Down Expand Up @@ -62,11 +58,6 @@ impl Fee {
Ok(MicroMinotari::from(weight) * fee_per_gram)
}

/// Normalizes the given fee returning a fee that is equal to or above the minimum fee
pub fn normalize(fee: MicroMinotari) -> MicroMinotari {
max(Self::MINIMUM_TRANSACTION_FEE, fee)
}

pub fn weighting(&self) -> &TransactionWeight {
&self.0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ use crate::{
consensus::ConsensusConstants,
covenants::Covenant,
transactions::{
fee::Fee,
key_manager::{TariKeyId, TransactionKeyManagerInterface, TxoStage},
tari_amount::*,
transaction_components::{
Expand Down Expand Up @@ -720,11 +719,6 @@ impl SenderTransactionProtocol {
/// Performs sanity checks on the collected transaction pieces prior to building the final Transaction instance
fn validate(&self) -> Result<(), TPE> {
if let SenderState::Finalizing(info) = &self.state {
let fee = info.metadata.fee;
// The fee must be greater than MIN_FEE to prevent spam attacks
if fee < Fee::MINIMUM_TRANSACTION_FEE {
return Err(TPE::ValidationError("Fee is less than the minimum".into()));
}
// Prevent overflow attacks by imposing sane limits on some key parameters
if info.inputs.len() > MAX_TRANSACTION_INPUTS {
return Err(TPE::ValidationError("Too many inputs in transaction".into()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,6 @@ where KM: TransactionKeyManagerInterface
target: LOG_TARGET,
"Build transaction with Fee: {}. Change: {}. Output: {:?}", total_fee, change, change_output,
);
// Some checks on the fee
if total_fee < Fee::MINIMUM_TRANSACTION_FEE {
return self.build_err("Fee is less than the minimum");
}

let change_output_pair = match change_output {
Some((output, sender_offset_key_id)) => {
Expand Down Expand Up @@ -843,12 +839,13 @@ mod test {
}

#[tokio::test]
async fn fee_too_low() {
async fn zero_fee_allowed() {
// Create some inputs
let key_manager = create_memory_db_key_manager().unwrap();
let p = TestParams::new(&key_manager).await;
let fee_per_gram = MicroMinotari(0);
let tx_fee = p.fee().calculate(
MicroMinotari(1),
fee_per_gram,
1,
1,
1,
Expand All @@ -874,7 +871,7 @@ mod test {
Covenant::default(),
TariAddress::default(),
)
.with_fee_per_gram(MicroMinotari(1))
.with_fee_per_gram(fee_per_gram)
.with_recipient_data(
script,
Default::default(),
Expand All @@ -884,8 +881,7 @@ mod test {
)
.await
.unwrap();
let err = builder.build().await.unwrap_err();
assert_eq!(err.message, "Fee is less than the minimum");
assert!(builder.build().await.is_ok(), "Zero fee should be allowed");
}

#[tokio::test]
Expand Down
9 changes: 4 additions & 5 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,15 +937,14 @@ where
.get_serialized_size()
.map_err(|e| OutputManagerError::ConversionError(e.to_string()))?,
);
let fee = fee_calc.calculate(fee_per_gram, 1, 1, num_outputs, default_features_and_scripts_size);
return Ok(Fee::normalize(fee));
return Ok(fee_calc.calculate(fee_per_gram, 1, 1, num_outputs, default_features_and_scripts_size));
},
Err(e) => Err(e),
}?;

debug!(target: LOG_TARGET, "{} utxos selected.", utxo_selection.utxos.len());

let fee = Fee::normalize(utxo_selection.as_final_fee());
let fee = utxo_selection.as_final_fee();

debug!(target: LOG_TARGET, "Fee calculated: {}", fee);
Ok(fee)
Expand Down Expand Up @@ -1279,7 +1278,7 @@ where
output_hash, tx_id
))
})?,
UseOutput::AsProvided(val) => val,
UseOutput::AsProvided(ref val) => val.clone(),
};
if output.commitment != expected_commitment {
return Err(OutputManagerError::ServiceError(format!(
Expand Down Expand Up @@ -1386,7 +1385,7 @@ where
let fee = self.get_fee_calc();
let fee = fee.calculate(fee_per_gram, 1, 1, 1, metadata_byte_size);
let amount = input.value - fee;
trace!(target: LOG_TARGET, "encumber_aggregate_utxo: created script");
trace!(target: LOG_TARGET, "encumber_aggregate_utxo: created script, with fee {}", fee);

// Create sender transaction protocol builder with recipient data and no change
let mut builder = SenderTransactionProtocol::builder(
Expand Down
Loading