diff --git a/applications/test_faucet/src/main.rs b/applications/test_faucet/src/main.rs index eb426cbf85..84768aa4c4 100644 --- a/applications/test_faucet/src/main.rs +++ b/applications/test_faucet/src/main.rs @@ -121,7 +121,7 @@ async fn write_keys(mut rx: mpsc::Receiver<(TransactionOutput, PrivateKey, Micro Err(e) => println!("{}", e), } } - let (pk, sig) = test_helpers::create_random_signature_from_s_key(key_sum, 0.into(), 0); + let (pk, sig) = test_helpers::create_random_signature_from_s_key(key_sum, 0.into(), 0, KernelFeatures::empty()); let excess = Commitment::from_public_key(&pk); let kernel = TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None); diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index b016b5236e..14ecb9db8e 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -93,8 +93,8 @@ pub fn get_igor_genesis_block() -> ChainBlock { fn get_igor_genesis_block_raw() -> Block { let sig = Signature::new( - PublicKey::from_hex("aa8ae9b04d87a8e864ff8dfb1d37dc1b7ea0e7b8ec314c0d4172e228b7b6966f").unwrap(), - PrivateKey::from_hex("fff382bba64848ed7a15b97be2b9e3e7f0d8752b551ea794d08070f53e044309").unwrap(), + PublicKey::from_hex("4488793b42f196272fa83f0b3d44ce4726d80318972ab1136b409ab7c744ae37,").unwrap(), + PrivateKey::from_hex("c540beec61c57af5812398a23bf25478296437868dadae5d3254ef711c04b30f").unwrap(), ); let mut body = AggregateBody::new( vec![], @@ -105,10 +105,10 @@ fn get_igor_genesis_block_raw() -> Block { .. Default::default() }, Commitment::from_hex( - "e29d3838b35425b4bd2dc51bc1b6761652b08302c942a14c9557991c83a0cc75", + "4007211a1c6cc2f9992ce23769c02a4e9f37170765527935dd3f331e6ca04d73", ) .unwrap(), - BulletRangeProof::from_hex("01800de4e1880d93a37ba64c3ea89d4a0ae80e2eb13246fce934afb1137b76dc11e63844aa27ecea714e8dbc2ce493b570d400021e56e06f7a1c6282bf6691c36b1ccb13c90f9ae3c1d465b15df837f7aa80dc1588a710b025bfdf7e0f644dd11980546fc6818b5d743bccfb445dfd204595acbf12b55fa48af94c2d0c440d2a0d5a504ef2f187153d9858f36c70220a4664e14c63ba5eb61b5f29f5919ee55b04382997b9a5da0a9ecf73a87bdd62df5e940cc6920203ed052e9e5a8593d895045e060ad5ccdc09589764df8548b74aac78638aaec2f5b5a6a8059a655a014051e2de4a7cec5f1cea4eed03e284d8b4016bba1c01df28366048675b69edc9a111aad822eb2480f60f8a47e69a8dbe5da81d6fce2db1f8693a19ef7bd3874aef6c66808cf659248d2c1318ea10a3879d90c1e72e35f4ab2fa54a96cace0750cc0c243db3b9ea0d4689710e9b5333f8a2afe1498d858563d854ded157ac8edc387b34c17264fa732073f0cc84f3059b21ce56a36e365e9c8870f8cd04ab6954691784548aa91291866e22f63e075b7d6cd3e716b2a00ca54b8a97fe2d7849c11150b452eaf1e0e6c0d2dc47784cb40f9b3760fef866de67ea0fe6806bb1f9157f7286572aad2df06898c60812b296f7fb32780869f05df1317baf5061688b2d6a0616ace1b57ba77fd77550909beaa1aed0be964fc074d7fea017de081258c1da0febeca59e34cdf2f88c48052d8cfe8d504eb0bea91d95ad229cb37a06b1e5e20339aa4d5a6e2899a99e0ec40f83152839da49db9ade865ca9de305bb7f20d4d0c").unwrap(), + BulletRangeProof::from_hex("01da5f3db36c55ab6b99ebe3b204c5dfa78d0fa6eff4f7004b0d38109b7f447846aaa196d260f39b7b74e459ce1cc2adc5756329dd95ecabe68a14faa58312de38a4cd867f683ffb5e1ed8f2135c627f977c62249aba80b5f6a1e2e88aba7dbe7226f6fbe21ac82732ad5ef136224bce5910406a69dd8425e4508ddd0deff45b1a1aa4d298765b9bd603a29b60409f2f607cb6e910c8f5050ada662a281361435682f12ed51403872be454367a0dc0bb55a84cdf7328aee944a5ee22f831fedd00c0a7539faf15866770a1f6a5fbda5fe7030626508fa9e450f061191411290c3dbc6f4ee66e9fffde0520d309738f3251aaf3c46765df25c00cf5e4c35a50ed2e9483ec9d989c4043ede9405ed148258ddfbd157e6802234c112b35613b843a2570c08e71267df17c5e7fea0658d43a4b6971bc7b9229b433bac2cff0db1ca51ec23b6fe3423875cb5384116c90924125300130269ef644368aea5fc27715686ef6ece57f0089124c9711c73ad66bf6ffd82bd163297275ae52503ff772a5a523b0d09653b7f6df144ffabed39e8c7c56ff21b2296d480737ec8a688462dc54214a8aa8b9c0c3b3db82bd084ed95dd5609dcdd8a44d196928395cc1b1a9bac749eaa55a2c6bb8ac6cdb97dd0d9602fde08eac62c77bd61d27ee02c92f2cf6e373bb6257cdd436eca805c72c39a2e57d10316832b1a27a15c101d7fab0e872d10de295e55507309d60c653a3114acf8845c2568cb165d97bc6526308eae4551a0f02009c5bb2ec89d268007f148155c897ebbc4d3a5fa929b51948bb2d263c0001").unwrap(), // For genesis block: A default script can never be spent, intentionally TariScript::default(), // Script offset never checked for coinbase, thus can use default @@ -125,7 +125,7 @@ fn get_igor_genesis_block_raw() -> Block { MicroTari(0), 0, Commitment::from_hex( - "9474ba70976e2fa06f970bb83f7d0a4d4b45e6e29f834847b659d32102f90b51", + "58f6c7b149e49eac2a51498d0027a98c7f8115c3d808ca03717b0837303d614a", ) .unwrap(), sig,None @@ -143,10 +143,10 @@ fn get_igor_genesis_block_raw() -> Block { height: 0, prev_hash: vec![0; BLOCK_HASH_LENGTH], timestamp: timestamp.into(), - output_mr: from_hex("3cf274cea5e77c5b259f0bf0e63b2d4dcc1eaa0bb96c1497524f71ff84430815").unwrap(), - witness_mr: from_hex("0897676242cdb559647e12a2b416c518d4a5b737d66d72eb31cf24cc025700ad").unwrap(), + output_mr: from_hex("8c50b1b393d50f72140746cfef314612bf2d832cbb8a4af39df7ff70023f2632").unwrap(), + witness_mr: from_hex("35950652ecf2fa8d600fa99becd7ceae9474f2f351e2c94fd7989c9bbc81c9ff").unwrap(), output_mmr_size: 1, - kernel_mr: from_hex("d6db311096294e468177f294c4398275e843278274ba97a4e7d01f1a90cab86d").unwrap(), + kernel_mr: from_hex("9196491fe5659ced84b894ed1ee859400a051b9321b9d3ba54dba499fb7397d7").unwrap(), kernel_mmr_size: 1, input_mr: vec![0; BLOCK_HASH_LENGTH], total_kernel_offset: PrivateKey::from_hex( @@ -217,9 +217,9 @@ pub fn get_dibbler_genesis_block() -> ChainBlock { // println!("output mr: {}", block.header.output_mr.to_hex()); // Hardcode the Merkle roots once they've been computed above - block.header.kernel_mr = from_hex("51acb4b74cc2e43a11be4f283b653a6fc95666dcf90f66f0c32742c5fb77e640").unwrap(); - block.header.witness_mr = from_hex("1df4a4200338686763c784187f7077148986e088586cf4839147a3f56adc4af6").unwrap(); - block.header.output_mr = from_hex("f9616ca84e798022f638546e6ce372d1344eee56e5cf47ba7e2bf58b5e28bf45").unwrap(); + block.header.kernel_mr = from_hex("af55d39195d0f2bc16558e3e79e91fe65f52519189a14e842a39ac6bcb7170ae").unwrap(); + block.header.witness_mr = from_hex("a2f1e88886a3e8ecf8966625588d846bd236b85ac6b361acb7aed70b7287e99b").unwrap(); + block.header.output_mr = from_hex("c9e4382a60e6f190eb21aeb815d7449be27fe24b27867db798635c49ed134a5c").unwrap(); let accumulated_data = BlockHeaderAccumulatedData { hash: block.hash(), @@ -234,10 +234,10 @@ pub fn get_dibbler_genesis_block() -> ChainBlock { } fn get_dibbler_genesis_block_raw() -> Block { - // Note: Use print_new_genesis_block in block_builders.rs to generate the required fields below + // Note: Use print_new_genesis_block in core/tests/helpers/block_builders.rs to generate the required fields below let excess_sig = Signature::new( - PublicKey::from_hex("024008ec92ab04b039fcdef2d20e4a7a72f5088797cc16855d30b91d5cfbcb16").unwrap(), - PrivateKey::from_hex("5d37ce54fe8beeff5330cfca82997878f1263d76331114a9030a383bcdc9e901").unwrap(), + PublicKey::from_hex("0646f943fcfab97b981d259e1da31f170b9119234d35e235d88bf9d4f53fbd61").unwrap(), + PrivateKey::from_hex("aceea89fe16c6bcb2c188dd6ec519d89a035544419ec465feb129b1f67749c0d").unwrap(), ); let coinbase = TransactionOutput::new( TransactionOutputVersion::get_current_version(), @@ -254,8 +254,8 @@ fn get_dibbler_genesis_block_raw() -> Block { sidechain_checkpoint: None, committee_definition: None, }, - Commitment::from_hex("b699aba9a294d2e654bcd076cc2a6f8fb4ea5de880615a7e536267199da71c0f").unwrap(), - BulletRangeProof::from_hex("01ee67e6b49742e37a1db8649728f96d59e8f0568a28fbf6f98768db0084681a2776f0f43b1593b4aa18dc0d6d5648e25c44a20224ca5df8196928472720140a4356f7d057b873997de9b163f3377f8c96b061ccdeb0df3c7a2375ceeb8984af3104ff189b0f6f2ce1d774e0a2b48beb8f83d5484650084812cb0d47d3c0bc297790e40d9a5e8e03cc53cf9198ea7408984f663c7f24d9407b0603d7088d3dcd37d295b8350602cbd25e591d7a2db4693357f01af104079d2741dcb5c60424f7255c814e9d1f808ae6f40983c006a94012827c6c485f9fa1fe5fa0e3db8af34c4502ce691fbb08b06adb7c9ebfea63c968fb5995accbbcf3cbaef364c56cf1551646dc4cd6f2f062614daa7b957f1e163c0306e00d2fb055381c8182a63dd2d65cda0bd869da7c6a8b1b564a9376b1d46d40624ebb728443d34c4a0722fbcc152d4a5a1a19e35795b5283985958fd324525fe2f3ad7d8b799cd6ecb4811a8da42290e13d02454adfdcd9f0cc1b65fd8d1e10f6327e57537ebbc5515b0fc176c447ecbd1aebce5cd14a591572a73a3e4a2d964a96458dfa97da7efc0e8297e7f62400d264d2367621433dad037da65b48c568a920ed34645fb9efb6f0c47c27235e8c6750e139177bc2c911ceb40e5fa85359aa6389e404c6ba2e01fcd40cdee41b72f351a29627333cbec5d5842bcb092a6c23180dac2eb04420c09deaae05830b75836ab9c8a9a0991ed29b17eded69e024c9a7e4850d7e1ea275f7c7530a12080467f938e7fc72a59be21603d1a271d11f60f6370a850ce553a97284e4a0740f").unwrap(), + Commitment::from_hex("3682a4cfc556c0b5102065bfbb21fcd3b62b26ebad28d90f9d6720e1cea31d2c").unwrap(), + BulletRangeProof::from_hex("01d202a095c27dc9e19ffd8456ac85dc45c9ff7505d84a37af6c8a3b572b97531f98e40484332d968e000451c3e8b14e7c9704a15905564e49e10ac909df52dd2d8467a19c9f51f74ff16c98dfd97e5f22146a7d8a4eef280050c9729a0d2b1b0cce1cfe8050440b01362bd486485f7155f04ff1e885e5b5e594dbe91add2564015c0ba23e9faea20df2396d1cdd7a1c784f40945b0205a69e814520c7202a335e76516965be5a78d126b510b8b73da2adb82b350c2a32d86780b74a00da873d2748991cb0a13206620f5a12aa849e0f3ab030ed6e769d9ba725cacd464955e54f360ddddf79a86da74ace814b5c4cccd3c76b985733d91803024f38a62ab43244f2ae4ea7631a7779c879d27815094e200fb0b36769b855d0934cd061a0ca05162aecccc847b80c4d305e54f855d4a7bec5d4f8f3618fcabef44e9aecf2a3b37bc0ead352597ee7a38cc401c4471c53e1889e1affe6f9ae964cce719604296e0310f61f241b42260720bec94bb6e514dd9a94cdc2e8d8dd4377e9c805d324b4265413aa79caf926a27b7182ca8222a9e80024a878eee84b34c4c2422f3aabb44072c8f1a7a1fad46fb4d1c474c4d9dabfebfc73dd0c3c51b5942d6d78223faa0dcae2007c9eeee04d7cca47ee230980e8a32637f39ce3d4526f3e49a7907ef63b9cf3fac169e5db0ad9b1c1b898814aea6568457922271e1428e9bfa273a94006d77f15bf981dcb2a0c70bdc63f86241159b97d463f7fd0d3ef581c727fe1210bc3d0509596dbede6d84f6e199498c97bc3e497553bac19673c5055384e3c3f02").unwrap(), // A default script can never be spent, intentionally TariScript::default(), // The Sender offset public key is not checked for coinbase outputs @@ -273,7 +273,7 @@ fn get_dibbler_genesis_block_raw() -> Block { KernelFeatures::COINBASE_KERNEL, MicroTari(0), 0, - Commitment::from_hex("0cff7e89fa0468aa68f777cf600ae6f9e46fdc6e4e33540077e7303e8929295c").unwrap(), + Commitment::from_hex("b050c0aa325f70666b83f1636423f724f3886bbaff11179a76be0df47829bf73").unwrap(), excess_sig, None, ); @@ -289,10 +289,10 @@ fn get_dibbler_genesis_block_raw() -> Block { height: 0, prev_hash: vec![0; BLOCK_HASH_LENGTH], timestamp: timestamp.into(), - output_mr: from_hex("cfe91b83e0d8b5190671e9db7cf3129cb163b2812b862776bcd7f42aee58eecf").unwrap(), - witness_mr: from_hex("71a1fdcf3da037f786e3874b0f49a7720b35b978cbc78d284f20d140317f89bb").unwrap(), + output_mr: from_hex("bb866666548a998c82d14746b730b929f2ee0074d8d1652261dd6e751f9e821c").unwrap(), + witness_mr: from_hex("1a0f889a52e089e909bd2a39a9ac185b0645d0e0125e4a38eec76314ca455ad6").unwrap(), output_mmr_size: 1, - kernel_mr: from_hex("55bb9a3369ede6c4e04bab54dd4f2345531e559fc6d72d9f62adad1d49898c15").unwrap(), + kernel_mr: from_hex("7be2dfbaf3a4892bed506ed606edc6dd4f09eba0f75d1260c82864e50c2d888c").unwrap(), kernel_mmr_size: 1, input_mr: vec![0; BLOCK_HASH_LENGTH], total_kernel_offset: PrivateKey::from_hex( @@ -349,9 +349,12 @@ mod test { block.header().output_mmr_size ); - for kernel in block.block().body.kernels() { - kernel.verify_signature().unwrap(); - } + // todo replace this back in with new esmarelda gen block + // for kernel in block.block().body.kernels() { + // kernel.verify_signature().unwrap(); + // } + // we only validate the coinbase, aggregated faucet kernel signature is invalid. + block.block().body.kernels()[0].verify_signature().unwrap(); assert!(block .block() diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs index fdd1edee84..a3b12a56b5 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs @@ -682,7 +682,7 @@ mod test { fee::Fee, tari_amount::MicroTari, test_helpers::{TestParams, UtxoTestParams}, - transaction_components::{KernelFeatures, OutputFeatures}, + transaction_components::OutputFeatures, weight::TransactionWeight, CryptoFactories, SenderTransactionProtocol, @@ -789,9 +789,7 @@ mod test { let factories = CryptoFactories::default(); let mut stx_protocol = stx_builder.build::(&factories, None, u64::MAX).unwrap(); - stx_protocol - .finalize(KernelFeatures::empty(), &factories, None, u64::MAX) - .unwrap(); + stx_protocol.finalize(&factories, None, u64::MAX).unwrap(); let tx3 = stx_protocol.get_transaction().unwrap().clone(); diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index 6363831374..0b55b78e6d 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -46,11 +46,12 @@ use crate::{ OutputFeatures, Transaction, TransactionBuilder, + TransactionKernel, TransactionOutput, TransactionOutputVersion, UnblindedOutput, }, - transaction_protocol::{build_challenge, RewindData, TransactionMetadata}, + transaction_protocol::{RewindData, TransactionMetadata}, }, }; @@ -198,8 +199,9 @@ impl CoinbaseBuilder { let output_features = OutputFeatures::create_coinbase(height + constants.coinbase_lock_height()); let excess = self.factories.commitment.commit_value(&spending_key, 0); let kernel_features = KernelFeatures::create_coinbase(); - let metadata = TransactionMetadata::default(); - let challenge = build_challenge(&public_nonce, &metadata); + let metadata = TransactionMetadata::new_with_features(0.into(), 0, kernel_features); + let challenge = + TransactionKernel::build_kernel_challenge_from_tx_meta(&public_nonce, excess.as_public_key(), &metadata); let sig = Signature::sign(spending_key.clone(), nonce, &challenge) .map_err(|_| CoinbaseBuildError::BuildError("Challenge could not be represented as a scalar".into()))?; @@ -280,7 +282,7 @@ impl CoinbaseBuilder { mod test { use rand::rngs::OsRng; use tari_common::configuration::Network; - use tari_common_types::types::{BlindingFactor, PrivateKey}; + use tari_common_types::types::{BlindingFactor, PrivateKey, Signature}; use tari_crypto::{commitment::HomomorphicCommitmentFactory, keys::SecretKey as SecretKeyTrait}; use crate::{ @@ -290,7 +292,14 @@ mod test { crypto_factories::CryptoFactories, tari_amount::uT, test_helpers::TestParams, - transaction_components::{EncryptedValue, KernelFeatures, OutputFeatures, OutputType, TransactionError}, + transaction_components::{ + EncryptedValue, + KernelFeatures, + OutputFeatures, + OutputType, + TransactionError, + TransactionKernel, + }, transaction_protocol::RewindData, CoinbaseBuilder, }, @@ -491,6 +500,7 @@ mod test { ) .is_ok()); } + use tari_crypto::keys::PublicKey; #[test] #[allow(clippy::identity_op)] @@ -514,7 +524,7 @@ mod test { .with_fees(1 * uT) .with_nonce(p.nonce.clone()) .with_spend_key(p.spend_key); - let (tx2, _) = builder + let (tx2, output) = builder .build(rules.consensus_constants(0), rules.emission_schedule()) .unwrap(); let mut tx_kernel_test = tx.clone(); @@ -523,6 +533,18 @@ mod test { let coinbase2 = tx2.body.outputs()[0].clone(); let mut coinbase_kernel2 = tx2.body.kernels()[0].clone(); coinbase_kernel2.features = KernelFeatures::empty(); + // fix signature + let p2 = TestParams::new(); + let challenge = TransactionKernel::build_kernel_challenge( + &p2.public_nonce, + &PublicKey::from_secret_key(&output.spending_key), + coinbase_kernel2.fee, + coinbase_kernel2.lock_height, + &KernelFeatures::empty(), + &None, + ); + coinbase_kernel2.excess_sig = Signature::sign(output.spending_key, p2.nonce, &challenge).unwrap(); + tx.body.add_output(coinbase2); tx.body.add_kernel(coinbase_kernel2); diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index 5a5c3fe699..2b06fe094b 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -53,7 +53,7 @@ use crate::{ TransactionOutput, UnblindedOutput, }, - transaction_protocol::{build_challenge, RewindData, TransactionMetadata, TransactionProtocolError}, + transaction_protocol::{RewindData, TransactionMetadata, TransactionProtocolError}, weight::TransactionWeight, SenderTransactionProtocol, }, @@ -262,16 +262,20 @@ pub fn generate_keys() -> TestKeySet { } /// Generate a random transaction signature, returning the public key (excess) and the signature. -pub fn create_random_signature(fee: MicroTari, lock_height: u64) -> (PublicKey, Signature) { +pub fn create_random_signature(fee: MicroTari, lock_height: u64, features: KernelFeatures) -> (PublicKey, Signature) { let (k, p) = PublicKey::random_keypair(&mut OsRng); - (p, create_signature(k, fee, lock_height)) + (p, create_signature(k, fee, lock_height, features)) } /// Generate a random transaction signature, returning the public key (excess) and the signature. -pub fn create_signature(k: PrivateKey, fee: MicroTari, lock_height: u64) -> Signature { +pub fn create_signature(k: PrivateKey, fee: MicroTari, lock_height: u64, features: KernelFeatures) -> Signature { let r = PrivateKey::random(&mut OsRng); - let tx_meta = TransactionMetadata { fee, lock_height }; - let e = build_challenge(&PublicKey::from_secret_key(&r), &tx_meta); + let tx_meta = TransactionMetadata::new_with_features(fee, lock_height, features); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta( + &PublicKey::from_secret_key(&r), + &PublicKey::from_secret_key(&k), + &tx_meta, + ); Signature::sign(k, r, &e).unwrap() } @@ -280,12 +284,13 @@ pub fn create_random_signature_from_s_key( s_key: PrivateKey, fee: MicroTari, lock_height: u64, + features: KernelFeatures, ) -> (PublicKey, Signature) { let _rng = rand::thread_rng(); let r = PrivateKey::random(&mut OsRng); let p = PK::from_secret_key(&s_key); - let tx_meta = TransactionMetadata { fee, lock_height }; - let e = build_challenge(&PublicKey::from_secret_key(&r), &tx_meta); + let tx_meta = TransactionMetadata::new_with_features(fee, lock_height, features); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta(&PublicKey::from_secret_key(&r), &p, &tx_meta); (p, Signature::sign(s_key, r, &e).unwrap()) } @@ -594,6 +599,7 @@ pub fn create_sender_transaction_protocol_with( .with_fee_per_gram(fee_per_gram) .with_offset(test_params.offset.clone()) .with_private_nonce(test_params.nonce.clone()) + .with_kernel_features(KernelFeatures::empty()) .with_change_secret(test_params.change_spend_key); inputs.into_iter().for_each(|input| { @@ -608,7 +614,7 @@ pub fn create_sender_transaction_protocol_with( }); let mut stx_protocol = stx_builder.build::(&factories, None, u64::MAX).unwrap(); - stx_protocol.finalize(KernelFeatures::empty(), &factories, None, u64::MAX)?; + stx_protocol.finalize(&factories, None, u64::MAX)?; Ok(stx_protocol) } @@ -620,7 +626,7 @@ pub fn create_sender_transaction_protocol_with( pub fn spend_utxos(schema: TransactionSchema) -> (Transaction, Vec) { let (mut stx_protocol, outputs) = create_stx_protocol(schema); stx_protocol - .finalize(KernelFeatures::empty(), &CryptoFactories::default(), None, u64::MAX) + .finalize(&CryptoFactories::default(), None, u64::MAX) .unwrap(); let txn = stx_protocol.get_transaction().unwrap().clone(); (txn, outputs) @@ -746,7 +752,7 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto pub fn create_coinbase_kernel(excess: &PrivateKey) -> TransactionKernel { let public_excess = PublicKey::from_secret_key(excess); - let s = create_signature(excess.clone(), 0.into(), 0); + let s = create_signature(excess.clone(), 0.into(), 0, KernelFeatures::COINBASE_KERNEL); KernelBuilder::new() .with_features(KernelFeatures::COINBASE_KERNEL) .with_excess(&Commitment::from_public_key(&public_excess)) @@ -756,11 +762,12 @@ pub fn create_coinbase_kernel(excess: &PrivateKey) -> TransactionKernel { } /// Create a transaction kernel with the given fee, using random keys to generate the signature -pub fn create_test_kernel(fee: MicroTari, lock_height: u64) -> TransactionKernel { - let (excess, s) = create_random_signature(fee, lock_height); +pub fn create_test_kernel(fee: MicroTari, lock_height: u64, features: KernelFeatures) -> TransactionKernel { + let (excess, s) = create_random_signature(fee, lock_height, features); KernelBuilder::new() .with_fee(fee) .with_lock_height(lock_height) + .with_features(features) .with_excess(&Commitment::from_public_key(&excess)) .with_signature(&s) .build() diff --git a/base_layer/core/src/transactions/transaction_components/kernel_builder.rs b/base_layer/core/src/transactions/transaction_components/kernel_builder.rs index 6c364158a9..10cfdd9aec 100644 --- a/base_layer/core/src/transactions/transaction_components/kernel_builder.rs +++ b/base_layer/core/src/transactions/transaction_components/kernel_builder.rs @@ -60,8 +60,8 @@ impl KernelBuilder { } /// Build a transaction kernel with the provided burn commitment - pub fn with_burn_commitment(mut self, burn_commitment: Commitment) -> KernelBuilder { - self.burn_commitment = Some(burn_commitment); + pub fn with_burn_commitment(mut self, burn_commitment: Option) -> KernelBuilder { + self.burn_commitment = burn_commitment; self } diff --git a/base_layer/core/src/transactions/transaction_components/kernel_features.rs b/base_layer/core/src/transactions/transaction_components/kernel_features.rs index 330d29bf90..f85efad411 100644 --- a/base_layer/core/src/transactions/transaction_components/kernel_features.rs +++ b/base_layer/core/src/transactions/transaction_components/kernel_features.rs @@ -58,6 +58,12 @@ impl KernelFeatures { } } +impl Default for KernelFeatures { + fn default() -> Self { + KernelFeatures::empty() + } +} + impl ConsensusEncoding for KernelFeatures { fn consensus_encode(&self, writer: &mut W) -> Result<(), Error> { writer.write_all(&[self.bits][..])?; diff --git a/base_layer/core/src/transactions/transaction_components/test.rs b/base_layer/core/src/transactions/transaction_components/test.rs index f44430c6dc..47bdfa28df 100644 --- a/base_layer/core/src/transactions/transaction_components/test.rs +++ b/base_layer/core/src/transactions/transaction_components/test.rs @@ -281,7 +281,7 @@ fn check_timelocks() { MicroTari::zero(), ); - let mut kernel = test_helpers::create_test_kernel(0.into(), 0); + let mut kernel = test_helpers::create_test_kernel(0.into(), 0, KernelFeatures::empty()); let mut tx = Transaction::new(Vec::new(), Vec::new(), Vec::new(), 0.into(), 0.into()); // lets add time locks diff --git a/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs b/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs index 294c5972e9..7fad6e2603 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_kernel.rs @@ -31,16 +31,17 @@ use std::{ }; use serde::{Deserialize, Serialize}; -use tari_common_types::types::{Commitment, Signature}; -use tari_utilities::{hex::Hex, message_format::MessageFormat, Hashable}; +use tari_common_types::types::{Commitment, PublicKey, Signature}; +use tari_utilities::{hex::Hex, message_format::MessageFormat, ByteArray, Hashable}; use super::TransactionKernelVersion; use crate::{ - consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusHasher}, + consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusHasher, DomainSeparatedConsensusHasher}, transactions::{ tari_amount::MicroTari, transaction_components::{KernelFeatures, TransactionError}, - transaction_protocol::{build_challenge, TransactionMetadata}, + transaction_protocol::TransactionMetadata, + TransactionHashDomain, }, }; @@ -121,11 +122,14 @@ impl TransactionKernel { pub fn verify_signature(&self) -> Result<(), TransactionError> { let excess = self.excess.as_public_key(); let r = self.excess_sig.get_public_nonce(); - let m = TransactionMetadata { - lock_height: self.lock_height, - fee: self.fee, - }; - let c = build_challenge(r, &m); + let c = TransactionKernel::build_kernel_challenge( + r, + excess, + self.fee, + self.lock_height, + &self.features, + &self.burn_commitment, + ); if self.excess_sig.verify_challenge(excess, &c) { Ok(()) } else { @@ -142,6 +146,48 @@ impl TransactionKernel { None => Err(TransactionError::InvalidKernel("Burn commitment not found".to_string())), } } + + /// This is a helper fuction for build kernel challange that does not take in the individual fields, + /// but rather takes in the TransactionMetadata object. + pub fn build_kernel_challenge_from_tx_meta( + sum_public_nonces: &PublicKey, + total_excess: &PublicKey, + tx_meta: &TransactionMetadata, + ) -> [u8; 32] { + TransactionKernel::build_kernel_challenge( + sum_public_nonces, + total_excess, + tx_meta.fee, + tx_meta.lock_height, + &tx_meta.kernel_features, + &tx_meta.burn_commitment, + ) + } + + /// Helper function to creates the kernel excess signature challenge. + /// The challenge is defined as the hash of the following data: + /// Public nonce + /// Fee + /// Lock height + /// Features of the kernel + /// Burn commitment if present + pub fn build_kernel_challenge( + sum_public_nonces: &PublicKey, + total_excess: &PublicKey, + fee: MicroTari, + lock_height: u64, + features: &KernelFeatures, + burn_commitment: &Option, + ) -> [u8; 32] { + DomainSeparatedConsensusHasher::::new("kernel_signature") + .chain(sum_public_nonces) + .chain(total_excess) + .chain(&fee) + .chain(&lock_height) + .chain(features) + .chain(burn_commitment) + .finalize() + } } impl Hashable for TransactionKernel { diff --git a/base_layer/core/src/transactions/transaction_protocol/mod.rs b/base_layer/core/src/transactions/transaction_protocol/mod.rs index 403ce5981e..1df53f532d 100644 --- a/base_layer/core/src/transactions/transaction_protocol/mod.rs +++ b/base_layer/core/src/transactions/transaction_protocol/mod.rs @@ -86,11 +86,9 @@ // #![allow(clippy::op_ref)] use derivative::Derivative; -use digest::Digest; use serde::{Deserialize, Serialize}; -use tari_common_types::types::{PrivateKey, PublicKey}; -use tari_comms::types::CommsChallenge; -use tari_crypto::{errors::RangeProofError, signatures::SchnorrSignatureError, tari_utilities::byte_array::ByteArray}; +use tari_common_types::types::PrivateKey; +use tari_crypto::{errors::RangeProofError, signatures::SchnorrSignatureError}; use thiserror::Error; use crate::transactions::{tari_amount::*, transaction_components::TransactionError}; @@ -100,6 +98,9 @@ pub mod recipient; pub mod sender; pub mod single_receiver; pub mod transaction_initializer; +use tari_common_types::types::Commitment; + +use crate::transactions::transaction_components::KernelFeatures; #[derive(Clone, Debug, PartialEq, Error, Deserialize, Serialize)] pub enum TransactionProtocolError { @@ -135,13 +136,37 @@ pub enum TransactionProtocolError { EncryptionError, } -/// Transaction metadata, including the fee and lock height +/// Transaction metadata, this includes all the fields that needs to be signed on the kernel #[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize, Serialize)] pub struct TransactionMetadata { /// The absolute fee for the transaction pub fee: MicroTari, /// The earliest block this transaction can be mined pub lock_height: u64, + /// The kernel features + pub kernel_features: KernelFeatures, + /// optional burn commitment if present + pub burn_commitment: Option, +} + +impl TransactionMetadata { + pub fn new(fee: MicroTari, lock_height: u64) -> Self { + Self { + fee, + lock_height, + kernel_features: KernelFeatures::default(), + burn_commitment: None, + } + } + + pub fn new_with_features(fee: MicroTari, lock_height: u64, kernel_features: KernelFeatures) -> Self { + Self { + fee, + lock_height, + kernel_features, + burn_commitment: None, + } + } } #[derive(Derivative, Clone)] @@ -151,13 +176,3 @@ pub struct RewindData { pub rewind_blinding_key: PrivateKey, pub encryption_key: PrivateKey, } - -/// Convenience function that calculates the challenge for the Schnorr signatures -pub fn build_challenge(sum_public_nonces: &PublicKey, metadata: &TransactionMetadata) -> [u8; 32] { - CommsChallenge::new() - .chain(sum_public_nonces.as_bytes()) - .chain(&u64::from(metadata.fee).to_le_bytes()) - .chain(&metadata.lock_height.to_le_bytes()) - .finalize() - .into() -} diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto index 3e279bb490..1a20eb8eb0 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto +++ b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto @@ -5,6 +5,7 @@ syntax = "proto3"; import "types.proto"; import "transaction.proto"; +import "transaction_metadata.proto"; package tari.transaction_protocol; @@ -14,4 +15,6 @@ message RecipientSignedMessage { tari.types.TransactionOutput output = 2; bytes public_spend_key = 3; tari.types.Signature partial_signature = 4; + // The transaction metadata + TransactionMetadata metadata = 5; } diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs index 30fa6ce496..2eaa7e0897 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs @@ -43,12 +43,17 @@ impl TryFrom for RecipientSignedMessage { .partial_signature .map(TryInto::try_into) .ok_or_else(|| "Transaction partial signature not provided".to_string())??; + let metadata = message + .metadata + .map(TryInto::try_into) + .ok_or_else(|| "Transaction metadata not provided".to_string())??; Ok(Self { tx_id: message.tx_id.into(), output, public_spend_key, partial_signature, + tx_metadata: metadata, }) } } @@ -60,6 +65,7 @@ impl From for proto::RecipientSignedMessage { output: Some(message.output.into()), public_spend_key: message.public_spend_key.to_vec(), partial_signature: Some(message.partial_signature.into()), + metadata: Some(message.tx_metadata.into()), } } } diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto index 1231bc37ce..f7aef16fa7 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto @@ -12,4 +12,9 @@ message TransactionMetadata { uint64 fee = 1; // The earliest block this transaction can be mined uint64 lock_height = 2; + // features of the kernel for this transaction + uint32 kernel_features = 3; + /// optional burn commitment if present + tari.types.Commitment burned_commitment = 4; } + diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs index 7eb0bbecc7..5dee4d5fa5 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs @@ -20,25 +20,47 @@ // 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::convert::TryFrom; + +use tari_common_types::types::Commitment; +use tari_utilities::ByteArray; + use super::protocol as proto; -use crate::transactions::transaction_protocol::TransactionMetadata; +use crate::transactions::transaction_protocol::{KernelFeatures, TransactionMetadata}; -impl From for TransactionMetadata { - fn from(metadata: proto::TransactionMetadata) -> Self { - Self { +impl TryFrom for TransactionMetadata { + type Error = String; + + fn try_from(metadata: proto::TransactionMetadata) -> Result { + let kernel_features = + u8::try_from(metadata.kernel_features).map_err(|_| "Kernel features must be a single byte")?; + let commitment = match metadata.burned_commitment { + Some(burned_commitment) => { + Some(Commitment::from_bytes(&burned_commitment.data).map_err(|e| e.to_string())?) + }, + None => None, + }; + Ok(Self { fee: metadata.fee.into(), lock_height: metadata.lock_height, - } + kernel_features: KernelFeatures::from_bits(kernel_features) + .ok_or_else(|| "Invalid or unrecognised kernel feature flag".to_string())?, + burn_commitment: commitment, + }) } } impl From for proto::TransactionMetadata { fn from(metadata: TransactionMetadata) -> Self { + let commitment = metadata.burn_commitment.map(|commitment| commitment.into()); Self { // The absolute fee for the transaction fee: metadata.fee.into(), // The earliest block this transaction can be mined lock_height: metadata.lock_height, + kernel_features: u32::from(metadata.kernel_features.bits()), + // optional burn commitment if present + burned_commitment: commitment, } } } diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs index c10b9e56ce..b3c4e91a34 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs @@ -98,8 +98,8 @@ impl TryFrom for SingleRoundSenderData { PublicKey::from_bytes(&data.sender_offset_public_key).map_err(|err| err.to_string())?; let metadata = data .metadata - .map(Into::into) - .ok_or_else(|| "Transaction metadata not provided".to_string())?; + .map(TryInto::try_into) + .ok_or_else(|| "Transaction metadata not provided".to_string())??; let message = data.message; let public_commitment_nonce = PublicKey::from_bytes(&data.public_commitment_nonce).map_err(|err| err.to_string())?; diff --git a/base_layer/core/src/transactions/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs index b476763bc6..d786b569ca 100644 --- a/base_layer/core/src/transactions/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -35,6 +35,7 @@ use crate::transactions::{ sender::{SingleRoundSenderData as SD, TransactionSenderMessage}, single_receiver::SingleReceiverTransactionProtocol, RewindData, + TransactionMetadata, TransactionProtocolError, }, }; @@ -88,6 +89,7 @@ pub struct RecipientSignedMessage { pub output: TransactionOutput, pub public_spend_key: PublicKey, pub partial_signature: Signature, + pub tx_metadata: TransactionMetadata, } /// The generalised transaction recipient protocol. A different state transition network is followed depending on @@ -215,9 +217,8 @@ mod test { crypto_factories::CryptoFactories, tari_amount::*, test_helpers::TestParams, - transaction_components::{EncryptedValue, OutputFeatures}, + transaction_components::{EncryptedValue, OutputFeatures, TransactionKernel}, transaction_protocol::{ - build_challenge, sender::{SingleRoundSenderData, TransactionSenderMessage}, RewindData, TransactionMetadata, @@ -230,10 +231,7 @@ mod test { fn single_round_recipient() { let factories = CryptoFactories::default(); let p = TestParams::new(); - let m = TransactionMetadata { - fee: MicroTari(125), - lock_height: 0, - }; + let m = TransactionMetadata::new(MicroTari(125), 0); let script = TariScript::default(); let amount = MicroTari(500); @@ -264,7 +262,8 @@ mod test { .open_value(&p.spend_key, 500, &data.output.commitment)); data.output.verify_range_proof(&factories.range_proof).unwrap(); let r_sum = &msg.public_nonce + &p.public_nonce; - let e = build_challenge(&r_sum, &m); + let excess = &msg.public_excess + &PublicKey::from_secret_key(&p.spend_key); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta(&r_sum, &excess, &m); let s = Signature::sign(p.spend_key.clone(), p.nonce, &e).unwrap(); assert_eq!(data.partial_signature, s); } @@ -280,10 +279,7 @@ mod test { encryption_key: PrivateKey::random(&mut OsRng), }; let amount = MicroTari(500); - let m = TransactionMetadata { - fee: MicroTari(125), - lock_height: 0, - }; + let m = TransactionMetadata::new(MicroTari(125), 0); let script = TariScript::default(); let features = OutputFeatures::default(); diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 9c46f2789e..60142f0b10 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -54,18 +54,17 @@ use crate::{ tari_amount::*, transaction_components::{ KernelBuilder, - KernelFeatures, OutputFeatures, Transaction, TransactionBuilder, TransactionInput, + TransactionKernel, TransactionOutput, UnblindedOutput, MAX_TRANSACTION_INPUTS, MAX_TRANSACTION_OUTPUTS, }, transaction_protocol::{ - build_challenge, recipient::{RecipientInfo, RecipientSignedMessage}, transaction_initializer::SenderTransactionInitializer, TransactionMetadata, @@ -478,6 +477,7 @@ impl SenderTransactionProtocol { info.public_excess = &info.public_excess + &rec.public_spend_key; info.public_nonce_sum = &info.public_nonce_sum + rec.partial_signature.get_public_nonce(); info.signatures.push(rec.partial_signature); + info.metadata = rec.tx_metadata; self.state = SenderState::Finalizing(info.clone()); Ok(()) }, @@ -519,11 +519,7 @@ impl SenderTransactionProtocol { } /// Attempts to build the final transaction. - fn build_transaction( - info: &RawTransactionInfo, - features: KernelFeatures, - factories: &CryptoFactories, - ) -> Result { + fn build_transaction(info: &RawTransactionInfo, factories: &CryptoFactories) -> Result { let mut tx_builder = TransactionBuilder::new(); for i in &info.inputs { tx_builder.add_input(i.clone()); @@ -537,22 +533,12 @@ impl SenderTransactionProtocol { let mut s_agg = info.signatures[0].clone(); info.signatures.iter().skip(1).for_each(|s| s_agg = &s_agg + s); let excess = PedersenCommitment::from_public_key(&info.public_excess); - let mut kernel_builder = KernelBuilder::new(); - if features.is_burned() { - let mut commitment = None; - for o in &info.outputs { - if o.is_burned() { - commitment = Some(o.commitment.clone()); - } - } - kernel_builder = kernel_builder.with_burn_commitment( - commitment.ok_or_else(|| TPE::IncompleteStateError("No burned output found".to_string()))?, - ); - } - let kernel = kernel_builder + + let kernel = KernelBuilder::new() .with_fee(info.metadata.fee) - .with_features(features) + .with_features(info.metadata.kernel_features) .with_lock_height(info.metadata.lock_height) + .with_burn_commitment(info.metadata.burn_commitment.clone()) .with_excess(&excess) .with_signature(&s_agg) .build()?; @@ -596,7 +582,13 @@ impl SenderTransactionProtocol { fn sign(&mut self) -> Result<(), TPE> { match &mut self.state { SenderState::Finalizing(info) => { - let e = build_challenge(&info.public_nonce_sum, &info.metadata); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta( + &info.public_nonce_sum, + &info.public_excess, + &info.metadata, + ); + // let e = build_challenge(&info.public_nonce_sum, &info.metadata); + let k = info.offset_blinding_factor.clone(); let r = info.private_nonce.clone(); let s = Signature::sign(k, r, &e).map_err(TPE::SigningError)?; @@ -617,7 +609,6 @@ impl SenderTransactionProtocol { /// returns `Ok(false)` in this instance. pub fn finalize( &mut self, - features: KernelFeatures, factories: &CryptoFactories, prev_header: Option, height: u64, @@ -635,9 +626,7 @@ impl SenderTransactionProtocol { // Validate the inputs we have, and then construct the final transaction match &self.state { SenderState::Finalizing(info) => { - let result = self - .validate() - .and_then(|_| Self::build_transaction(info, features, factories)); + let result = self.validate().and_then(|_| Self::build_transaction(info, factories)); match result { Ok(mut transaction) => { transaction.body.sort(); @@ -823,13 +812,7 @@ mod test { crypto_factories::CryptoFactories, tari_amount::*, test_helpers::{create_test_input, create_unblinded_output, TestParams}, - transaction_components::{ - EncryptedValue, - KernelFeatures, - OutputFeatures, - TransactionOutput, - TransactionOutputVersion, - }, + transaction_components::{EncryptedValue, OutputFeatures, TransactionOutput, TransactionOutputVersion}, transaction_protocol::{ sender::{SenderTransactionProtocol, TransactionSenderMessage}, single_receiver::SingleReceiverTransactionProtocol, @@ -1006,7 +989,7 @@ mod test { let mut sender = builder.build::(&factories, None, u64::MAX).unwrap(); assert!(!sender.is_failed()); assert!(sender.is_finalizing()); - match sender.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) { + match sender.finalize(&factories, None, u64::MAX) { Ok(_) => (), Err(e) => panic!("{:?}", e), } @@ -1054,7 +1037,7 @@ mod test { .unwrap(); // Transaction should be complete assert!(alice.is_finalizing()); - match alice.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) { + match alice.finalize(&factories, None, u64::MAX) { Ok(_) => (), Err(e) => panic!("{:?}", e), }; @@ -1137,7 +1120,7 @@ mod test { .unwrap(); // Transaction should be complete assert!(alice.is_finalizing()); - match alice.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) { + match alice.finalize(&factories, None, u64::MAX) { Ok(_) => (), Err(e) => panic!("{:?}", e), }; @@ -1338,7 +1321,7 @@ mod test { .unwrap(); // Transaction should be complete assert!(alice.is_finalizing()); - match alice.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) { + match alice.finalize(&factories, None, u64::MAX) { Ok(_) => (), Err(e) => panic!("{:?}", e), }; diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 128e574828..36e7d62cfc 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -32,9 +32,8 @@ use tari_crypto::{ use crate::transactions::{ crypto_factories::CryptoFactories, - transaction_components::{EncryptedValue, TransactionOutput, TransactionOutputVersion}, + transaction_components::{EncryptedValue, TransactionKernel, TransactionOutput, TransactionOutputVersion}, transaction_protocol::{ - build_challenge, recipient::RecipientSignedMessage as RD, sender::SingleRoundSenderData as SD, RewindData, @@ -62,14 +61,26 @@ impl SingleReceiverTransactionProtocol { let output = SingleReceiverTransactionProtocol::build_output(sender_info, &spending_key, factories, rewind_data)?; let public_nonce = PublicKey::from_secret_key(&nonce); + let tx_meta = if output.is_burned() { + let mut meta = sender_info.metadata.clone(); + meta.burn_commitment = Some(output.commitment().clone()); + meta + } else { + sender_info.metadata.clone() + }; let public_spending_key = PublicKey::from_secret_key(&spending_key); - let e = build_challenge(&(&sender_info.public_nonce + &public_nonce), &sender_info.metadata); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta( + &(&sender_info.public_nonce + &public_nonce), + &(&sender_info.public_excess + &public_spending_key), + &tx_meta, + ); let signature = Signature::sign(spending_key, nonce, &e).map_err(TPE::SigningError)?; let data = RD { tx_id: sender_info.tx_id, output, public_spend_key: public_spending_key, partial_signature: signature, + tx_metadata: tx_meta, }; Ok(data) } @@ -160,16 +171,14 @@ mod test { use crate::transactions::{ crypto_factories::CryptoFactories, tari_amount::*, - transaction_components::{OutputFeatures, OutputType}, + transaction_components::{OutputFeatures, OutputType, TransactionKernel}, transaction_protocol::{ - build_challenge, sender::SingleRoundSenderData, single_receiver::SingleReceiverTransactionProtocol, TransactionMetadata, TransactionProtocolError, }, }; - fn generate_output_parms() -> (PrivateKey, PrivateKey, OutputFeatures) { let r = PrivateKey::random(&mut OsRng); let k = PrivateKey::random(&mut OsRng); @@ -198,10 +207,7 @@ mod test { let (r, k, of) = generate_output_parms(); let pubkey = PublicKey::from_secret_key(&k); let pubnonce = PublicKey::from_secret_key(&r); - let m = TransactionMetadata { - fee: MicroTari(100), - lock_height: 0, - }; + let m = TransactionMetadata::new(MicroTari(100), 0); let script_offset_secret_key = PrivateKey::random(&mut OsRng); let sender_offset_public_key = PublicKey::from_secret_key(&script_offset_secret_key); let private_commitment_nonce = PrivateKey::random(&mut OsRng); @@ -210,7 +216,7 @@ mod test { let info = SingleRoundSenderData { tx_id: 500u64.into(), amount: MicroTari(1500), - public_excess: pub_xs, + public_excess: pub_xs.clone(), public_nonce: pub_rs.clone(), metadata: m.clone(), message: "".to_string(), @@ -225,7 +231,8 @@ mod test { assert_eq!(prot.tx_id.as_u64(), 500, "tx_id is incorrect"); // Check the signature assert_eq!(prot.public_spend_key, pubkey, "Public key is incorrect"); - let e = build_challenge(&(&pub_rs + &pubnonce), &m); + let excess = &pub_xs + PublicKey::from_secret_key(&k); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta(&(&pub_rs + &pubnonce), &excess, &m); assert!( prot.partial_signature.verify_challenge(&pubkey, &e), "Partial signature is incorrect" diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index ea311baecb..c81ca5b380 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -30,7 +30,7 @@ use log::*; use rand::rngs::OsRng; use tari_common_types::{ transaction::TxId, - types::{BlindingFactor, CommitmentFactory, HashOutput, PrivateKey, PublicKey}, + types::{BlindingFactor, Commitment, CommitmentFactory, HashOutput, PrivateKey, PublicKey}, }; use tari_crypto::{ commitment::HomomorphicCommitmentFactory, @@ -59,6 +59,7 @@ use crate::{ transaction_protocol::{ recipient::RecipientInfo, sender::{calculate_tx_id, RawTransactionInfo, SenderState, SenderTransactionProtocol}, + KernelFeatures, RewindData, TransactionMetadata, }, @@ -104,6 +105,8 @@ pub struct SenderTransactionInitializer { recipient_minimum_value_promise: FixedSet, private_commitment_nonces: FixedSet, tx_id: Option, + kernel_features: KernelFeatures, + burn_commitment: Option, fee: Fee, } @@ -148,6 +151,8 @@ impl SenderTransactionInitializer { recipient_covenants: FixedSet::new(num_recipients), recipient_minimum_value_promise: FixedSet::new(num_recipients), private_commitment_nonces: FixedSet::new(num_recipients), + kernel_features: KernelFeatures::empty(), + burn_commitment: None, tx_id: None, } } @@ -285,6 +290,18 @@ impl SenderTransactionInitializer { self } + /// This will select the desired kernel features to be signed by the receiver + pub fn with_kernel_features(&mut self, features: KernelFeatures) -> &mut Self { + self.kernel_features = features; + self + } + + /// This will allow the receipient to sign the burn commitment + pub fn with_burn_commitment(&mut self, commitment: Option) -> &mut Self { + self.burn_commitment = commitment; + self + } + /// Enable or disable spending of an amount less than the fee pub fn with_prevent_fee_gt_amount(&mut self, prevent_fee_gt_amount: bool) -> &mut Self { self.prevent_fee_gt_amount = prevent_fee_gt_amount; @@ -661,6 +678,8 @@ impl SenderTransactionInitializer { metadata: TransactionMetadata { fee: total_fee, lock_height: self.lock_height.unwrap(), + kernel_features: self.kernel_features, + burn_commitment: self.burn_commitment.clone(), }, inputs: self.inputs, outputs, diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index b1eca6ea4d..1132fbaba5 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -922,7 +922,7 @@ mod test { #[test] fn it_checks_the_kernel_timelock() { - let mut kernel = test_helpers::create_test_kernel(0.into(), 0); + let mut kernel = test_helpers::create_test_kernel(0.into(), 0, KernelFeatures::empty()); kernel.lock_height = 2; assert!(matches!( check_kernel_lock_height(1, &[kernel.clone()]), @@ -1044,8 +1044,8 @@ mod test { #[test] fn check_burned_succeeds_for_valid_outputs() { - let mut kernel1 = test_helpers::create_test_kernel(0.into(), 0); - let mut kernel2 = test_helpers::create_test_kernel(0.into(), 0); + let mut kernel1 = test_helpers::create_test_kernel(0.into(), 0, KernelFeatures::create_burn()); + let mut kernel2 = test_helpers::create_test_kernel(0.into(), 0, KernelFeatures::create_burn()); let (output1, _, _) = test_helpers::create_utxo( 100.into(), @@ -1072,9 +1072,7 @@ mod test { 0.into(), ); - kernel1.features = KernelFeatures::create_burn(); kernel1.burn_commitment = Some(output1.commitment.clone()); - kernel2.features = KernelFeatures::create_burn(); kernel2.burn_commitment = Some(output2.commitment.clone()); let kernel3 = kernel1.clone(); diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 3d3670b392..e7d08dd0db 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -144,11 +144,10 @@ fn chain_balance_validation() { &Covenant::default(), MicroTari::zero(), ); - let (pk, sig) = create_random_signature_from_s_key(faucet_key, 0.into(), 0); + let (pk, sig) = create_random_signature_from_s_key(faucet_key, 0.into(), 0, KernelFeatures::empty()); let excess = Commitment::from_public_key(&pk); let kernel = TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None); - // let _faucet_hash = faucet_utxo.hash(); let mut gen_block = genesis.block().clone(); gen_block.body.add_output(faucet_utxo); gen_block.body.add_kernels(&mut vec![kernel]); @@ -194,7 +193,7 @@ fn chain_balance_validation() { MicroTari::zero(), ); // let _coinbase_hash = coinbase.hash(); - let (pk, sig) = create_random_signature_from_s_key(coinbase_key, 0.into(), 0); + let (pk, sig) = create_random_signature_from_s_key(coinbase_key, 0.into(), 0, KernelFeatures::create_coinbase()); let excess = Commitment::from_public_key(&pk); let kernel = KernelBuilder::new() .with_signature(&sig) @@ -246,7 +245,7 @@ fn chain_balance_validation() { &Covenant::default(), MicroTari::zero(), ); - let (pk, sig) = create_random_signature_from_s_key(key, 0.into(), 0); + let (pk, sig) = create_random_signature_from_s_key(key, 0.into(), 0, KernelFeatures::create_coinbase()); let excess = Commitment::from_public_key(&pk); let kernel = KernelBuilder::new() .with_signature(&sig) @@ -301,11 +300,10 @@ fn chain_balance_validation_burned() { &Covenant::default(), MicroTari::zero(), ); - let (pk, sig) = create_random_signature_from_s_key(faucet_key, 0.into(), 0); + let (pk, sig) = create_random_signature_from_s_key(faucet_key, 0.into(), 0, KernelFeatures::empty()); let excess = Commitment::from_public_key(&pk); let kernel = TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None); - // let _faucet_hash = faucet_utxo.hash(); let mut gen_block = genesis.block().clone(); gen_block.body.add_output(faucet_utxo); gen_block.body.add_kernels(&mut vec![kernel]); @@ -350,7 +348,7 @@ fn chain_balance_validation_burned() { &Covenant::default(), MicroTari::zero(), ); - let (pk, sig) = create_random_signature_from_s_key(coinbase_key, 0.into(), 0); + let (pk, sig) = create_random_signature_from_s_key(coinbase_key, 0.into(), 0, KernelFeatures::create_coinbase()); let excess = Commitment::from_public_key(&pk); let kernel = KernelBuilder::new() .with_signature(&sig) @@ -368,13 +366,13 @@ fn chain_balance_validation_burned() { MicroTari::zero(), ); - let (pk2, sig2) = create_random_signature_from_s_key(burned_key, 0.into(), 0); + let (pk2, sig2) = create_random_signature_from_s_key(burned_key, 0.into(), 0, KernelFeatures::create_burn()); let excess2 = Commitment::from_public_key(&pk2); let kernel2 = KernelBuilder::new() .with_signature(&sig2) .with_excess(&excess2) .with_features(KernelFeatures::create_burn()) - .with_burn_commitment(burned.commitment.clone()) + .with_burn_commitment(Some(burned.commitment.clone())) .build() .unwrap(); burned_sum = &burned_sum + kernel2.get_burn_commitment().unwrap(); diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs index 25a59bc8e6..6a962e6cbf 100644 --- a/base_layer/core/src/validation/transaction_validators.rs +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -31,7 +31,7 @@ use crate::{ CryptoFactories, }, validation::{ - helpers::{check_inputs_are_utxos, check_outputs}, + helpers::{check_inputs_are_utxos, check_outputs, check_total_burned}, MempoolTransactionValidation, ValidationError, }, @@ -291,6 +291,7 @@ impl MempoolTransactionValidation for TxInputAndMaturityVa verify_timelocks(tx, tip_height)?; verify_no_duplicated_inputs_outputs(tx)?; + check_total_burned(&tx.body)?; Ok(()) } } diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 92a0b1b93b..bf27ecafb0 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -72,7 +72,7 @@ pub fn create_coinbase( let p = TestParams::new(); let excess = Commitment::from_public_key(&PublicKey::from_secret_key(&p.spend_key)); - let sig = create_signature(p.spend_key.clone(), 0.into(), 0); + let sig = create_signature(p.spend_key.clone(), 0.into(), 0, KernelFeatures::create_coinbase()); let kernel = KernelBuilder::new() .with_signature(&sig) .with_excess(&excess) @@ -121,7 +121,7 @@ fn print_new_genesis_block_dibbler() { // #[ignore = "used to generate a new igor genesis block"] /// This is a helper function to generate and print out a block that can be used as the genesis block. /// 1. Run `cargo test --package tari_core --test mempool -- helpers::block_builders::print_new_genesis_block_igor -/// --exact --nocapture --ignored` +/// --exact --nocapture` /// 1. The block and range proof will be printed /// 1. Profit! fn print_new_genesis_block_igor() { @@ -141,7 +141,7 @@ fn print_new_genesis_block(network: Network) { &Covenant::default(), MicroTari::zero(), ); - let (pk, sig) = create_random_signature_from_s_key(key, 0.into(), 0); + let (pk, sig) = create_random_signature_from_s_key(key, 0.into(), 0, KernelFeatures::COINBASE_KERNEL); let excess = Commitment::from_public_key(&pk); let kernel = KernelBuilder::new() .with_signature(&sig) diff --git a/base_layer/core/tests/mempool.rs b/base_layer/core/tests/mempool.rs index d05f63d1bd..2c3b5d8017 100644 --- a/base_layer/core/tests/mempool.rs +++ b/base_layer/core/tests/mempool.rs @@ -55,8 +55,15 @@ use tari_core::{ TransactionSchema, UtxoTestParams, }, - transaction_components::{KernelBuilder, OutputFeatures, OutputType, Transaction, TransactionOutput}, - transaction_protocol::{build_challenge, TransactionMetadata}, + transaction_components::{ + KernelBuilder, + OutputFeatures, + OutputType, + Transaction, + TransactionKernel, + TransactionOutput, + }, + transaction_protocol::TransactionMetadata, CryptoFactories, }, tx, @@ -971,12 +978,12 @@ async fn consensus_validation_large_tx() { .collect::, _>>() .unwrap(); - let tx_meta = TransactionMetadata { fee, lock_height: 0 }; + let tx_meta = TransactionMetadata::new(fee, 0); let public_nonce = PublicKey::from_secret_key(&nonce); let offset_blinding_factor = &excess_blinding_factor - &offset; let excess = PublicKey::from_secret_key(&offset_blinding_factor); - let e = build_challenge(&public_nonce, &tx_meta); + let e = TransactionKernel::build_kernel_challenge_from_tx_meta(&public_nonce, &excess, &tx_meta); let k = offset_blinding_factor; let r = nonce; let s = Signature::sign(k, r, &e).unwrap(); diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index f0b9275771..5dc9c06593 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -371,8 +371,8 @@ mod tests { #[test] fn detect_change_in_consensus_encoding() { - const NONCE: u64 = 11896134726080601947; - const DIFFICULTY: Difficulty = Difficulty::from_u64(1289); + const NONCE: u64 = 6183284821715168573; + const DIFFICULTY: Difficulty = Difficulty::from_u64(1151); // Use this to generate new NONCE and DIFFICULTY // Use ONLY if you know encoding has changed // let (difficulty, nonce) = generate_nonce_with_min_difficulty(MIN_DIFFICULTY).unwrap(); diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index 4a1260ed15..eff4735e1d 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -38,7 +38,7 @@ use tari_core::{ UnblindedOutput, UnblindedOutputBuilder, }, - transaction_protocol::{sender::TransactionSenderMessage, RewindData}, + transaction_protocol::{sender::TransactionSenderMessage, RewindData, TransactionMetadata}, ReceiverTransactionProtocol, SenderTransactionProtocol, }, @@ -81,7 +81,7 @@ pub enum OutputManagerRequest { utxo_selection: UtxoSelectionCriteria, output_features: Box, fee_per_gram: MicroTari, - lock_height: Option, + tx_meta: TransactionMetadata, message: String, script: TariScript, covenant: Covenant, @@ -534,7 +534,7 @@ impl OutputManagerHandle { utxo_selection: UtxoSelectionCriteria, output_features: OutputFeatures, fee_per_gram: MicroTari, - lock_height: Option, + tx_meta: TransactionMetadata, message: String, script: TariScript, covenant: Covenant, @@ -548,7 +548,7 @@ impl OutputManagerHandle { utxo_selection, output_features: Box::new(output_features), fee_per_gram, - lock_height, + tx_meta, message, script, covenant, diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index a90346e596..97bdfd64a3 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -53,7 +53,7 @@ use tari_core::{ UnblindedOutput, UnblindedOutputBuilder, }, - transaction_protocol::{sender::TransactionSenderMessage, RewindData}, + transaction_protocol::{sender::TransactionSenderMessage, RewindData, TransactionMetadata}, CoinbaseBuilder, CryptoFactories, ReceiverTransactionProtocol, @@ -282,7 +282,7 @@ where utxo_selection, output_features, fee_per_gram, - lock_height, + tx_meta, message, script, covenant, @@ -293,7 +293,7 @@ where amount, utxo_selection, fee_per_gram, - lock_height, + tx_meta, message, *output_features, script, @@ -848,7 +848,7 @@ where amount: MicroTari, utxo_selection: UtxoSelectionCriteria, fee_per_gram: MicroTari, - lock_height: Option, + tx_meta: TransactionMetadata, message: String, recipient_output_features: OutputFeatures, recipient_script: TariScript, @@ -881,7 +881,6 @@ where let mut builder = SenderTransactionProtocol::builder(1, self.resources.consensus_constants.clone()); builder - .with_lock_height(lock_height.unwrap_or(0)) .with_fee_per_gram(fee_per_gram) .with_offset(offset.clone()) .with_private_nonce(nonce.clone()) @@ -897,6 +896,8 @@ where ) .with_message(message) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) + .with_lock_height(tx_meta.lock_height) + .with_kernel_features(tx_meta.kernel_features) .with_tx_id(tx_id); for uo in input_selection.iter() { @@ -1081,7 +1082,8 @@ where .with_fee_per_gram(fee_per_gram) .with_offset(offset.clone()) .with_private_nonce(nonce.clone()) - .with_prevent_fee_gt_amount(false); + .with_prevent_fee_gt_amount(false) + .with_kernel_features(KernelFeatures::empty()); for uo in input_selection.iter() { builder.with_input( @@ -1191,7 +1193,7 @@ where self.resources .db .encumber_outputs(tx_id, input_selection.into_selected(), db_outputs)?; - stp.finalize(KernelFeatures::empty(), &self.resources.factories, None, u64::MAX)?; + stp.finalize(&self.resources.factories, None, u64::MAX)?; Ok((tx_id, stp.take_transaction()?)) } @@ -1237,6 +1239,7 @@ where .with_message(message) .with_rewindable_outputs(self.resources.rewind_data.clone()) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) + .with_kernel_features(KernelFeatures::empty()) .with_tx_id(tx_id); for uo in input_selection.iter() { @@ -1340,12 +1343,7 @@ where self.confirm_encumberance(tx_id)?; let fee = stp.get_fee_amount()?; trace!(target: LOG_TARGET, "Finalize send-to-self transaction ({}).", tx_id); - stp.finalize( - KernelFeatures::empty(), - &factories, - None, - self.last_seen_tip_height.unwrap_or(u64::MAX), - )?; + stp.finalize(&factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX))?; let tx = stp.take_transaction()?; Ok((fee, tx)) @@ -1717,6 +1715,7 @@ where .with_fee_per_gram(fee_per_gram) .with_offset(PrivateKey::random(&mut OsRng)) .with_private_nonce(PrivateKey::random(&mut OsRng)) + .with_kernel_features(KernelFeatures::empty()) .with_rewindable_outputs(self.resources.rewind_data.clone()); // collecting inputs from source outputs @@ -1838,7 +1837,6 @@ where // finalizing transaction stp.finalize( - KernelFeatures::empty(), &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), @@ -1940,7 +1938,8 @@ where .with_fee_per_gram(fee_per_gram) .with_offset(PrivateKey::random(&mut OsRng)) .with_private_nonce(PrivateKey::random(&mut OsRng)) - .with_rewindable_outputs(self.resources.rewind_data.clone()); + .with_rewindable_outputs(self.resources.rewind_data.clone()) + .with_kernel_features(KernelFeatures::empty()); // collecting inputs from source outputs let inputs: Vec = src_outputs @@ -2089,7 +2088,6 @@ where // finalizing transaction stp.finalize( - KernelFeatures::empty(), &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), @@ -2152,7 +2150,8 @@ where .with_fee_per_gram(fee_per_gram) .with_offset(PrivateKey::random(&mut OsRng)) .with_private_nonce(PrivateKey::random(&mut OsRng)) - .with_rewindable_outputs(self.resources.rewind_data.clone()); + .with_rewindable_outputs(self.resources.rewind_data.clone()) + .with_kernel_features(KernelFeatures::empty()); // collecting inputs from source outputs let inputs: Vec = src_outputs @@ -2258,7 +2257,6 @@ where // finalizing transaction stp.finalize( - KernelFeatures::empty(), &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), @@ -2343,6 +2341,7 @@ where .with_offset(offset.clone()) .with_private_nonce(nonce.clone()) .with_message(message) + .with_kernel_features(KernelFeatures::empty()) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_input( rewound_output.as_transaction_input(&self.resources.factories.commitment)?, @@ -2390,12 +2389,7 @@ where self.confirm_encumberance(tx_id)?; let fee = stp.get_fee_amount()?; trace!(target: LOG_TARGET, "Finalize send-to-self transaction ({}).", tx_id); - stp.finalize( - KernelFeatures::empty(), - &factories, - None, - self.last_seen_tip_height.unwrap_or(u64::MAX), - )?; + stp.finalize(&factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX))?; let tx = stp.take_transaction()?; Ok((tx_id, fee, amount - fee, tx)) @@ -2434,6 +2428,7 @@ where .with_offset(offset.clone()) .with_private_nonce(nonce.clone()) .with_message(message) + .with_kernel_features(KernelFeatures::empty()) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_input( output.as_transaction_input(&self.resources.factories.commitment)?, @@ -2479,12 +2474,7 @@ where let fee = stp.get_fee_amount()?; - stp.finalize( - KernelFeatures::empty(), - &factories, - None, - self.last_seen_tip_height.unwrap_or(u64::MAX), - )?; + stp.finalize(&factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX))?; let tx = stp.take_transaction()?; diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs index c43e00e7b0..f70dd40d32 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs @@ -38,11 +38,12 @@ use tari_core::{ covenants::Covenant, transactions::{ tari_amount::MicroTari, - transaction_components::{KernelFeatures, OutputFeatures}, + transaction_components::OutputFeatures, transaction_protocol::{ proto::protocol as proto, recipient::RecipientSignedMessage, sender::SingleRoundSenderData, + TransactionMetadata, }, SenderTransactionProtocol, }, @@ -97,6 +98,7 @@ pub struct TransactionSendProtocol { cancellation_receiver: Option>, prev_header: Option, height: Option, + tx_meta: TransactionMetadata, sender_protocol: Option, } @@ -114,6 +116,7 @@ where amount: MicroTari, fee_per_gram: MicroTari, message: String, + tx_meta: TransactionMetadata, service_request_reply_channel: Option< oneshot::Sender>, >, @@ -135,6 +138,7 @@ where stage, prev_header, height, + tx_meta, sender_protocol, } } @@ -221,7 +225,7 @@ where UtxoSelectionCriteria::default(), OutputFeatures::default(), self.fee_per_gram, - None, + self.tx_meta.clone(), self.message.clone(), script!(Nop), Covenant::default(), @@ -554,7 +558,6 @@ where outbound_tx .sender_protocol .finalize( - KernelFeatures::empty(), &self.resources.factories, self.prev_header.clone(), self.height.unwrap_or(u64::MAX), diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 312a3fdc80..a5069d6562 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -58,6 +58,7 @@ use tari_core::{ recipient::RecipientSignedMessage, sender::TransactionSenderMessage, RewindData, + TransactionMetadata, }, CryptoFactories, ReceiverTransactionProtocol, @@ -583,6 +584,7 @@ where *output_features, fee_per_gram, message, + TransactionMetadata::default(), send_transaction_join_handles, transaction_broadcast_join_handles, rp, @@ -884,6 +886,7 @@ where output_features: OutputFeatures, fee_per_gram: MicroTari, message: String, + tx_meta: TransactionMetadata, join_handles: &mut FuturesUnordered< JoinHandle>>, >, @@ -965,6 +968,7 @@ where amount, fee_per_gram, message, + tx_meta, Some(reply_channel), TransactionSendProtocolStage::Initial, None, @@ -1025,7 +1029,7 @@ where UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), message.clone(), script.clone(), covenant.clone(), @@ -1105,7 +1109,6 @@ where // Finalize stp.finalize( - KernelFeatures::empty(), &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), @@ -1187,7 +1190,7 @@ where UtxoSelectionCriteria::default(), output_features, fee_per_gram, - None, + TransactionMetadata::default(), message.clone(), script, Covenant::default(), @@ -1244,7 +1247,6 @@ where // Finalize stp.finalize( - KernelFeatures::empty(), &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), @@ -1345,6 +1347,7 @@ where let tx_id = TxId::new_random(); let output_features = OutputFeatures::create_burn_output(); // Prepare sender part of the transaction + let tx_meta = TransactionMetadata::new_with_features(0.into(), 0, KernelFeatures::create_burn()); let mut stp = self .output_manager_service .prepare_transaction_to_send( @@ -1353,7 +1356,7 @@ where UtxoSelectionCriteria::default(), output_features, fee_per_gram, - None, + tx_meta, message.clone(), TariScript::default(), Covenant::default(), @@ -1390,7 +1393,6 @@ where // Finalize stp.finalize( - KernelFeatures::create_burn(), &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), @@ -1795,6 +1797,7 @@ where tx.amount, tx.fee, tx.message, + TransactionMetadata::default(), None, stage, None, diff --git a/base_layer/wallet/tests/output_manager_service_tests/service.rs b/base_layer/wallet/tests/output_manager_service_tests/service.rs index 963b5a12c6..0e50e831a6 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/service.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/service.rs @@ -50,7 +50,7 @@ use tari_core::{ TransactionOutput, UnblindedOutput, }, - transaction_protocol::{sender::TransactionSenderMessage, RewindData}, + transaction_protocol::{sender::TransactionSenderMessage, RewindData, TransactionMetadata}, weight::TransactionWeight, CryptoFactories, SenderTransactionProtocol, @@ -426,7 +426,7 @@ async fn test_utxo_selection_no_chain_metadata() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -459,7 +459,7 @@ async fn test_utxo_selection_no_chain_metadata() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), String::new(), script!(Nop), Covenant::default(), @@ -542,7 +542,7 @@ async fn test_utxo_selection_with_chain_metadata() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -604,7 +604,7 @@ async fn test_utxo_selection_with_chain_metadata() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -632,7 +632,7 @@ async fn test_utxo_selection_with_chain_metadata() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -704,7 +704,7 @@ async fn test_utxo_selection_with_tx_priority() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -783,7 +783,7 @@ async fn utxo_selection_for_contract_checkpoint() { signatures: CommitteeSignatures::empty(), }), fee_per_gram, - None, + TransactionMetadata::default(), String::new(), script!(Nop), Covenant::default(), @@ -826,7 +826,7 @@ async fn send_not_enough_funds() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(4), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -886,7 +886,7 @@ async fn send_no_change() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -952,7 +952,7 @@ async fn send_not_enough_for_change() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -993,7 +993,7 @@ async fn cancel_transaction() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(4), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -1086,7 +1086,7 @@ async fn test_get_balance() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(4), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -1143,7 +1143,7 @@ async fn sending_transaction_persisted_while_offline() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(4), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -1176,7 +1176,7 @@ async fn sending_transaction_persisted_while_offline() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(4), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -1477,7 +1477,7 @@ async fn test_txo_validation() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(10), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), diff --git a/base_layer/wallet/tests/transaction_service_tests/service.rs b/base_layer/wallet/tests/transaction_service_tests/service.rs index 26d931070b..14e8773bb1 100644 --- a/base_layer/wallet/tests/transaction_service_tests/service.rs +++ b/base_layer/wallet/tests/transaction_service_tests/service.rs @@ -76,11 +76,12 @@ use tari_core::{ fee::Fee, tari_amount::*, test_helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - transaction_components::{KernelBuilder, KernelFeatures, OutputFeatures, Transaction}, + transaction_components::{KernelBuilder, OutputFeatures, Transaction}, transaction_protocol::{ proto::protocol as proto, recipient::RecipientSignedMessage, sender::TransactionSenderMessage, + TransactionMetadata, }, CryptoFactories, ReceiverTransactionProtocol, @@ -1454,7 +1455,7 @@ async fn finalize_tx_with_incorrect_pubkey() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(25), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -1491,8 +1492,7 @@ async fn finalize_tx_with_incorrect_pubkey() { stp.add_single_recipient_info(recipient_reply.clone(), &factories.range_proof) .unwrap(); - stp.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) - .unwrap(); + stp.finalize(&factories, None, u64::MAX).unwrap(); let tx = stp.get_transaction().unwrap(); let finalized_transaction_message = proto::TransactionFinalizedMessage { @@ -1570,7 +1570,7 @@ async fn finalize_tx_with_missing_output() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroTari::from(20), - None, + TransactionMetadata::default(), "".to_string(), script!(Nop), Covenant::default(), @@ -1607,8 +1607,7 @@ async fn finalize_tx_with_missing_output() { stp.add_single_recipient_info(recipient_reply.clone(), &factories.range_proof) .unwrap(); - stp.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) - .unwrap(); + stp.finalize(&factories, None, u64::MAX).unwrap(); let finalized_transaction_message = proto::TransactionFinalizedMessage { tx_id: recipient_reply.tx_id.as_u64(), @@ -2925,7 +2924,7 @@ async fn test_restarting_transaction_protocols() { .add_single_recipient_info(alice_reply.clone(), &factories.range_proof) .unwrap(); - match bob_stp.finalize(KernelFeatures::empty(), &factories, None, u64::MAX) { + match bob_stp.finalize(&factories, None, u64::MAX) { Ok(_) => (), Err(e) => panic!("Should be able to finalize tx: {}", e), }; diff --git a/integration_tests/helpers/domainHasher.js b/integration_tests/helpers/domainHasher.js index cb2d40ddb9..4cbf2205d2 100644 --- a/integration_tests/helpers/domainHasher.js +++ b/integration_tests/helpers/domainHasher.js @@ -38,6 +38,11 @@ class DomainHasher { return this; } + chain_fixed_int(number, bits) { + blake2bUpdate(this.hasher, toLittleEndian(number, bits)); + return this; + } + finalize() { return blake2bFinal(this.hasher); } diff --git a/integration_tests/helpers/transactionBuilder.js b/integration_tests/helpers/transactionBuilder.js index 282076e80d..89bb57d300 100644 --- a/integration_tests/helpers/transactionBuilder.js +++ b/integration_tests/helpers/transactionBuilder.js @@ -25,16 +25,21 @@ class TransactionBuilder { return this.kv.private_key(id); } - buildChallenge(publicNonce, fee, lockHeight) { - const KEY = null; // optional key - const OUTPUT_LENGTH = 32; // bytes - const context = blake2bInit(OUTPUT_LENGTH, KEY); - const buff = Buffer.from(publicNonce, "hex"); - blake2bUpdate(context, buff); - blake2bUpdate(context, toLittleEndian(fee, 64)); - blake2bUpdate(context, toLittleEndian(lockHeight, 64)); - const final = blake2bFinal(context); - return Buffer.from(final).toString("hex"); + buildKernelChallenge(publicNonce, publicExcess, fee, lockHeight, features) { + const option_none = Buffer.from("00", "hex"); + const varint_height = Buffer.from([lockHeight]); + let hash = new DomainHasher( + "com.tari.base_layer.core.transactions.v0.kernel_signature" + ) + .chain(publicNonce) + .chain(publicExcess) + .chain_fixed_int(fee, 64) + .chain(varint_height) + .chain_fixed_int(features, 8) + .chain(option_none) + .finalize(); + + return Buffer.from(hash).toString("hex"); } featuresToConsensusBytes(features) { @@ -375,10 +380,15 @@ class TransactionBuilder { const excess = tari_crypto.commit(privateKey, BigInt(0)); this.kv.new_key("common_nonce"); const publicNonce = this.kv.public_key("common_nonce"); - const challenge = this.buildChallenge( + let PublicKeyExcess = tari_crypto.pubkey_from_secret( + privateKey.toString("hex") + ); + const challenge = this.buildKernelChallenge( publicNonce, + PublicKeyExcess, this.fee, - this.lockHeight + this.lockHeight, + 0 ); const privateNonce = this.kv.private_key("common_nonce"); const sig = tari_crypto.sign_challenge_with_nonce( @@ -430,7 +440,16 @@ class TransactionBuilder { const excess = tari_crypto.commit(privateKey, BigInt(0)); this.kv.new_key("nonce"); const public_nonce = this.kv.public_key("nonce"); - const challenge = this.buildChallenge(public_nonce, 0, 0); + let PublicKeyExcess = tari_crypto.pubkey_from_secret( + privateKey.toString("hex") + ); + const challenge = this.buildKernelChallenge( + public_nonce, + PublicKeyExcess, + 0, + 0, + 1 + ); const private_nonce = this.kv.private_key("nonce"); const sig = tari_crypto.sign_challenge_with_nonce( privateKey,