diff --git a/chain-impl-mockchain/benches/tally.rs b/chain-impl-mockchain/benches/tally.rs index 2f6f162c2..a042b21b6 100644 --- a/chain-impl-mockchain/benches/tally.rs +++ b/chain-impl-mockchain/benches/tally.rs @@ -133,6 +133,7 @@ fn tally_benchmark( ); res.push(controller.fragment_factory().vote_cast( + BlockDate::first(), private_voter, VoteCast::new(vote_plan.to_id(), proposal_idx as u8, payload), )); @@ -171,9 +172,11 @@ fn tally_benchmark( let mut alice = controller.wallet(ALICE).unwrap(); let encrypted_tally = EncryptedVoteTally::new(vote_plan.to_id()); - let fragment = controller - .fragment_factory() - .vote_encrypted_tally(&alice, encrypted_tally); + let fragment = controller.fragment_factory().vote_encrypted_tally( + BlockDate::first(), + &alice, + encrypted_tally, + ); let parameters = ledger.parameters.clone(); let date = ledger.date(); @@ -264,9 +267,10 @@ fn tally_benchmark( let decrypted_tally = VoteTally::new_private(vote_plan.to_id(), DecryptedPrivateTally::new(shares)); - let fragment = controller - .fragment_factory() - .vote_tally(&alice, decrypted_tally); + let fragment = + controller + .fragment_factory() + .vote_tally(BlockDate::first(), &alice, decrypted_tally); c.bench_function(&format!("vote_tally_{}", benchmark_name), |b| { b.iter(|| { diff --git a/chain-impl-mockchain/doc/format.abnf b/chain-impl-mockchain/doc/format.abnf index 54bdc8122..53ed4dc74 100644 --- a/chain-impl-mockchain/doc/format.abnf +++ b/chain-impl-mockchain/doc/format.abnf @@ -134,7 +134,8 @@ UPDATE-VOTE = TODO ; note: IOW stand for Inputs-Outputs-Witnesses ; #################### -IOW = SIZE-ELEMENT-8BIT ; number of inputs +IOW = BLOCK-DATE ; end validity of this IOW + SIZE-ELEMENT-8BIT ; number of inputs SIZE-ELEMENT-8BIT ; number of outputs *INPUT ; as many as indicated in the number of inputs *OUTPUT ; sa many as indicated in the number of outputs diff --git a/chain-impl-mockchain/src/certificate/pool.rs b/chain-impl-mockchain/src/certificate/pool.rs index 0b043f584..e1b7cce88 100644 --- a/chain-impl-mockchain/src/certificate/pool.rs +++ b/chain-impl-mockchain/src/certificate/pool.rs @@ -456,6 +456,7 @@ mod tests { use super::{PoolOwnersSigned, PoolPermissions}; use crate::{ chaintypes::HeaderId, + date::BlockDate, key::EitherEd25519SecretKey, testing::{ builders::{make_witness, StakePoolBuilder}, @@ -739,6 +740,7 @@ mod tests { let builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&pool_owner_with_sign.inputs(), &[]); let auth_data_hash = builder.get_auth_data_for_witness().hash(); let builder = builder diff --git a/chain-impl-mockchain/src/config.rs b/chain-impl-mockchain/src/config.rs index 4d4cb024a..c760f173f 100644 --- a/chain-impl-mockchain/src/config.rs +++ b/chain-impl-mockchain/src/config.rs @@ -82,6 +82,7 @@ pub enum ConfigParam { AddCommitteeId(CommitteeId), RemoveCommitteeId(CommitteeId), PerVoteCertificateFees(PerVoteCertificateFee), + TransactionMaxExpiryEpochs(u8), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -153,6 +154,8 @@ pub enum Tag { RemoveCommitteeId = 27, #[strum(to_string = "per-vote-certificate-fees")] PerVoteCertificateFees = 28, + #[strum(to_string = "transaction-maximum-expiry-epochs")] + TransactionMaxExpiryEpochs = 29, } impl Tag { @@ -183,6 +186,7 @@ impl Tag { 26 => Some(Tag::AddCommitteeId), 27 => Some(Tag::RemoveCommitteeId), 28 => Some(Tag::PerVoteCertificateFees), + 29 => Some(Tag::TransactionMaxExpiryEpochs), _ => None, } } @@ -218,6 +222,7 @@ impl<'a> From<&'a ConfigParam> for Tag { ConfigParam::AddCommitteeId(..) => Tag::AddCommitteeId, ConfigParam::RemoveCommitteeId(..) => Tag::RemoveCommitteeId, ConfigParam::PerVoteCertificateFees(..) => Tag::PerVoteCertificateFees, + ConfigParam::TransactionMaxExpiryEpochs(..) => Tag::TransactionMaxExpiryEpochs, } } } @@ -298,6 +303,9 @@ impl Readable for ConfigParam { Tag::PerVoteCertificateFees => { ConfigParamVariant::from_payload(bytes).map(ConfigParam::PerVoteCertificateFees) } + Tag::TransactionMaxExpiryEpochs => { + ConfigParamVariant::from_payload(bytes).map(ConfigParam::TransactionMaxExpiryEpochs) + } } .map_err(Into::into) } @@ -334,6 +342,7 @@ impl property::Serialize for ConfigParam { ConfigParam::AddCommitteeId(data) => data.to_payload(), ConfigParam::RemoveCommitteeId(data) => data.to_payload(), ConfigParam::PerVoteCertificateFees(data) => data.to_payload(), + ConfigParam::TransactionMaxExpiryEpochs(data) => data.to_payload(), }; let taglen = TagLen::new(tag, bytes.len()).ok_or_else(|| { io::Error::new( @@ -858,7 +867,7 @@ mod test { impl Arbitrary for ConfigParam { fn arbitrary(g: &mut G) -> Self { - match u8::arbitrary(g) % 29 { + match u8::arbitrary(g) % 30 { 0 => ConfigParam::Block0Date(Arbitrary::arbitrary(g)), 1 => ConfigParam::Discrimination(Arbitrary::arbitrary(g)), 2 => ConfigParam::ConsensusVersion(Arbitrary::arbitrary(g)), @@ -888,6 +897,7 @@ mod test { 26 => ConfigParam::AddCommitteeId(Arbitrary::arbitrary(g)), 27 => ConfigParam::RemoveCommitteeId(Arbitrary::arbitrary(g)), 28 => ConfigParam::PerCertificateFees(Arbitrary::arbitrary(g)), + 29 => ConfigParam::TransactionMaxExpiryEpochs(Arbitrary::arbitrary(g)), _ => unreachable!(), } } diff --git a/chain-impl-mockchain/src/ledger/check.rs b/chain-impl-mockchain/src/ledger/check.rs index b446631be..3118a1bd7 100644 --- a/chain-impl-mockchain/src/ledger/check.rs +++ b/chain-impl-mockchain/src/ledger/check.rs @@ -1,5 +1,7 @@ use super::{Block0Error, Error}; use crate::certificate; +use crate::date::BlockDate; +use crate::setting; use crate::transaction::*; use crate::value::Value; use chain_addr::Address; @@ -142,6 +144,10 @@ pub(super) fn valid_pool_update_certificate(reg: &certificate::PoolUpdate) -> Le pub enum TxVerifyError { #[error("too many outputs, expected maximum of {expected}, but received {actual}")] TooManyOutputs { expected: u8, actual: u8 }, + #[error("Transaction validity expired")] + TransactionExpired, + #[error("Transaction validity is too far in the future")] + TransactionValidForTooLong, } #[allow(clippy::absurd_extreme_comparisons)] @@ -164,6 +170,27 @@ pub(super) fn valid_transaction_ios_number

( Ok(()) } +pub(super) fn valid_transaction_date

( + settings: &setting::Settings, + tx: &TransactionSlice

, + date: BlockDate, +) -> Result<(), TxVerifyError> { + let valid_until = tx.valid_until(); + + // if current date epoch is less than until.epoch - setting, then + // the transaction has a validity range that is too big to be accepted + if_cond_fail_with!( + date.epoch + < valid_until + .epoch + .saturating_sub(settings.transaction_max_expiry_epochs.into()), + TxVerifyError::TransactionValidForTooLong + )?; + // if current date is passed the validity until, the transaction is expired + if_cond_fail_with!(date > valid_until, TxVerifyError::TransactionExpired)?; + Ok(()) +} + #[cfg(test)] mod tests { diff --git a/chain-impl-mockchain/src/ledger/ledger.rs b/chain-impl-mockchain/src/ledger/ledger.rs index 8819c9f51..b43574f58 100644 --- a/chain-impl-mockchain/src/ledger/ledger.rs +++ b/chain-impl-mockchain/src/ledger/ledger.rs @@ -834,7 +834,7 @@ impl Ledger { Fragment::Transaction(tx) => { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_; } Fragment::OwnerStakeDelegation(tx) => { @@ -867,13 +867,13 @@ impl Ledger { } let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_stake_delegation(&payload)?; } Fragment::PoolRegistration(tx) => { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_pool_registration_signcheck( &tx.payload().into_payload(), &tx.transaction_binding_auth_data(), @@ -884,7 +884,7 @@ impl Ledger { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_pool_retirement( &tx.payload().into_payload(), &tx.transaction_binding_auth_data(), @@ -895,7 +895,7 @@ impl Ledger { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_pool_update( &tx.payload().into_payload(), &tx.transaction_binding_auth_data(), @@ -918,7 +918,7 @@ impl Ledger { Fragment::VotePlan(tx) => { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_vote_plan( &tx, block_date, @@ -936,7 +936,7 @@ impl Ledger { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_vote_tally( &tx.payload().into_payload(), @@ -948,7 +948,7 @@ impl Ledger { let tx = tx.as_slice(); let (new_ledger_, _fee) = - new_ledger.apply_transaction(&fragment_id, &tx, ledger_params)?; + new_ledger.apply_transaction(&fragment_id, &tx, block_date, ledger_params)?; new_ledger = new_ledger_.apply_encrypted_vote_tally( &tx.payload().into_payload(), @@ -965,6 +965,7 @@ impl Ledger { mut self, fragment_id: &FragmentId, tx: &TransactionSlice<'a, Extra>, + cur_date: BlockDate, dyn_params: &LedgerParameters, ) -> Result<(Self, Value), Error> where @@ -972,6 +973,7 @@ impl Ledger { LinearFee: FeeAlgorithm, { check::valid_transaction_ios_number(tx)?; + check::valid_transaction_date(&self.settings, tx, cur_date)?; let fee = calculate_fee(tx, dyn_params); tx.verify_strictly_balanced(fee)?; self = self.apply_tx_inputs(tx)?; @@ -1867,6 +1869,7 @@ mod tests { TestTx::new( TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], &[]) .set_witnesses(&[]) .set_payload_auth(&()), @@ -1877,6 +1880,7 @@ mod tests { TestTx::new( TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(inputs, outputs) .set_witnesses(&[]) .set_payload_auth(&()), @@ -1890,6 +1894,7 @@ mod tests { ) -> TestTx { let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[input], &[]); let witness = make_witness( @@ -2454,6 +2459,7 @@ mod tests { let builder_tx = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&inputs, &[reciever.make_output(Value(100))]); let witnesses: Vec = faucets @@ -2471,7 +2477,9 @@ mod tests { let tx = builder_tx.set_witnesses(&witnesses).set_payload_auth(&()); let fragment = TestTx::new(tx).get_fragment(); - assert!(test_ledger.apply_transaction(fragment).is_err()); + assert!(test_ledger + .apply_transaction(fragment, BlockDate::first()) + .is_err()); } #[test] @@ -2497,7 +2505,7 @@ mod tests { ); println!( "{:?}", - test_ledger.apply_transaction(test_tx.get_fragment()) + test_ledger.apply_transaction(test_tx.get_fragment(), BlockDate::first()) ); TestResult::error(""); } @@ -2524,7 +2532,7 @@ mod tests { &[receiver], ); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2543,6 +2551,7 @@ mod tests { let inputs: Vec = faucets.iter().map(|x| x.make_input(None)).collect(); let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&inputs, &[reciever.make_output(Value(2))]); let witness = make_witness( @@ -2557,7 +2566,7 @@ mod tests { ); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2578,7 +2587,7 @@ mod tests { &reciever, ); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_ok()); LedgerStateVerifier::new(test_ledger.into()) .pots() @@ -2602,10 +2611,13 @@ mod tests { .unwrap(); let block0_hash = test_ledger.block0_hash; - let tx_builder = TxBuilder::new().set_payload(&NoExtra).set_ios( - &input_addresses.make_inputs(&test_ledger), - &output_addresses.make_outputs(), - ); + let tx_builder = TxBuilder::new() + .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) + .set_ios( + &input_addresses.make_inputs(&test_ledger), + &output_addresses.make_outputs(), + ); let witnesses: Vec = input_addresses .as_addresses() @@ -2625,7 +2637,7 @@ mod tests { .and_then(|balance| balance - fee); match ( balance_res, - test_ledger.apply_transaction(test_tx.get_fragment()), + test_ledger.apply_transaction(test_tx.get_fragment(), BlockDate::first()), ) { (Ok(balance), Ok(_)) => TestResult::from_bool(balance == Value::zero()), (Err(err), Ok(_)) => TestResult::error(format!( @@ -2652,6 +2664,7 @@ mod tests { let inputs = [faucets[0].make_input(None), faucets[1].make_input(None)]; let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&inputs, &[reciever.make_output(Value(2))]); let auth_data = tx_builder.get_auth_data_for_witness().hash(); let witnesses = make_witnesses( @@ -2663,7 +2676,7 @@ mod tests { let tx = tx_builder.set_witnesses(&witnesses).set_payload_auth(&()); let test_tx = TestTx::new(tx); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2682,7 +2695,7 @@ mod tests { ); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2705,7 +2718,9 @@ mod tests { let fragment = TestTxBuilder::new(test_ledger.block0_hash) .move_all_funds(&mut test_ledger, &faucet, &reciever) .get_fragment(); - assert!(test_ledger.apply_transaction(fragment).is_ok()); + assert!(test_ledger + .apply_transaction(fragment, BlockDate::first()) + .is_ok()); LedgerStateVerifier::new(test_ledger.into()) .address_has_expected_balance(reciever.into(), Value(1)) @@ -2728,6 +2743,7 @@ mod tests { let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[faucet.make_input(utxo)], &[reciever.make_output()]); let witness = Witness::new_account( @@ -2741,7 +2757,7 @@ mod tests { let test_tx = TestTx::new(tx); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2754,7 +2770,9 @@ mod tests { .build() .unwrap(); - let tx_builder = TxBuilder::new().set_payload(&NoExtra); + let tx_builder = TxBuilder::new() + .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()); let tx_builder = tx_builder.set_ios(&[faucet.make_input(None)], &[reciever.make_output()]); let random_bytes = TestGen::bytes(); @@ -2765,7 +2783,7 @@ mod tests { let tx = tx_builder.set_witnesses(&[witness]).set_payload_auth(&()); let test_tx = TestTx::new(tx); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2782,6 +2800,7 @@ mod tests { let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[faucet.make_input(None)], &[reciever.make_output()]); let witness = make_witness( @@ -2794,7 +2813,7 @@ mod tests { let test_tx = TestTx::new(tx); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2811,6 +2830,7 @@ mod tests { let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[faucet.make_input(None)], &[reciever.make_output()]); let witness = make_witness( @@ -2823,7 +2843,7 @@ mod tests { let test_tx = TestTx::new(tx); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } @@ -2839,6 +2859,7 @@ mod tests { let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[faucet.make_input(None)], &[reciever.make_output()]); let witness = make_witness( @@ -2849,7 +2870,7 @@ mod tests { let tx = tx_builder.set_witnesses(&[witness]).set_payload_auth(&()); let test_tx = TestTx::new(tx); assert!(test_ledger - .apply_transaction(test_tx.get_fragment()) + .apply_transaction(test_tx.get_fragment(), BlockDate::first()) .is_err()); } } diff --git a/chain-impl-mockchain/src/ledger/tests/apply_block_tests.rs b/chain-impl-mockchain/src/ledger/tests/apply_block_tests.rs index 981b40883..8731b82ff 100644 --- a/chain-impl-mockchain/src/ledger/tests/apply_block_tests.rs +++ b/chain-impl-mockchain/src/ledger/tests/apply_block_tests.rs @@ -20,10 +20,7 @@ pub fn apply_block_increases_leaders_log() { .unwrap(); let stake_pool = controller.stake_pool("stake_pool").unwrap(); - let date = BlockDate { - epoch: 1, - slot_id: 0, - }; + let date = BlockDate::first(); let block = GenesisPraosBlockBuilder::new() .with_date(date) .with_chain_length(ledger.chain_length()) @@ -142,10 +139,7 @@ pub fn apply_block_incorrect_fragment() { let alice = controller.wallet("Alice").unwrap(); let bob = controller.wallet("Bob").unwrap(); - let date = BlockDate { - epoch: 1, - slot_id: 0, - }; + let date = BlockDate::first(); let fragment = TestTxBuilder::new(ledger.block0_hash) .move_funds( diff --git a/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_registration.rs b/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_registration.rs index 997358a96..3e0d2228e 100644 --- a/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_registration.rs +++ b/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_registration.rs @@ -39,9 +39,9 @@ pub fn pool_registration_is_accepted() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice, bob, clarice], &certificate); + .make_transaction(test_ledger.date(), &[alice, bob, clarice], &certificate); assert!(test_ledger - .apply_fragment(&fragment, BlockDate::first()) + .apply_fragment(&fragment, test_ledger.date()) .is_ok()); } @@ -67,10 +67,10 @@ pub fn pool_registration_zero_management_threshold() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice, bob, clarice], &certificate); + .make_transaction(test_ledger.date(), &[alice, bob, clarice], &certificate); assert_err!( Error::PoolRegistrationManagementThresholdZero, - test_ledger.apply_fragment(&fragment, BlockDate::first()) + test_ledger.apply_fragment(&fragment, test_ledger.date()) ); } @@ -96,10 +96,10 @@ pub fn pool_registration_management_threshold_above() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice, bob, clarice], &certificate); + .make_transaction(test_ledger.date(), &[alice, bob, clarice], &certificate); assert_err!( Error::PoolRegistrationManagementThresholdAbove, - test_ledger.apply_fragment(&fragment, BlockDate::first()) + test_ledger.apply_fragment(&fragment, test_ledger.date()) ); } @@ -123,7 +123,7 @@ pub fn pool_registration_too_many_owners() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert_err!( Error::PoolRegistrationHasTooManyOwners, test_ledger.apply_fragment(&fragment, BlockDate::first()) @@ -151,7 +151,7 @@ pub fn pool_registration_too_many_operators() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert_err!( Error::PoolRegistrationHasTooManyOperators, test_ledger.apply_fragment(&fragment, BlockDate::first()) @@ -175,9 +175,9 @@ pub fn pool_registration_zero_signatures() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction_different_signers(&alice, &[], &certificate); + .make_transaction_different_signers(test_ledger.date(), &alice, &[], &certificate); test_ledger - .apply_fragment(&fragment, BlockDate::first()) + .apply_fragment(&fragment, test_ledger.date()) .unwrap(); } @@ -201,9 +201,9 @@ pub fn pool_registration_too_many_signatures() { let certificate = build_stake_pool_registration_cert(&stake_pool.info()); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction_different_signers(&alice, &signers, &certificate); + .make_transaction_different_signers(test_ledger.date(), &alice, &signers, &certificate); assert_err!( Error::CertificateInvalidSignature, - test_ledger.apply_fragment(&fragment, BlockDate::first()) + test_ledger.apply_fragment(&fragment, test_ledger.date()) ); } diff --git a/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_update.rs b/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_update.rs index 977e8bab2..f6c6422c2 100644 --- a/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_update.rs +++ b/chain-impl-mockchain/src/ledger/tests/certificate_tests/pool_update.rs @@ -81,11 +81,11 @@ pub fn pool_update_wrong_last_hash() { }; let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert_eq!( test_ledger - .apply_fragment(&fragment, BlockDate::first()) + .apply_fragment(&fragment, test_ledger.date()) .err() .unwrap(), Error::PoolUpdateLastHashDoesntMatch @@ -119,7 +119,7 @@ pub fn pool_update_not_enough_fee() { }; let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, LinearFee::new(0, 0, 0)) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert_eq!( test_ledger @@ -161,7 +161,7 @@ pub fn pool_update_wrong_pool_id() { }; let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert_eq!( test_ledger @@ -203,7 +203,7 @@ pub fn pool_update_use_old_hash() { let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(Some(&alice), &certificate); + .make_transaction(test_ledger.date(), Some(&alice), &certificate); assert!(test_ledger .apply_fragment(&fragment, BlockDate::first()) @@ -222,7 +222,7 @@ pub fn pool_update_use_old_hash() { let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(Some(&alice), &certificate); + .make_transaction(test_ledger.date(), Some(&alice), &certificate); assert!(test_ledger .apply_fragment(&fragment, BlockDate::first()) @@ -242,7 +242,7 @@ pub fn pool_update_use_old_hash() { let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert!(test_ledger .apply_fragment(&fragment, BlockDate::first()) @@ -276,7 +276,7 @@ pub fn pool_update_update_fee_is_not_allowed() { let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(&[alice], &certificate); + .make_transaction(test_ledger.date(), &[alice], &certificate); assert_eq!( test_ledger @@ -310,7 +310,7 @@ pub fn pool_update_without_any_change() { }; let certificate = build_stake_pool_update_cert(&pool_update); let fragment = TestTxCertBuilder::new(test_ledger.block0_hash, test_ledger.fee()) - .make_transaction(Some(&alice), &certificate); + .make_transaction(test_ledger.date(), Some(&alice), &certificate); assert!(test_ledger .apply_fragment(&fragment, BlockDate::first()) diff --git a/chain-impl-mockchain/src/ledger/tests/discrimination_tests.rs b/chain-impl-mockchain/src/ledger/tests/discrimination_tests.rs index a7537342b..a685fd916 100644 --- a/chain-impl-mockchain/src/ledger/tests/discrimination_tests.rs +++ b/chain-impl-mockchain/src/ledger/tests/discrimination_tests.rs @@ -1,6 +1,7 @@ #![cfg(test)] use crate::{ + date::BlockDate, testing::{ arbitrary::KindTypeWithoutMultisig, builders::TestTxBuilder, @@ -73,7 +74,7 @@ pub fn ledger_verifies_transaction_discrimination( .get_fragment(); let are_discriminations_unified = arbitrary_input_disc == arbitrary_output_disc; - let actual_result = ledger.apply_transaction(fragment); + let actual_result = ledger.apply_transaction(fragment, BlockDate::first()); match (are_discriminations_unified, actual_result) { (true, Ok(_)) => TestResult::passed(), diff --git a/chain-impl-mockchain/src/ledger/tests/ledger_tests.rs b/chain-impl-mockchain/src/ledger/tests/ledger_tests.rs index 41c411d46..eee1867ff 100644 --- a/chain-impl-mockchain/src/ledger/tests/ledger_tests.rs +++ b/chain-impl-mockchain/src/ledger/tests/ledger_tests.rs @@ -3,6 +3,7 @@ use crate::{ chaintypes::ConsensusType, config::ConfigParam, + date::BlockDate, fragment::{config::ConfigParams, Fragment}, ledger::{ ledger::{ @@ -38,7 +39,7 @@ pub fn ledger_accepts_correct_transaction( .move_funds(&mut ledger, &faucet, &receiver, faucet.value) .get_fragment(); let total_funds_before = ledger.total_funds(); - let result = ledger.apply_transaction(fragment); + let result = ledger.apply_transaction(fragment, BlockDate::first()); if result.is_err() { return TestResult::error(format!("Error from ledger: {}", result.err().unwrap())); @@ -72,7 +73,7 @@ pub fn total_funds_are_const_in_ledger( &transaction_data.output_addresses, ); let total_funds_before = ledger.total_funds(); - let result = ledger.apply_transaction(signed_tx.get_fragment()); + let result = ledger.apply_transaction(signed_tx.get_fragment(), BlockDate::first()); if result.is_err() { return TestResult::error(format!("Error from ledger: {:?}", result.err())); diff --git a/chain-impl-mockchain/src/ledger/tests/transaction_tests.rs b/chain-impl-mockchain/src/ledger/tests/transaction_tests.rs index d1cd0c2e2..cf5844764 100644 --- a/chain-impl-mockchain/src/ledger/tests/transaction_tests.rs +++ b/chain-impl-mockchain/src/ledger/tests/transaction_tests.rs @@ -2,6 +2,7 @@ use crate::{ accounting::account::LedgerError::NonExistent, + date::BlockDate, ledger::{ self, check::TxVerifyError, @@ -40,7 +41,42 @@ pub fn transaction_fail_when_255_outputs() { expected: 254, actual: 255 }), - test_ledger.apply_transaction(fragment) + test_ledger.apply_transaction(fragment, BlockDate::first()) + ); +} + +#[test] +pub fn transaction_fail_when_validity_out_of_range() { + let mut test_ledger = LedgerBuilder::from_config(ConfigBuilder::new(0)) + .faucet_value(Value(1000)) + .build() + .expect("cannot build test ledger"); + + let receiver = AddressData::utxo(Discrimination::Test); + let output = Output { + address: receiver.address, + value: Value(1), + }; + let outputs = [output]; + + let valid_until = Some(BlockDate { + epoch: 10, + slot_id: 50, + }); + + let fragment = TestTxBuilder::new(test_ledger.block0_hash) + .move_to_outputs_from_faucet_with_validity(&mut test_ledger, valid_until, &outputs) + .get_fragment(); + + assert_err!( + TransactionMalformed(TxVerifyError::TransactionExpired), + test_ledger.apply_transaction( + fragment, + BlockDate { + epoch: 10, + slot_id: 51, + } + ) ); } @@ -57,14 +93,14 @@ pub fn duplicated_account_transaction() { .move_from_faucet(&mut test_ledger, &receiver.address, Value(100)) .get_fragment(); let fragment2 = fragment.clone(); - let result = test_ledger.apply_transaction(fragment); + let result = test_ledger.apply_transaction(fragment, BlockDate::first()); match result { Err(err) => panic!("first transaction should be succesful but {}", err), Ok(_) => { assert_err_match!( ledger::Error::AccountInvalidSignature { .. }, - test_ledger.apply_transaction(fragment2) + test_ledger.apply_transaction(fragment2, BlockDate::first()) ); } } @@ -86,7 +122,7 @@ pub fn transaction_nonexisting_account_input() { assert_err!( Account(NonExistent), - test_ledger.apply_transaction(fragment) + test_ledger.apply_transaction(fragment, BlockDate::first()) ); } @@ -105,7 +141,9 @@ pub fn transaction_with_incorrect_account_spending_counter() { .move_from_faucet(&mut test_ledger, &receiver.into(), Value(1000)) .get_fragment(); assert!( - test_ledger.apply_transaction(fragment).is_err(), + test_ledger + .apply_transaction(fragment, BlockDate::first()) + .is_err(), "first transaction should be successful" ); } @@ -123,10 +161,14 @@ pub fn repeated_account_transaction() { let fragment = TestTxBuilder::new(test_ledger.block0_hash) .move_all_funds(&mut test_ledger, &faucet, &receiver) .get_fragment(); - assert!(test_ledger.apply_transaction(fragment).is_ok()); + assert!(test_ledger + .apply_transaction(fragment, BlockDate::first()) + .is_ok()); faucet.confirm_transaction(); let fragment = TestTxBuilder::new(test_ledger.block0_hash) .move_all_funds(&mut test_ledger, &faucet, &receiver) .get_fragment(); - assert!(test_ledger.apply_transaction(fragment).is_err()); + assert!(test_ledger + .apply_transaction(fragment, BlockDate::first()) + .is_err()); } diff --git a/chain-impl-mockchain/src/setting.rs b/chain-impl-mockchain/src/setting.rs index af8e54cad..8e96eaf69 100644 --- a/chain-impl-mockchain/src/setting.rs +++ b/chain-impl-mockchain/src/setting.rs @@ -41,6 +41,7 @@ pub struct Settings { pub rewards_limit: rewards::Limit, pub pool_participation_capping: Option<(NonZeroU32, NonZeroU32)>, pub committees: Arc>, + pub transaction_max_expiry_epochs: u8, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -127,6 +128,7 @@ impl Settings { rewards_limit: rewards::Limit::None, pool_participation_capping: None, committees: Arc::new(Box::new([])), + transaction_max_expiry_epochs: 1, } } @@ -237,6 +239,9 @@ impl Settings { .into(), ); } + ConfigParam::TransactionMaxExpiryEpochs(max_expiry_epochs) => { + new_state.transaction_max_expiry_epochs = *max_expiry_epochs; + } } } @@ -269,6 +274,9 @@ impl Settings { } params.push(ConfigParam::LinearFee(self.linear_fees)); params.push(ConfigParam::ProposalExpiration(self.proposal_expiration)); + params.push(ConfigParam::TransactionMaxExpiryEpochs( + self.transaction_max_expiry_epochs, + )); match &self.reward_params { Some(p) => params.push(ConfigParam::RewardParams(p.clone())), diff --git a/chain-impl-mockchain/src/testing/builders/initial_builder.rs b/chain-impl-mockchain/src/testing/builders/initial_builder.rs index 685fbb9b0..1f6267366 100644 --- a/chain-impl-mockchain/src/testing/builders/initial_builder.rs +++ b/chain-impl-mockchain/src/testing/builders/initial_builder.rs @@ -1,6 +1,7 @@ use crate::{ account::DelegationType, certificate::{Certificate, PoolUpdate, VoteCast, VotePlan, VoteTally}, + date::BlockDate, fragment::Fragment, key::EitherEd25519SecretKey, ledger::ledger::OutputAddress, @@ -74,6 +75,7 @@ pub fn create_initial_vote_tally(vote_tally: &VoteTally, owners: &[Wallet]) -> F pub fn create_initial_transaction(wallet: &Wallet) -> Fragment { let tx = TxBuilder::new() .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], &[wallet.make_output()]) .set_witnesses_unchecked(&[]) .set_payload_auth(&()); @@ -92,11 +94,12 @@ pub fn create_initial_stake_pool_owner_delegation(delegation_type: DelegationTyp } fn set_initial_ios( - builder: TxBuilderState>, + builder: TxBuilderState>, inputs: &[Input], outputs: &[OutputAddress], ) -> TxBuilderState> { builder + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(inputs, outputs) .set_witnesses_unchecked(&[]) } @@ -199,6 +202,7 @@ impl InitialFaultTolerantTxBuilder { let output = self.reciever.make_output_with_value(Value(1)); let tx = TxBuilder::new() .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[input], &[output]) .set_witnesses_unchecked(&[]) .set_payload_auth(&()); @@ -209,6 +213,7 @@ impl InitialFaultTolerantTxBuilder { let input = self.sender.make_input_with_value(Value(1)); let tx = TxBuilder::new() .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[input], &[]) .set_witnesses_unchecked(&[]) .set_payload_auth(&()); @@ -216,7 +221,10 @@ impl InitialFaultTolerantTxBuilder { } pub fn transaction_with_witness_only(&self) -> Fragment { - let tx = TxBuilder::new().set_nopayload().set_ios(&[], &[]); + let tx = TxBuilder::new() + .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) + .set_ios(&[], &[]); let witness = self .sender .clone() diff --git a/chain-impl-mockchain/src/testing/builders/tx_builder.rs b/chain-impl-mockchain/src/testing/builders/tx_builder.rs index 747a8bff1..5ce8efa74 100644 --- a/chain-impl-mockchain/src/testing/builders/tx_builder.rs +++ b/chain-impl-mockchain/src/testing/builders/tx_builder.rs @@ -1,5 +1,6 @@ use crate::{ chaintypes::HeaderId, + date::BlockDate, fee::FeeAlgorithm, fragment::{Fragment, FragmentId}, testing::{ @@ -92,6 +93,7 @@ impl TestTxBuilder { }]; let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&inputs, &outputs); let witness = @@ -102,9 +104,10 @@ impl TestTxBuilder { TestTx { tx } } - pub fn move_to_outputs_from_faucet( + pub fn move_to_outputs_from_faucet_with_validity( &self, test_ledger: &mut TestLedger, + validity: Option, destination: &[Output

], ) -> TestTx { assert_eq!( @@ -125,6 +128,7 @@ impl TestTxBuilder { )]; let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(validity.unwrap_or(BlockDate::first())) .set_ios(&inputs, &destination); let witness = @@ -135,6 +139,14 @@ impl TestTxBuilder { TestTx { tx } } + pub fn move_to_outputs_from_faucet( + &self, + test_ledger: &mut TestLedger, + destination: &[Output
], + ) -> TestTx { + self.move_to_outputs_from_faucet_with_validity(test_ledger, None, destination) + } + pub fn move_all_funds( &self, test_ledger: &mut TestLedger, @@ -186,6 +198,7 @@ impl TestTxBuilder { .collect(); let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&inputs, &destinations); let witnesses: Vec = sources diff --git a/chain-impl-mockchain/src/testing/builders/tx_cert_builder.rs b/chain-impl-mockchain/src/testing/builders/tx_cert_builder.rs index 11d5c73c6..94ae5fc05 100644 --- a/chain-impl-mockchain/src/testing/builders/tx_cert_builder.rs +++ b/chain-impl-mockchain/src/testing/builders/tx_cert_builder.rs @@ -5,6 +5,7 @@ use crate::{ TallyProof, VotePlan, VotePlanProof, VoteTally, }, chaintypes::HeaderId, + date::BlockDate, fee::FeeAlgorithm, fee::LinearFee, fragment::Fragment, @@ -12,7 +13,7 @@ use crate::{ ledger::ledger::OutputAddress, testing::{data::Wallet, make_witness}, transaction::{ - AccountBindingSignature, Input, Payload, SetAuthData, SetIOs, + AccountBindingSignature, Input, Payload, SetAuthData, SetValidity, SingleAccountBindingSignature, TxBuilder, TxBuilderState, Witness, }, value::Value, @@ -42,14 +43,21 @@ impl TestTxCertBuilder { fn set_initial_ios( &self, - builder: TxBuilderState>, + current_date: BlockDate, + builder: TxBuilderState>, funder: &Wallet, inputs: &[Input], outputs: &[OutputAddress], should_make_witness: bool, ) -> TxBuilderState> { + let valid_until = BlockDate { + epoch: current_date.epoch + 1, + slot_id: current_date.slot_id, + }; //utxo not supported yet - let builder = builder.set_ios(inputs, outputs); + let builder = builder + .set_expiry_date(valid_until) + .set_ios(inputs, outputs); let witnesses: Vec = { if should_make_witness { @@ -68,6 +76,7 @@ impl TestTxCertBuilder { fn fragment( &self, + current_date: BlockDate, cert: &Certificate, keys: Vec, inputs: &[Input], @@ -78,6 +87,7 @@ impl TestTxCertBuilder { match cert { Certificate::StakeDelegation(s) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(s), &funder, inputs, @@ -93,6 +103,7 @@ impl TestTxCertBuilder { } Certificate::PoolRegistration(s) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(s), &funder, inputs, @@ -105,6 +116,7 @@ impl TestTxCertBuilder { } Certificate::PoolRetirement(s) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(s), &funder, inputs, @@ -117,6 +129,7 @@ impl TestTxCertBuilder { } Certificate::PoolUpdate(s) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(s), &funder, inputs, @@ -129,6 +142,7 @@ impl TestTxCertBuilder { } Certificate::OwnerStakeDelegation(s) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(s), &funder, inputs, @@ -140,6 +154,7 @@ impl TestTxCertBuilder { } Certificate::VotePlan(vp) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(vp), &funder, inputs, @@ -152,6 +167,7 @@ impl TestTxCertBuilder { } Certificate::VoteCast(vp) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(vp), &funder, inputs, @@ -163,6 +179,7 @@ impl TestTxCertBuilder { } Certificate::VoteTally(vt) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(vt), &funder, inputs, @@ -175,6 +192,7 @@ impl TestTxCertBuilder { } Certificate::EncryptedVoteTally(vote_tally) => { let builder = self.set_initial_ios( + current_date, TxBuilder::new().set_payload(vote_tally), &funder, inputs, @@ -188,13 +206,19 @@ impl TestTxCertBuilder { } } - pub fn make_transaction<'a, T>(self, signers: T, certificate: &Certificate) -> Fragment + pub fn make_transaction<'a, T>( + self, + date: BlockDate, + signers: T, + certificate: &Certificate, + ) -> Fragment where T: IntoIterator, { let mut remainder = signers.into_iter(); let funder = remainder.next().expect("needs at least one signer"); self.make_transaction_different_signers( + date, funder, iter::once(funder).chain(remainder), certificate, @@ -203,6 +227,7 @@ impl TestTxCertBuilder { pub fn make_transaction_different_signers<'a, T>( self, + date: BlockDate, funder: &'a Wallet, signers: T, certificate: &Certificate, @@ -212,7 +237,7 @@ impl TestTxCertBuilder { { let keys = signers.into_iter().map(|x| x.private_key()).collect(); let input = funder.make_input_with_value(self.fee(certificate)); - self.fragment(certificate, keys, &[input], &[], true, funder) + self.fragment(date, certificate, keys, &[input], &[], true, funder) } } @@ -285,16 +310,24 @@ pub fn pool_owner_signed( /// in order to test robustness of ledger pub struct FaultTolerantTxCertBuilder { builder: TestTxCertBuilder, + date: BlockDate, cert: Certificate, funder: Wallet, } impl FaultTolerantTxCertBuilder { - pub fn new(block0_hash: HeaderId, fee: LinearFee, cert: Certificate, funder: Wallet) -> Self { + pub fn new( + block0_hash: HeaderId, + fee: LinearFee, + cert: Certificate, + date: BlockDate, + funder: Wallet, + ) -> Self { Self { builder: TestTxCertBuilder::new(block0_hash, fee), cert, funder, + date, } } @@ -303,8 +336,15 @@ impl FaultTolerantTxCertBuilder { let input = self .funder .make_input_with_value(self.builder.fee(&self.cert)); - self.builder - .fragment(&self.cert, keys, &[input], &[], false, &self.funder) + self.builder.fragment( + self.date, + &self.cert, + keys, + &[input], + &[], + false, + &self.funder, + ) } pub fn transaction_input_to_low(&self) -> Fragment { @@ -312,8 +352,15 @@ impl FaultTolerantTxCertBuilder { let input_value = Value(1); let input = self.funder.make_input_with_value(input_value); let output = self.funder.make_output_with_value(Value(2)); - self.builder - .fragment(&self.cert, keys, &[input], &[output], false, &self.funder) + self.builder.fragment( + self.date, + &self.cert, + keys, + &[input], + &[output], + false, + &self.funder, + ) } pub fn transaction_with_input_output(&self) -> Fragment { @@ -321,8 +368,15 @@ impl FaultTolerantTxCertBuilder { let input_value = Value(self.builder.fee(&self.cert).0 + 1); let input = self.funder.make_input_with_value(input_value); let output = self.funder.make_output_with_value(Value(1)); - self.builder - .fragment(&self.cert, keys, &[input], &[output], false, &self.funder) + self.builder.fragment( + self.date, + &self.cert, + keys, + &[input], + &[output], + false, + &self.funder, + ) } pub fn transaction_with_output_only(&self) -> Fragment { @@ -330,8 +384,15 @@ impl FaultTolerantTxCertBuilder { let output = self .funder .make_output_with_value(self.builder.fee(&self.cert)); - self.builder - .fragment(&self.cert, keys, &[], &[output], false, &self.funder) + self.builder.fragment( + self.date, + &self.cert, + keys, + &[], + &[output], + false, + &self.funder, + ) } pub fn transaction_with_input_only(&self) -> Fragment { @@ -339,8 +400,15 @@ impl FaultTolerantTxCertBuilder { let input = self .funder .make_input_with_value(self.builder.fee(&self.cert)); - self.builder - .fragment(&self.cert, keys, &[input], &[], false, &self.funder) + self.builder.fragment( + self.date, + &self.cert, + keys, + &[input], + &[], + false, + &self.funder, + ) } pub fn transaction_with_witness(&self) -> Fragment { @@ -348,7 +416,14 @@ impl FaultTolerantTxCertBuilder { let input = self .funder .make_input_with_value(self.builder.fee(&self.cert)); - self.builder - .fragment(&self.cert, keys, &[input], &[], false, &self.funder) + self.builder.fragment( + self.date, + &self.cert, + keys, + &[input], + &[], + false, + &self.funder, + ) } } diff --git a/chain-impl-mockchain/src/testing/ledger.rs b/chain-impl-mockchain/src/testing/ledger.rs index 7e4f78dd7..c8e573520 100644 --- a/chain-impl-mockchain/src/testing/ledger.rs +++ b/chain-impl-mockchain/src/testing/ledger.rs @@ -339,6 +339,7 @@ impl LedgerBuilder { pub fn prefill_output(self, output: Output
) -> Self { let tx = TxBuilder::new() .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], &[output]) .set_witnesses(&[]) .set_payload_auth(&()); @@ -349,6 +350,7 @@ impl LedgerBuilder { for outputs_chunk in outputs.chunks(CHECK_TX_MAXIMUM_INPUTS.into()) { let tx = TxBuilder::new() .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], outputs_chunk) .set_witnesses(&[]) .set_payload_auth(&()); @@ -418,6 +420,7 @@ impl LedgerBuilder { { let tx = TxBuilder::new() .set_nopayload() + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], &group) .set_witnesses(&[]) .set_payload_auth(&()); @@ -468,13 +471,14 @@ pub struct TestLedger { } impl TestLedger { - pub fn apply_transaction(&mut self, fragment: Fragment) -> Result<(), Error> { + pub fn apply_transaction(&mut self, fragment: Fragment, date: BlockDate) -> Result<(), Error> { let fragment_id = fragment.hash(); match fragment { Fragment::Transaction(tx) => { match self.ledger.clone().apply_transaction( &fragment_id, &tx.as_slice(), + date, &self.parameters, ) { Err(err) => Err(err), diff --git a/chain-impl-mockchain/src/testing/scenario/controller.rs b/chain-impl-mockchain/src/testing/scenario/controller.rs index 09efcb6c6..1c645f1b9 100644 --- a/chain-impl-mockchain/src/testing/scenario/controller.rs +++ b/chain-impl-mockchain/src/testing/scenario/controller.rs @@ -3,6 +3,7 @@ use crate::{ DecryptedPrivateTally, EncryptedVoteTally, ExternalProposalId, Proposal, VoteCast, VotePlan, VoteTally, }, + date::BlockDate, fee::LinearFee, key::Hash, ledger::Error as LedgerError, @@ -113,7 +114,7 @@ impl Controller { let transaction = self .fragment_factory .transaction(from, to, test_ledger, funds); - test_ledger.apply_transaction(transaction) + test_ledger.apply_transaction(transaction, BlockDate::first()) } pub fn register( @@ -122,9 +123,9 @@ impl Controller { stake_pool: &StakePool, test_ledger: &mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self - .fragment_factory - .stake_pool_registration(funder, stake_pool); + let fragment = + self.fragment_factory + .stake_pool_registration(test_ledger.date(), funder, stake_pool); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -134,7 +135,9 @@ impl Controller { stake_pool: &StakePool, test_ledger: &mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self.fragment_factory.delegation(from, stake_pool); + let fragment = self + .fragment_factory + .delegation(test_ledger.date(), from, stake_pool); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -145,9 +148,12 @@ impl Controller { stake_pool: &StakePool, test_ledger: &mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self - .fragment_factory - .delegation_different_funder(funder, delegation, stake_pool); + let fragment = self.fragment_factory.delegation_different_funder( + test_ledger.date(), + funder, + delegation, + stake_pool, + ); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -156,7 +162,9 @@ impl Controller { from: &Wallet, test_ledger: &mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self.fragment_factory.delegation_remove(from); + let fragment = self + .fragment_factory + .delegation_remove(test_ledger.date(), from); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -166,7 +174,9 @@ impl Controller { distribution: &[(&StakePool, u8)], test_ledger: &mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self.fragment_factory.delegation_to_many(from, distribution); + let fragment = + self.fragment_factory + .delegation_to_many(test_ledger.date(), from, distribution); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -176,7 +186,9 @@ impl Controller { stake_pool: &StakePool, test_ledger: &mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self.fragment_factory.owner_delegation(from, stake_pool); + let fragment = self + .fragment_factory + .owner_delegation(test_ledger.date(), from, stake_pool); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -186,7 +198,9 @@ impl Controller { stake_pool: &'a StakePool, test_ledger: &'a mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self.fragment_factory.stake_pool_retire(owners, stake_pool); + let fragment = + self.fragment_factory + .stake_pool_retire(test_ledger.date(), owners, stake_pool); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -197,9 +211,9 @@ impl Controller { owners: impl IntoIterator, test_ledger: &'a mut TestLedger, ) -> Result<(), LedgerError> { - let fragment = self - .fragment_factory - .stake_pool_update(owners, stake_pool, update); + let fragment = + self.fragment_factory + .stake_pool_update(test_ledger.date(), owners, stake_pool, update); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -270,7 +284,9 @@ impl Controller { .expect("cannot find proposal"); let payload = payload_producer(&vote_plan, proposal); let vote_cast = VoteCast::new(vote_plan.to_id(), index, payload); - let fragment = self.fragment_factory.vote_cast(owner, vote_cast); + let fragment = self + .fragment_factory + .vote_cast(test_ledger.date(), owner, vote_cast); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -282,9 +298,9 @@ impl Controller { ) -> Result<(), LedgerError> { let vote_plan: VotePlan = vote_plan_def.clone().into(); let encrypted_tally = EncryptedVoteTally::new(vote_plan.to_id()); - let fragment = self - .fragment_factory - .vote_encrypted_tally(owner, encrypted_tally); + let fragment = + self.fragment_factory + .vote_encrypted_tally(test_ledger.date(), owner, encrypted_tally); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -297,7 +313,9 @@ impl Controller { let vote_plan: VotePlan = vote_plan_def.clone().into(); let vote_tally = VoteTally::new_public(vote_plan.to_id()); - let fragment = self.fragment_factory.vote_tally(owner, vote_tally); + let fragment = self + .fragment_factory + .vote_tally(test_ledger.date(), owner, vote_tally); test_ledger.apply_fragment(&fragment, test_ledger.date()) } @@ -311,7 +329,9 @@ impl Controller { let vote_plan: VotePlan = vote_plan_def.clone().into(); let vote_tally = VoteTally::new_private(vote_plan.to_id(), decrypted_tally); - let fragment = self.fragment_factory.vote_tally(owner, vote_tally); + let fragment = self + .fragment_factory + .vote_tally(test_ledger.date(), owner, vote_tally); test_ledger.apply_fragment(&fragment, test_ledger.date()) } } diff --git a/chain-impl-mockchain/src/testing/scenario/fragment_factory.rs b/chain-impl-mockchain/src/testing/scenario/fragment_factory.rs index fd278176d..830d54109 100644 --- a/chain-impl-mockchain/src/testing/scenario/fragment_factory.rs +++ b/chain-impl-mockchain/src/testing/scenario/fragment_factory.rs @@ -3,6 +3,7 @@ use crate::{ certificate::{ Certificate, EncryptedVoteTally, PoolId, PoolUpdate, VoteCast, VotePlan, VoteTally, }, + date::BlockDate, fee::LinearFee, fragment::Fragment, key::Hash, @@ -51,32 +52,43 @@ impl FragmentFactory { .get_fragment() } - pub fn stake_pool_registration(&self, funder: &Wallet, stake_pool: &StakePool) -> Fragment { + pub fn stake_pool_registration( + &self, + date: BlockDate, + funder: &Wallet, + stake_pool: &StakePool, + ) -> Fragment { let cert = build_stake_pool_registration_cert(&stake_pool.info()); - self.transaction_with_cert(Some(funder), &cert) + self.transaction_with_cert(date, Some(funder), &cert) } - pub fn delegation(&self, from: &Wallet, stake_pool: &StakePool) -> Fragment { + pub fn delegation(&self, date: BlockDate, from: &Wallet, stake_pool: &StakePool) -> Fragment { let cert = build_stake_delegation_cert(&stake_pool.info(), &from.as_account_data()); - self.transaction_with_cert(Some(from), &cert) + self.transaction_with_cert(date, Some(from), &cert) } pub fn delegation_different_funder( &self, + date: BlockDate, funder: &Wallet, delegation: &Wallet, stake_pool: &StakePool, ) -> Fragment { let cert = build_stake_delegation_cert(&stake_pool.info(), &delegation.as_account_data()); - self.transaction_with_cert(Some(funder), &cert) + self.transaction_with_cert(date, Some(funder), &cert) } - pub fn delegation_remove(&self, from: &Wallet) -> Fragment { + pub fn delegation_remove(&self, date: BlockDate, from: &Wallet) -> Fragment { let cert = build_no_stake_delegation(); - self.transaction_with_cert(Some(from), &cert) + self.transaction_with_cert(date, Some(from), &cert) } - pub fn delegation_to_many(&self, from: &Wallet, distribution: &[(&StakePool, u8)]) -> Fragment { + pub fn delegation_to_many( + &self, + date: BlockDate, + from: &Wallet, + distribution: &[(&StakePool, u8)], + ) -> Fragment { let pools_ratio_sum: u8 = distribution.iter().map(|(_st, ratio)| *ratio as u8).sum(); let pools: Vec<(PoolId, u8)> = distribution .iter() @@ -86,25 +98,32 @@ impl FragmentFactory { let delegation_ratio = DelegationRatio::new(pools_ratio_sum, pools); let delegation_type = DelegationType::Ratio(delegation_ratio.unwrap()); let cert = build_owner_stake_delegation(delegation_type); - self.transaction_with_cert(Some(from), &cert) + self.transaction_with_cert(date, Some(from), &cert) } - pub fn owner_delegation(&self, from: &Wallet, stake_pool: &StakePool) -> Fragment { + pub fn owner_delegation( + &self, + date: BlockDate, + from: &Wallet, + stake_pool: &StakePool, + ) -> Fragment { let cert = build_owner_stake_full_delegation(stake_pool.id()); - self.transaction_with_cert(Some(from), &cert) + self.transaction_with_cert(date, Some(from), &cert) } pub fn stake_pool_retire<'a>( &self, + date: BlockDate, owners: impl IntoIterator, stake_pool: &StakePool, ) -> Fragment { let certificate = build_stake_pool_retirement_cert(stake_pool.id(), 0); - self.transaction_with_cert(owners, &certificate) + self.transaction_with_cert(date, owners, &certificate) } pub fn stake_pool_update<'a>( &self, + date: BlockDate, owners: impl IntoIterator, stake_pool: &StakePool, update: StakePool, @@ -115,34 +134,40 @@ impl FragmentFactory { new_pool_reg: update.info(), }; let certificate = build_stake_pool_update_cert(&pool_update); - self.transaction_with_cert(owners, &certificate) + self.transaction_with_cert(date, owners, &certificate) } - pub fn vote_plan(&self, owner: &Wallet, vote_plan: VotePlan) -> Fragment { - self.transaction_with_cert(Some(owner), &vote_plan.into()) + pub fn vote_plan(&self, date: BlockDate, owner: &Wallet, vote_plan: VotePlan) -> Fragment { + self.transaction_with_cert(date, Some(owner), &vote_plan.into()) } - pub fn vote_cast(&self, owner: &Wallet, vote_cast: VoteCast) -> Fragment { - self.transaction_with_cert(Some(owner), &vote_cast.into()) + pub fn vote_cast(&self, date: BlockDate, owner: &Wallet, vote_cast: VoteCast) -> Fragment { + self.transaction_with_cert(date, Some(owner), &vote_cast.into()) } pub fn vote_encrypted_tally( &self, + date: BlockDate, owner: &Wallet, encrypted_tally: EncryptedVoteTally, ) -> Fragment { - self.transaction_with_cert(Some(owner), &encrypted_tally.into()) + self.transaction_with_cert(date, Some(owner), &encrypted_tally.into()) } - pub fn vote_tally(&self, owner: &Wallet, vote_tally: VoteTally) -> Fragment { - self.transaction_with_cert(Some(owner), &vote_tally.into()) + pub fn vote_tally(&self, date: BlockDate, owner: &Wallet, vote_tally: VoteTally) -> Fragment { + self.transaction_with_cert(date, Some(owner), &vote_tally.into()) } fn transaction_with_cert<'a>( &self, + date: BlockDate, wallets: impl IntoIterator, certificate: &Certificate, ) -> Fragment { - TestTxCertBuilder::new(self.block0_hash, self.fee).make_transaction(wallets, certificate) + TestTxCertBuilder::new(self.block0_hash, self.fee).make_transaction( + date, + wallets, + certificate, + ) } } diff --git a/chain-impl-mockchain/src/transaction/builder.rs b/chain-impl-mockchain/src/transaction/builder.rs index f20dfd2ea..97d976134 100644 --- a/chain-impl-mockchain/src/transaction/builder.rs +++ b/chain-impl-mockchain/src/transaction/builder.rs @@ -5,6 +5,7 @@ use super::transaction::{ }; use super::transfer::Output; use super::witness::Witness; +use crate::date::BlockDate; use chain_addr::Address; use std::marker::PhantomData; @@ -26,6 +27,7 @@ impl Clone for TxBuilderState { } pub enum SetPayload {} +pub struct SetValidity

(PhantomData

); pub struct SetIOs

(PhantomData

); pub struct SetWitnesses

(PhantomData

); pub struct SetAuthData(PhantomData

); @@ -52,6 +54,7 @@ impl TxBuilder { sz: 0, nb_inputs: 0, nb_outputs: 0, + valid_until: BlockDate::first(), inputs: 0, outputs: 0, witnesses: 0, @@ -70,7 +73,7 @@ impl TxBuilderState { impl TxBuilderState { /// Set the payload of this transaction - pub fn set_payload(mut self, payload: &P) -> TxBuilderState> { + pub fn set_payload(mut self, payload: &P) -> TxBuilderState> { if P::HAS_DATA { self.data.extend_from_slice(payload.payload_data().as_ref()); } @@ -82,11 +85,27 @@ impl TxBuilderState { } } - pub fn set_nopayload(self) -> TxBuilderState> { + pub fn set_nopayload(self) -> TxBuilderState> { self.set_payload(&NoExtra) } } +impl

TxBuilderState> { + pub fn set_expiry_date(mut self, valid_until: BlockDate) -> TxBuilderState> { + fn write_date(data: &mut Vec, date: BlockDate) { + data.extend_from_slice(&date.epoch.to_be_bytes()); + data.extend_from_slice(&date.slot_id.to_be_bytes()); + } + write_date(&mut self.data, valid_until); + self.tstruct.valid_until = valid_until; + TxBuilderState { + data: self.data, + tstruct: self.tstruct, + phantom: PhantomData, + } + } +} + impl

TxBuilderState> { /// Set the inputs and outputs of this transaction /// @@ -219,6 +238,7 @@ mod tests { let block0_hash = TestGen::hash(); let tx_builder = TxBuilder::new() .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[faucets[0].make_input(None)], &[reciever.make_output()]); let witness1 = make_witness( @@ -243,10 +263,13 @@ mod tests { ]; let reciever = AddressData::utxo(Discrimination::Test); let block0_hash = TestGen::hash(); - let tx_builder = TxBuilder::new().set_payload(&NoExtra).set_ios( - &[faucets[0].make_input(None), faucets[1].make_input(None)], - &[reciever.make_output(Value(2))], - ); + let tx_builder = TxBuilder::new() + .set_payload(&NoExtra) + .set_expiry_date(BlockDate::first().next_epoch()) + .set_ios( + &[faucets[0].make_input(None), faucets[1].make_input(None)], + &[reciever.make_output(Value(2))], + ); let witness = make_witness( &block0_hash, diff --git a/chain-impl-mockchain/src/transaction/mod.rs b/chain-impl-mockchain/src/transaction/mod.rs index 2796788f9..562f354b2 100644 --- a/chain-impl-mockchain/src/transaction/mod.rs +++ b/chain-impl-mockchain/src/transaction/mod.rs @@ -16,7 +16,9 @@ use chain_core::mempack::{ReadBuf, ReadError, Readable}; use chain_core::property; // to remove.. -pub use builder::{SetAuthData, SetIOs, SetPayload, SetWitnesses, TxBuilder, TxBuilderState}; +pub use builder::{ + SetAuthData, SetIOs, SetPayload, SetValidity, SetWitnesses, TxBuilder, TxBuilderState, +}; pub use element::*; pub use input::*; pub use io::{Error, InputOutput, InputOutputBuilder, OutputPolicy}; diff --git a/chain-impl-mockchain/src/transaction/test.rs b/chain-impl-mockchain/src/transaction/test.rs index 36c759b9c..b941636cf 100644 --- a/chain-impl-mockchain/src/transaction/test.rs +++ b/chain-impl-mockchain/src/transaction/test.rs @@ -4,6 +4,7 @@ use super::{ }; #[cfg(test)] use crate::certificate::OwnerStakeDelegation; +use crate::date::BlockDate; use crate::key::{EitherEd25519SecretKey, SpendingSignature}; #[cfg(test)] use crate::testing::serialization::serialization_bijection_r; @@ -160,6 +161,7 @@ where TxBuilder::new() .set_payload(&payload) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&inputs, &outputs) .set_witnesses(&witnesses) .set_payload_auth(&payload_auth) diff --git a/chain-impl-mockchain/src/transaction/transaction.rs b/chain-impl-mockchain/src/transaction/transaction.rs index 5ec01713f..447a3d135 100644 --- a/chain-impl-mockchain/src/transaction/transaction.rs +++ b/chain-impl-mockchain/src/transaction/transaction.rs @@ -4,6 +4,7 @@ use super::input::{Input, INPUT_SIZE}; use super::payload::{Payload, PayloadAuthSlice, PayloadSlice}; use super::transfer::Output; use super::witness::Witness; +use crate::date::BlockDate; use crate::value::{Value, ValueError}; use chain_addr::Address; use chain_core::mempack::{ReadBuf, Readable}; @@ -34,6 +35,7 @@ impl

Debug for Transaction

{ .field("payload", &tx.payload().0) .field("nb_inputs", &tx.nb_inputs()) .field("nb_outputs", &tx.nb_outputs()) + .field("valid_until", &tx.valid_until()) .field("nb_witnesses", &tx.nb_witnesses()) .field("total_input_value", &self.total_input()) .field("total_output_value", &self.total_output()) @@ -224,6 +226,7 @@ impl<'a> InputsWitnessesSlice<'a> { pub enum TransactionStructError { CannotReadNbInputs, CannotReadNbOutputs, + CannotReadDate, PayloadInvalid, InputsInvalid, OutputsInvalid, @@ -238,6 +241,7 @@ pub(super) struct TransactionStruct { pub(super) sz: usize, pub(super) nb_inputs: u8, pub(super) nb_outputs: u8, + pub(super) valid_until: BlockDate, pub(super) inputs: usize, pub(super) outputs: usize, pub(super) witnesses: usize, @@ -254,6 +258,15 @@ fn get_spine(slice: &[u8]) -> Result(slice: &[u8]) -> Result Transaction

{ { TxBuilder::new() .set_payload(payload) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], &[]) .set_witnesses(&[]) .set_payload_auth(payload_auth) @@ -355,6 +370,7 @@ impl

Transaction

{ { TxBuilder::new() .set_payload(payload) + .set_expiry_date(BlockDate::first().next_epoch()) .set_ios(&[], &[]) .set_witnesses(&[]) } @@ -449,6 +465,10 @@ impl<'a, P> TransactionSlice<'a, P> { self.tstruct.nb_inputs } + pub fn valid_until(&self) -> BlockDate { + self.tstruct.valid_until + } + pub fn nb_outputs(&self) -> u8 { self.tstruct.nb_outputs } diff --git a/chain-impl-mockchain/src/vote/manager.rs b/chain-impl-mockchain/src/vote/manager.rs index e03a641c0..5ec088162 100644 --- a/chain-impl-mockchain/src/vote/manager.rs +++ b/chain-impl-mockchain/src/vote/manager.rs @@ -1007,9 +1007,11 @@ mod tests { 3, ); + let vote_start = BlockDate::from_epoch_slot_id(1, 0); + let vote_end = BlockDate::from_epoch_slot_id(2, 0); let vote_plan = VotePlan::new( - BlockDate::from_epoch_slot_id(1, 0), - BlockDate::from_epoch_slot_id(2, 0), + vote_start, + vote_end, BlockDate::from_epoch_slot_id(3, 0), proposals, PayloadType::Public, @@ -1044,7 +1046,7 @@ mod tests { ) .unwrap(); - let tally_proof = get_tally_proof(&committee, vote_plan.to_id()); + let tally_proof = get_tally_proof(vote_start, &committee, vote_plan.to_id()); let block_date = BlockDate { epoch: 2, @@ -1076,9 +1078,11 @@ mod tests { let committee = Wallet::from_value(Value(100)); let proposals = VoteTestGen::proposals(3); + let vote_start = BlockDate::from_epoch_slot_id(1, 0); + let vote_end = BlockDate::from_epoch_slot_id(2, 0); let vote_plan = VotePlan::new( - BlockDate::from_epoch_slot_id(1, 0), - BlockDate::from_epoch_slot_id(2, 0), + vote_start, + vote_end, BlockDate::from_epoch_slot_id(3, 0), proposals, PayloadType::Public, @@ -1094,7 +1098,7 @@ mod tests { stake_controlled = stake_controlled.add_to(committee.public_key().into(), Stake(51)); stake_controlled = stake_controlled.add_unassigned(Stake(49)); - let tally_proof = get_tally_proof(&committee, vote_plan.to_id()); + let tally_proof = get_tally_proof(vote_start, &committee, vote_plan.to_id()); let block_date = BlockDate { epoch: 2, @@ -1130,9 +1134,11 @@ mod tests { let committee = Wallet::from_value(Value(100)); let proposals = VoteTestGen::proposals(3); + let vote_start = BlockDate::from_epoch_slot_id(1, 0); + let vote_end = BlockDate::from_epoch_slot_id(2, 0); let vote_plan = VotePlan::new( - BlockDate::from_epoch_slot_id(1, 0), - BlockDate::from_epoch_slot_id(2, 0), + vote_start, + vote_end, BlockDate::from_epoch_slot_id(3, 0), proposals, PayloadType::Public, @@ -1148,7 +1154,7 @@ mod tests { stake_controlled = stake_controlled.add_to(committee.public_key().into(), Stake(51)); stake_controlled = stake_controlled.add_unassigned(Stake(49)); - let tally_proof = get_tally_proof(&committee, vote_plan.to_id()); + let tally_proof = get_tally_proof(vote_start, &committee, vote_plan.to_id()); let invalid_block_date = BlockDate { epoch: 0, @@ -1196,7 +1202,7 @@ mod tests { let mut stake_controlled = StakeControl::new(); stake_controlled = stake_controlled.add_to(committee.public_key().into(), Stake(51)); - let tally_proof = get_tally_proof(&committee, vote_plan.to_id()); + let tally_proof = get_tally_proof(vote_plan.vote_start(), &committee, vote_plan.to_id()); let block_date = BlockDate { epoch: 2, @@ -1251,7 +1257,7 @@ mod tests { let mut stake_controlled = StakeControl::new(); stake_controlled = stake_controlled.add_to(committee.public_key().into(), Stake(51)); - let tally_proof = get_tally_proof(&committee, vote_plan.to_id()); + let tally_proof = get_tally_proof(vote_plan.vote_start(), &committee, vote_plan.to_id()); let block_date = BlockDate { epoch: 2, @@ -1274,10 +1280,10 @@ mod tests { ); } - fn get_tally_proof(wallet: &Wallet, id: VotePlanId) -> TallyProof { + fn get_tally_proof(date: BlockDate, wallet: &Wallet, id: VotePlanId) -> TallyProof { let certificate = build_vote_tally_cert(id); let fragment = TestTxCertBuilder::new(TestGen::hash(), LinearFee::new(0, 0, 0)) - .make_transaction(Some(wallet), &certificate); + .make_transaction(date, Some(wallet), &certificate); match fragment { Fragment::VoteTally(tx) => {