diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fda5a91..2bcd23c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Changed +- **aiken**: Fix validator selection for `apply`, `address` and `policy` commands. Parameters are also now correctly applied to all handlers of a given validator, instead of needing to be manually targetted one-by-one. @KtorZ - **aiken**: Add more flexibility around the management of Plutus blueprint files for `build`, `address`, `policy` and `apply` commands. See [#1055](https://github.com/aiken-lang/aiken/issues/1055). @KtorZ - **aiken**: Rename `--filter_traces` to `--trace_filter` for more consistency with `--trace_level`. An alias for `--filter_traces` still exists for backward compatibility. @KtorZ - **aiken-project**: Fix `aiken docs` wrongly formatting list constants as tuples. See [#1048](https://github.com/aiken-lang/aiken/issues/1048). @KtorZ diff --git a/crates/aiken-project/src/blueprint/mod.rs b/crates/aiken-project/src/blueprint/mod.rs index 7417bcc2..40219199 100644 --- a/crates/aiken-project/src/blueprint/mod.rs +++ b/crates/aiken-project/src/blueprint/mod.rs @@ -52,7 +52,7 @@ pub struct Compiler { #[derive(Debug, PartialEq, Clone)] pub enum LookupResult<'a, T> { - One(&'a T), + One(String, &'a T), Many, } @@ -91,17 +91,48 @@ impl Blueprint { } impl Blueprint { - pub fn lookup(&self, title: Option<&String>) -> Option> { + pub fn lookup( + &self, + want_module_name: Option<&str>, + want_validator_name: Option<&str>, + ) -> Option> { let mut validator = None; for v in self.validators.iter() { - let match_title = Some(&v.title) == title.or(Some(&v.title)); - if match_title { - validator = Some(if validator.is_none() { - LookupResult::One(v) - } else { - LookupResult::Many - }) + let mut split = v.title.split('.'); + + let known_module_name = split + .next() + .expect("validator's name must have two dot-separated components."); + + let known_validator_name = split + .next() + .expect("validator's name must have two dot-separated components."); + + let is_target = match (want_module_name, want_validator_name) { + (None, None) => true, + (Some(want_module_name), None) => want_module_name == known_module_name, + (None, Some(want_validator_name)) => want_validator_name == known_validator_name, + (Some(want_module_name), Some(want_validator_name)) => { + want_module_name == known_module_name + && want_validator_name == known_validator_name + } + }; + + let title = format!("{known_module_name}.{known_validator_name}"); + + if is_target { + match validator { + Some(LookupResult::Many) => (), + None => { + validator = Some(LookupResult::One(title, v)); + } + Some(LookupResult::One(ref known_title, _)) => { + if title.as_str() != known_title { + validator = Some(LookupResult::Many) + } + } + } } } @@ -110,16 +141,17 @@ impl Blueprint { pub fn with_validator( &self, - title: Option<&String>, + module_name: Option<&str>, + validator_name: Option<&str>, when_too_many: fn(Vec) -> E, when_missing: fn(Vec) -> E, action: F, ) -> Result where - F: Fn(Validator) -> Result, + F: Fn(&Validator) -> Result, { - match self.lookup(title) { - Some(LookupResult::One(validator)) => action(validator.to_owned()), + match self.lookup(module_name, validator_name) { + Some(LookupResult::One(_, validator)) => action(validator), Some(LookupResult::Many) => Err(when_too_many( self.validators.iter().map(|v| v.title.clone()).collect(), )), diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index edb2c8b9..846162e0 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -448,8 +448,9 @@ where pub fn address( &self, - title: Option<&String>, - stake_address: Option<&String>, + module_name: Option<&str>, + validator_name: Option<&str>, + stake_address: Option<&str>, blueprint_path: &Path, mainnet: bool, ) -> Result { @@ -481,35 +482,39 @@ where |known_validators| Error::MoreThanOneValidatorFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; - blueprint.with_validator(title, when_too_many, when_missing, |validator| { - // Make sure we're not calculating the address for a minting validator - if let Some(title) = title { - if !title.ends_with("else") && !title.ends_with("spend") { - return Err(blueprint::error::Error::UnexpectedMintingValidator.into()); - } - } + blueprint.with_validator( + module_name, + validator_name, + when_too_many, + when_missing, + |validator| { + let n = validator.parameters.len(); - let n = validator.parameters.len(); - - if n > 0 { - Err(blueprint::error::Error::ParameterizedValidator { n }.into()) - } else { - let network = if mainnet { - Network::Mainnet + if n > 0 { + Err(blueprint::error::Error::ParameterizedValidator { n }.into()) } else { - Network::Testnet - }; + let network = if mainnet { + Network::Mainnet + } else { + Network::Testnet + }; - Ok(validator.program.inner().address( - network, - delegation_part.to_owned(), - &self.config.plutus.into(), - )) - } - }) + Ok(validator.program.inner().address( + network, + delegation_part.to_owned(), + &self.config.plutus.into(), + )) + } + }, + ) } - pub fn policy(&self, title: Option<&String>, blueprint_path: &Path) -> Result { + pub fn policy( + &self, + module_name: Option<&str>, + validator_name: Option<&str>, + blueprint_path: &Path, + ) -> Result { // Read blueprint let blueprint = File::open(blueprint_path) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; @@ -520,19 +525,20 @@ where |known_validators| Error::MoreThanOneValidatorFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; - blueprint.with_validator(title, when_too_many, when_missing, |validator| { - // Make sure we're not calculating the policy for a spending validator - if validator.datum.is_some() { - return Err(blueprint::error::Error::UnexpectedSpendingValidator.into()); - } - - let n = validator.parameters.len(); - if n > 0 { - Err(blueprint::error::Error::ParameterizedValidator { n }.into()) - } else { - Ok(validator.program.compiled_code_and_hash().1) - } - }) + blueprint.with_validator( + module_name, + validator_name, + when_too_many, + when_missing, + |validator| { + let n = validator.parameters.len(); + if n > 0 { + Err(blueprint::error::Error::ParameterizedValidator { n }.into()) + } else { + Ok(validator.program.compiled_code_and_hash().1) + } + }, + ) } pub fn export(&self, module: &str, name: &str, tracing: Tracing) -> Result { @@ -571,7 +577,8 @@ where pub fn construct_parameter_incrementally( &self, - title: Option<&String>, + module_name: Option<&str>, + validator_name: Option<&str>, blueprint_path: &Path, ask: F, ) -> Result @@ -591,18 +598,25 @@ where |known_validators| Error::MoreThanOneValidatorFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; - let data = blueprint.with_validator(title, when_too_many, when_missing, |validator| { - validator - .ask_next_parameter(&blueprint.definitions, &ask) - .map_err(|e| e.into()) - })?; + let data = blueprint.with_validator( + module_name, + validator_name, + when_too_many, + when_missing, + |validator| { + validator + .ask_next_parameter(&blueprint.definitions, &ask) + .map_err(|e| e.into()) + }, + )?; Ok(data) } pub fn apply_parameter( &self, - title: Option<&String>, + module_name: Option<&str>, + validator_name: Option<&str>, blueprint_path: &Path, param: &PlutusData, ) -> Result { @@ -616,21 +630,28 @@ where |known_validators| Error::MoreThanOneValidatorFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; - let applied_validator = - blueprint.with_validator(title, when_too_many, when_missing, |validator| { + let applied_validator = blueprint.with_validator( + module_name, + validator_name, + when_too_many, + when_missing, + |validator| { validator + .clone() .apply(&blueprint.definitions, param) .map_err(|e| e.into()) - })?; + }, + )?; + + let prefix = |v: &str| v.split('.').take(2).collect::>().join("."); // Overwrite validator blueprint.validators = blueprint .validators .into_iter() .map(|validator| { - let same_title = validator.title == applied_validator.title; - if same_title { - applied_validator.to_owned() + if prefix(&applied_validator.title) == prefix(&validator.title) { + applied_validator.clone() } else { validator } diff --git a/crates/aiken-project/src/watch.rs b/crates/aiken-project/src/watch.rs index 89940668..201e2871 100644 --- a/crates/aiken-project/src/watch.rs +++ b/crates/aiken-project/src/watch.rs @@ -143,14 +143,16 @@ where return Err(ExitFailure::into_report()); } - eprintln!( - "{}", - Summary { - check_count: project.checks_count, - error_count: 0, - warning_count - } - ); + if project.checks_count.unwrap_or_default() + warning_count > 0 { + eprintln!( + "{}", + Summary { + check_count: project.checks_count, + error_count: 0, + warning_count + } + ); + } } if warning_count > 0 && deny { diff --git a/crates/aiken/src/cmd/blueprint/address.rs b/crates/aiken/src/cmd/blueprint/address.rs index 4589e9fc..955f5111 100644 --- a/crates/aiken/src/cmd/blueprint/address.rs +++ b/crates/aiken/src/cmd/blueprint/address.rs @@ -47,21 +47,10 @@ pub fn exec( }: Args, ) -> miette::Result<()> { with_project(directory.as_deref(), false, false, |p| { - let title = module.as_ref().map(|m| { - format!( - "{m}{}", - validator - .as_ref() - .map(|v| format!(".{v}")) - .unwrap_or_default() - ) - }); - - let title = title.as_ref().or(validator.as_ref()); - let address = p.address( - title, - delegated_to.as_ref(), + module.as_deref(), + validator.as_deref(), + delegated_to.as_deref(), p.blueprint_path(input.as_deref()).as_path(), mainnet, )?; diff --git a/crates/aiken/src/cmd/blueprint/apply.rs b/crates/aiken/src/cmd/blueprint/apply.rs index 335b4d8c..c18e675f 100644 --- a/crates/aiken/src/cmd/blueprint/apply.rs +++ b/crates/aiken/src/cmd/blueprint/apply.rs @@ -63,18 +63,6 @@ pub fn exec( }: Args, ) -> miette::Result<()> { with_project(None, false, false, |p| { - let title = module.as_ref().map(|m| { - format!( - "{m}{}", - validator - .as_ref() - .map(|v| format!(".{v}")) - .unwrap_or_default() - ) - }); - - let title = title.as_ref().or(validator.as_ref()); - eprintln!( "{} blueprint", " Analyzing" @@ -120,9 +108,12 @@ pub fn exec( }) } - None => { - p.construct_parameter_incrementally(title, &blueprint_input_path, ask_schema)? - } + None => p.construct_parameter_incrementally( + module.as_deref(), + validator.as_deref(), + &blueprint_input_path, + ask_schema, + )?, }; eprintln!( @@ -136,7 +127,12 @@ pub fn exec( } ); - let blueprint = p.apply_parameter(title, &blueprint_input_path, &data)?; + let blueprint = p.apply_parameter( + module.as_deref(), + validator.as_deref(), + &blueprint_input_path, + &data, + )?; let json = serde_json::to_string_pretty(&blueprint).unwrap(); diff --git a/crates/aiken/src/cmd/blueprint/convert.rs b/crates/aiken/src/cmd/blueprint/convert.rs index f5512b3a..1fba7fb9 100644 --- a/crates/aiken/src/cmd/blueprint/convert.rs +++ b/crates/aiken/src/cmd/blueprint/convert.rs @@ -40,18 +40,6 @@ pub fn exec( to, }: Args, ) -> miette::Result<()> { - let title = module.as_ref().map(|m| { - format!( - "{m}{}", - validator - .as_ref() - .map(|v| format!(".{v}")) - .unwrap_or_default() - ) - }); - - let title = title.as_ref().or(validator.as_ref()); - let project_path = if let Some(d) = directory { d } else { @@ -80,8 +68,12 @@ pub fn exec( |known_validators| ProjectError::MoreThanOneValidatorFound { known_validators }; let when_missing = |known_validators| ProjectError::NoValidatorNotFound { known_validators }; - let result = - blueprint.with_validator(title, when_too_many, when_missing, |validator| match to { + let result = blueprint.with_validator( + module.as_deref(), + validator.as_deref(), + when_too_many, + when_missing, + |validator| match to { Format::CardanoCli => { let cbor_bytes = validator.program.inner().to_cbor().unwrap(); @@ -99,7 +91,8 @@ pub fn exec( "cborHex": cbor_hex })) } - }); + }, + ); match result { Ok(value) => { diff --git a/crates/aiken/src/cmd/blueprint/hash.rs b/crates/aiken/src/cmd/blueprint/hash.rs index 6b84c530..41c76c5e 100644 --- a/crates/aiken/src/cmd/blueprint/hash.rs +++ b/crates/aiken/src/cmd/blueprint/hash.rs @@ -37,20 +37,9 @@ pub fn exec( }: Args, ) -> miette::Result<()> { with_project(directory.as_deref(), false, false, |p| { - let title = module.as_ref().map(|m| { - format!( - "{m}{}", - validator - .as_ref() - .map(|v| format!(".{v}")) - .unwrap_or_default() - ) - }); - - let title = title.as_ref().or(validator.as_ref()); - let address = p.address( - title, + module.as_deref(), + validator.as_deref(), None, p.blueprint_path(input.as_deref()).as_path(), false, diff --git a/crates/aiken/src/cmd/blueprint/policy.rs b/crates/aiken/src/cmd/blueprint/policy.rs index 1be8faf4..da510a39 100644 --- a/crates/aiken/src/cmd/blueprint/policy.rs +++ b/crates/aiken/src/cmd/blueprint/policy.rs @@ -37,19 +37,11 @@ pub fn exec( }: Args, ) -> miette::Result<()> { with_project(directory.as_deref(), false, false, |p| { - let title = module.as_ref().map(|m| { - format!( - "{m}{}", - validator - .as_ref() - .map(|v| format!(".{v}")) - .unwrap_or_default() - ) - }); - - let title = title.as_ref().or(validator.as_ref()); - - let policy = p.policy(title, p.blueprint_path(input.as_deref()).as_path())?; + let policy = p.policy( + module.as_deref(), + validator.as_deref(), + p.blueprint_path(input.as_deref()).as_path(), + )?; println!("{}", policy);