Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions pallas-codec/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use minicbor::{
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, str::FromStr};
use std::{
collections::HashMap,
collections::{HashMap, BTreeSet},
fmt,
hash::Hash as StdHash,
ops::{Deref, DerefMut},
Expand Down Expand Up @@ -712,7 +712,7 @@ where
///
/// Optional 258 tag (until era after Conway, at which point is it required)
/// with a vec of items which should contain no duplicates
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Serialize, Deserialize, Ord)]
pub struct Set<T>(Vec<T>);

impl<T> Set<T> {
Expand Down Expand Up @@ -806,6 +806,35 @@ impl<T> NonEmptySet<T> {
}
}

pub trait BusinessInvariants {
/// Checks that the business invariants for this type hold.
fn validate_data(&self) -> bool;
}

impl<T: BusinessInvariants> BusinessInvariants for Option<T> {
fn validate_data(&self) -> bool
{
self.as_ref().is_none_or(|x| x.validate_data())
}
}

impl<T: Ord> BusinessInvariants for &NonEmptySet<T> {
/// The inner vec is nonempty and contains no duplicates.
fn validate_data(&self) -> bool
{
let mut seen = BTreeSet::new();
!self.0.is_empty() && self.0.iter().all(|item| seen.insert(item))
}
}

impl<T: Ord> BusinessInvariants for NonEmptySet<T> {
/// The inner vec is nonempty and contains no duplicates.
fn validate_data(&self) -> bool
{
(&self).validate_data()
}
}

impl<T> Deref for NonEmptySet<T> {
type Target = Vec<T>;

Expand Down
6 changes: 3 additions & 3 deletions pallas-primitives/src/alonzo/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ pub struct TransactionBody {
pub network_id: Option<NetworkId>,
}

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct VKeyWitness {
#[n(0)]
pub vkey: Bytes,
Expand All @@ -323,7 +323,7 @@ pub struct VKeyWitness {
pub signature: Bytes,
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cbor(flat)]
pub enum NativeScript {
#[n(0)]
Expand Down Expand Up @@ -384,7 +384,7 @@ pub struct RedeemerPointer {
, attributes : bytes
] */

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct BootstrapWitness {
#[n(0)]
pub public_key: Bytes,
Expand Down
51 changes: 42 additions & 9 deletions pallas-primitives/src/conway/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use crate::{
PlutusScript, PolicyId, PoolKeyhash, PoolMetadata, PoolMetadataHash, Port, PositiveCoin,
PositiveInterval, ProtocolVersion, RationalNumber, Relay, RewardAccount, ScriptHash, Set,
StakeCredential, TransactionIndex, TransactionInput, UnitInterval, VrfCert, VrfKeyhash,
BusinessInvariants,
};

use crate::BTreeMap;
Expand Down Expand Up @@ -49,7 +50,7 @@ pub type Withdrawals = BTreeMap<RewardAccount, Coin>;

pub type RequiredSigners = NonEmptySet<AddrKeyhash>;

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cbor(flat)]
pub enum Certificate {
#[n(0)]
Expand Down Expand Up @@ -152,7 +153,7 @@ pub enum Language {
#[deprecated(since = "0.31.0", note = "use `CostModels` instead")]
pub type CostMdls = CostModels;

#[derive(Serialize, Deserialize, Encode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cbor(map)]
pub struct CostModels {
#[n(0)]
Expand Down Expand Up @@ -195,7 +196,7 @@ impl<'b, C> minicbor::Decode<'b, C> for CostModels {
}
}

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cbor(map)]
pub struct ProtocolParamUpdate {
#[n(0)]
Expand Down Expand Up @@ -271,7 +272,7 @@ pub struct Update {
pub epoch: Epoch,
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct PoolVotingThresholds {
#[n(0)]
pub motion_no_confidence: UnitInterval,
Expand All @@ -285,7 +286,7 @@ pub struct PoolVotingThresholds {
pub security_voting_threshold: UnitInterval,
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct DRepVotingThresholds {
#[n(0)]
pub motion_no_confidence: UnitInterval,
Expand Down Expand Up @@ -377,6 +378,17 @@ pub struct TransactionBody<'a> {
#[deprecated(since = "1.0.0-alpha", note = "use `TransactionBody` instead")]
pub type MintedTransactionBody<'a> = TransactionBody<'a>;

impl BusinessInvariants for TransactionBody<'_> {
fn validate_data(&self) -> bool
{
self.certificates.validate_data() &&
self.collateral.validate_data() &&
self.required_signers.validate_data() &&
self.reference_inputs.validate_data() &&
self.proposal_procedures.validate_data()
}
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[cbor(index_only)]
pub enum Vote {
Expand All @@ -398,7 +410,7 @@ pub struct VotingProcedure {
pub anchor: Option<Anchor>,
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct ProposalProcedure {
#[n(0)]
pub deposit: Coin,
Expand All @@ -410,7 +422,7 @@ pub struct ProposalProcedure {
pub anchor: Anchor,
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cbor(flat)]
pub enum GovAction {
#[n(0)]
Expand Down Expand Up @@ -441,7 +453,7 @@ pub enum GovAction {
Information,
}

#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Encode, Decode, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct Constitution {
#[n(0)]
pub anchor: Anchor,
Expand Down Expand Up @@ -506,7 +518,7 @@ pub use crate::alonzo::VKeyWitness;

pub use crate::alonzo::NativeScript;

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct ExUnitPrices {
#[n(0)]
pub mem_price: RationalNumber,
Expand Down Expand Up @@ -617,6 +629,19 @@ pub struct WitnessSet<'b> {
#[deprecated(since = "1.0.0-alpha", note = "use `WitnessSet` instead")]
pub type MintedWitnessSet<'b> = WitnessSet<'b>;

impl BusinessInvariants for WitnessSet<'_> {
fn validate_data(&self) -> bool
{
self.vkeywitness.validate_data() &&
self.native_script.validate_data() &&
self.bootstrap_witness.validate_data() &&
self.plutus_v1_script.validate_data() &&
self.plutus_data.validate_data() &&
self.plutus_v2_script.validate_data() &&
self.plutus_v3_script.validate_data()
}
}

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)]
#[cbor(map)]
pub struct PostAlonzoAuxiliaryData {
Expand Down Expand Up @@ -720,6 +745,14 @@ pub struct Tx<'b> {
pub auxiliary_data: Nullable<KeepRaw<'b, AuxiliaryData>>,
}

impl BusinessInvariants for Tx<'_> {
fn validate_data(&self) -> bool
{
self.transaction_body.validate_data() &&
self.transaction_witness_set.validate_data()
}
}

#[deprecated(since = "1.0.0-alpha", note = "use `Tx` instead")]
pub type MintedTx<'b> = Tx<'b>;

Expand Down
12 changes: 6 additions & 6 deletions pallas-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use pallas_codec::codec_by_datatype;

pub use pallas_codec::utils::{
Bytes, Int, KeepRaw, KeyValuePairs, MaybeIndefArray, NonEmptySet, NonZeroInt, Nullable,
PositiveCoin, Set,
PositiveCoin, Set, BusinessInvariants,
};
pub use pallas_crypto::hash::Hash;

Expand All @@ -40,7 +40,7 @@ pub type DnsName = String;

pub type Epoch = u64;

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Copy)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct ExUnits {
#[n(0)]
pub mem: u64,
Expand Down Expand Up @@ -139,7 +139,7 @@ pub enum NonceVariant {
Nonce,
}

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cbor(transparent)]
pub struct PlutusScript<const VERSION: usize>(pub Bytes);

Expand All @@ -153,7 +153,7 @@ pub type PolicyId = Hash<28>;

pub type PoolKeyhash = Hash<28>;

#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct PoolMetadata {
#[n(0)]
pub url: String,
Expand All @@ -170,7 +170,7 @@ pub type PositiveInterval = RationalNumber;

pub type ProtocolVersion = (u64, u64);

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct RationalNumber {
pub numerator: u64,
pub denominator: u64,
Expand Down Expand Up @@ -202,7 +202,7 @@ impl<C> minicbor::encode::Encode<C> for RationalNumber {
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub enum Relay {
SingleHostAddr(Option<Port>, Option<IPv4>, Option<IPv6>),
SingleHostName(Option<Port>, DnsName),
Expand Down
11 changes: 10 additions & 1 deletion pallas-validate/src/phase1/conway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::utils::{
use pallas_addresses::{ScriptHash, ShelleyAddress, ShelleyPaymentPart};
use pallas_codec::{
minicbor::{encode, Encoder},
utils::{Bytes, KeepRaw},
utils::{BusinessInvariants, Bytes, KeepRaw},
};
use pallas_primitives::{
babbage,
Expand All @@ -38,6 +38,7 @@ pub fn validate_conway_tx(
) -> ValidationResult {
let tx_body: &TransactionBody = &mtx.transaction_body.clone();
let size: u32 = get_conway_tx_size(mtx).ok_or(PostAlonzo(UnknownTxSize))?;
check_data_business_invariants(mtx)?;
check_ins_not_empty(tx_body)?;
check_all_ins_in_utxos(tx_body, utxos)?;
check_tx_validity_interval(tx_body, block_slot)?;
Expand All @@ -56,6 +57,14 @@ pub fn validate_conway_tx(
check_script_data_hash(tx_body, mtx, utxos, network_magic, network_id, block_slot)
}

fn check_data_business_invariants(mtx: &Tx) -> ValidationResult {
if mtx.validate_data() {
Ok(())
} else {
Err(PostAlonzo(BrokenBusinessInvariant))
}
}

// The set of transaction inputs is not empty.
fn check_ins_not_empty(tx_body: &TransactionBody) -> ValidationResult {
if tx_body.inputs.is_empty() {
Expand Down
3 changes: 3 additions & 0 deletions pallas-validate/src/utils/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ pub enum PostAlonzoError {

#[error("invalid script integrity hash")]
ScriptIntegrityHash,

#[error("transaction data does not satisfy business/CDDL invariant")]
BrokenBusinessInvariant,
}

pub type ValidationResult = Result<(), ValidationError>;
Loading