Skip to content

Commit 715644b

Browse files
author
Mikhail Zabaluev
authored
Merge pull request #571 from input-output-hk/transaction-validity
add transaction time validity
2 parents 5b9c98d + fc7e2e5 commit 715644b

File tree

24 files changed

+467
-158
lines changed

24 files changed

+467
-158
lines changed

chain-impl-mockchain/benches/tally.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fn tally_benchmark(
133133
);
134134

135135
res.push(controller.fragment_factory().vote_cast(
136+
BlockDate::first(),
136137
private_voter,
137138
VoteCast::new(vote_plan.to_id(), proposal_idx as u8, payload),
138139
));
@@ -171,9 +172,11 @@ fn tally_benchmark(
171172
let mut alice = controller.wallet(ALICE).unwrap();
172173

173174
let encrypted_tally = EncryptedVoteTally::new(vote_plan.to_id());
174-
let fragment = controller
175-
.fragment_factory()
176-
.vote_encrypted_tally(&alice, encrypted_tally);
175+
let fragment = controller.fragment_factory().vote_encrypted_tally(
176+
BlockDate::first(),
177+
&alice,
178+
encrypted_tally,
179+
);
177180

178181
let parameters = ledger.parameters.clone();
179182
let date = ledger.date();
@@ -264,9 +267,10 @@ fn tally_benchmark(
264267

265268
let decrypted_tally =
266269
VoteTally::new_private(vote_plan.to_id(), DecryptedPrivateTally::new(shares));
267-
let fragment = controller
268-
.fragment_factory()
269-
.vote_tally(&alice, decrypted_tally);
270+
let fragment =
271+
controller
272+
.fragment_factory()
273+
.vote_tally(BlockDate::first(), &alice, decrypted_tally);
270274

271275
c.bench_function(&format!("vote_tally_{}", benchmark_name), |b| {
272276
b.iter(|| {

chain-impl-mockchain/doc/format.abnf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ UPDATE-VOTE = TODO
134134
; note: IOW stand for Inputs-Outputs-Witnesses
135135
; ####################
136136

137-
IOW = SIZE-ELEMENT-8BIT ; number of inputs
137+
IOW = BLOCK-DATE ; end validity of this IOW
138+
SIZE-ELEMENT-8BIT ; number of inputs
138139
SIZE-ELEMENT-8BIT ; number of outputs
139140
*INPUT ; as many as indicated in the number of inputs
140141
*OUTPUT ; sa many as indicated in the number of outputs

chain-impl-mockchain/src/certificate/pool.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ mod tests {
456456
use super::{PoolOwnersSigned, PoolPermissions};
457457
use crate::{
458458
chaintypes::HeaderId,
459+
date::BlockDate,
459460
key::EitherEd25519SecretKey,
460461
testing::{
461462
builders::{make_witness, StakePoolBuilder},
@@ -739,6 +740,7 @@ mod tests {
739740

740741
let builder = TxBuilder::new()
741742
.set_payload(&NoExtra)
743+
.set_expiry_date(BlockDate::first().next_epoch())
742744
.set_ios(&pool_owner_with_sign.inputs(), &[]);
743745
let auth_data_hash = builder.get_auth_data_for_witness().hash();
744746
let builder = builder

chain-impl-mockchain/src/config.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub enum ConfigParam {
8282
AddCommitteeId(CommitteeId),
8383
RemoveCommitteeId(CommitteeId),
8484
PerVoteCertificateFees(PerVoteCertificateFee),
85+
TransactionMaxExpiryEpochs(u8),
8586
}
8687

8788
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -153,6 +154,8 @@ pub enum Tag {
153154
RemoveCommitteeId = 27,
154155
#[strum(to_string = "per-vote-certificate-fees")]
155156
PerVoteCertificateFees = 28,
157+
#[strum(to_string = "transaction-maximum-expiry-epochs")]
158+
TransactionMaxExpiryEpochs = 29,
156159
}
157160

158161
impl Tag {
@@ -183,6 +186,7 @@ impl Tag {
183186
26 => Some(Tag::AddCommitteeId),
184187
27 => Some(Tag::RemoveCommitteeId),
185188
28 => Some(Tag::PerVoteCertificateFees),
189+
29 => Some(Tag::TransactionMaxExpiryEpochs),
186190
_ => None,
187191
}
188192
}
@@ -218,6 +222,7 @@ impl<'a> From<&'a ConfigParam> for Tag {
218222
ConfigParam::AddCommitteeId(..) => Tag::AddCommitteeId,
219223
ConfigParam::RemoveCommitteeId(..) => Tag::RemoveCommitteeId,
220224
ConfigParam::PerVoteCertificateFees(..) => Tag::PerVoteCertificateFees,
225+
ConfigParam::TransactionMaxExpiryEpochs(..) => Tag::TransactionMaxExpiryEpochs,
221226
}
222227
}
223228
}
@@ -298,6 +303,9 @@ impl Readable for ConfigParam {
298303
Tag::PerVoteCertificateFees => {
299304
ConfigParamVariant::from_payload(bytes).map(ConfigParam::PerVoteCertificateFees)
300305
}
306+
Tag::TransactionMaxExpiryEpochs => {
307+
ConfigParamVariant::from_payload(bytes).map(ConfigParam::TransactionMaxExpiryEpochs)
308+
}
301309
}
302310
.map_err(Into::into)
303311
}
@@ -334,6 +342,7 @@ impl property::Serialize for ConfigParam {
334342
ConfigParam::AddCommitteeId(data) => data.to_payload(),
335343
ConfigParam::RemoveCommitteeId(data) => data.to_payload(),
336344
ConfigParam::PerVoteCertificateFees(data) => data.to_payload(),
345+
ConfigParam::TransactionMaxExpiryEpochs(data) => data.to_payload(),
337346
};
338347
let taglen = TagLen::new(tag, bytes.len()).ok_or_else(|| {
339348
io::Error::new(
@@ -858,7 +867,7 @@ mod test {
858867

859868
impl Arbitrary for ConfigParam {
860869
fn arbitrary<G: Gen>(g: &mut G) -> Self {
861-
match u8::arbitrary(g) % 29 {
870+
match u8::arbitrary(g) % 30 {
862871
0 => ConfigParam::Block0Date(Arbitrary::arbitrary(g)),
863872
1 => ConfigParam::Discrimination(Arbitrary::arbitrary(g)),
864873
2 => ConfigParam::ConsensusVersion(Arbitrary::arbitrary(g)),
@@ -888,6 +897,7 @@ mod test {
888897
26 => ConfigParam::AddCommitteeId(Arbitrary::arbitrary(g)),
889898
27 => ConfigParam::RemoveCommitteeId(Arbitrary::arbitrary(g)),
890899
28 => ConfigParam::PerCertificateFees(Arbitrary::arbitrary(g)),
900+
29 => ConfigParam::TransactionMaxExpiryEpochs(Arbitrary::arbitrary(g)),
891901
_ => unreachable!(),
892902
}
893903
}

chain-impl-mockchain/src/ledger/check.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::{Block0Error, Error};
22
use crate::certificate;
3+
use crate::date::BlockDate;
4+
use crate::setting;
35
use crate::transaction::*;
46
use crate::value::Value;
57
use chain_addr::Address;
@@ -142,6 +144,10 @@ pub(super) fn valid_pool_update_certificate(reg: &certificate::PoolUpdate) -> Le
142144
pub enum TxVerifyError {
143145
#[error("too many outputs, expected maximum of {expected}, but received {actual}")]
144146
TooManyOutputs { expected: u8, actual: u8 },
147+
#[error("Transaction validity expired")]
148+
TransactionExpired,
149+
#[error("Transaction validity is too far in the future")]
150+
TransactionValidForTooLong,
145151
}
146152

147153
#[allow(clippy::absurd_extreme_comparisons)]
@@ -164,6 +170,27 @@ pub(super) fn valid_transaction_ios_number<P>(
164170
Ok(())
165171
}
166172

173+
pub(super) fn valid_transaction_date<P>(
174+
settings: &setting::Settings,
175+
tx: &TransactionSlice<P>,
176+
date: BlockDate,
177+
) -> Result<(), TxVerifyError> {
178+
let valid_until = tx.valid_until();
179+
180+
// if current date epoch is less than until.epoch - setting, then
181+
// the transaction has a validity range that is too big to be accepted
182+
if_cond_fail_with!(
183+
date.epoch
184+
< valid_until
185+
.epoch
186+
.saturating_sub(settings.transaction_max_expiry_epochs.into()),
187+
TxVerifyError::TransactionValidForTooLong
188+
)?;
189+
// if current date is passed the validity until, the transaction is expired
190+
if_cond_fail_with!(date > valid_until, TxVerifyError::TransactionExpired)?;
191+
Ok(())
192+
}
193+
167194
#[cfg(test)]
168195
mod tests {
169196

0 commit comments

Comments
 (0)