Add a blueprint policy command

Computes the policy ID of a minting policy; added guards for blueprint address to check that it's not a minting policy; Wasn't 100% sure where the errors should live, so I'm happy to move them if there's objections
This commit is contained in:
Pi Lanningham 2023-07-01 14:14:09 -04:00 committed by Lucas
parent 42544af799
commit 4a8cb72708
4 changed files with 117 additions and 2 deletions

View File

@ -49,6 +49,26 @@ pub enum Error {
))] ))]
ParameterizedValidator { n: usize }, ParameterizedValidator { n: usize },
#[error(
"I couldn't compute the address of the given validator because it's actually a minting policy!",
)]
#[diagnostic(code("aiken::blueprint::address::minting_validator"))]
#[diagnostic(help(
"I can only compute addresses for spending validators. Did you mean to call {blueprint_policy_command} instead?",
blueprint_policy_command = "blueprint policy".if_supports_color(Stdout, |s| s.purple()),
))]
UnexpectedMintingValidator,
#[error(
"I couldn't compute the policyId of the given validator because it's actually a spending policy!",
)]
#[diagnostic(code("aiken::blueprint::address::spending_validator"))]
#[diagnostic(help(
"I can only compute policyIds for minting validators. Did you mean to call {blueprint_address_command} instead?",
blueprint_address_command = "blueprint address".if_supports_color(Stdout, |s| s.purple()),
))]
UnexpectedSpendingValidator,
#[error("I stumble upon something else than a constant when I expected one.")] #[error("I stumble upon something else than a constant when I expected one.")]
#[diagnostic(code("aiken:blueprint::apply::malformed::argument"))] #[diagnostic(code("aiken:blueprint::apply::malformed::argument"))]
#[diagnostic(help( #[diagnostic(help(

View File

@ -27,9 +27,10 @@ use indexmap::IndexMap;
use miette::NamedSource; use miette::NamedSource;
use options::{CodeGenMode, Options}; use options::{CodeGenMode, Options};
use package_name::PackageName; use package_name::PackageName;
use pallas::ledger::addresses::{ use pallas::ledger::{addresses::{
Address, Network, ShelleyAddress, ShelleyDelegationPart, StakePayload, Address, Network, ShelleyAddress, ShelleyDelegationPart, StakePayload,
}; }, primitives::babbage::{self as cardano, PolicyId}};
use pallas_traverse::ComputeHash;
use script::{EvalHint, EvalInfo, Script}; use script::{EvalHint, EvalInfo, Script};
use std::{ use std::{
collections::HashMap, collections::HashMap,
@ -369,6 +370,11 @@ where
let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators };
blueprint.with_validator(title, when_too_many, when_missing, |validator| { blueprint.with_validator(title, when_too_many, when_missing, |validator| {
// Make sure we're not calculating the address for a minting validator
if validator.datum.is_none() {
return Err(blueprint::error::Error::UnexpectedMintingValidator.into());
}
let n = validator.parameters.len(); let n = validator.parameters.len();
if n > 0 { if n > 0 {
Err(blueprint::error::Error::ParameterizedValidator { n }.into()) Err(blueprint::error::Error::ParameterizedValidator { n }.into())
@ -380,6 +386,37 @@ where
}) })
} }
pub fn policy(
&self,
title: Option<&String>
) -> Result<PolicyId, Error> {
// Read blueprint
let blueprint = File::open(self.blueprint_path())
.map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?;
let blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?;
// Error handlers for ambiguous / missing validators
let when_too_many =
|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 {
let cbor = validator.program.to_cbor().unwrap();
let validator_hash = cardano::PlutusV2Script(cbor.into()).compute_hash();
Ok(validator_hash)
}
})
}
pub fn apply_parameter( pub fn apply_parameter(
&self, &self,
title: Option<&String>, title: Option<&String>,

View File

@ -1,4 +1,5 @@
pub mod address; pub mod address;
pub mod policy;
pub mod apply; pub mod apply;
pub mod convert; pub mod convert;
@ -8,6 +9,7 @@ use clap::Subcommand;
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum Cmd { pub enum Cmd {
Address(address::Args), Address(address::Args),
Policy(policy::Args),
Apply(apply::Args), Apply(apply::Args),
Convert(convert::Args), Convert(convert::Args),
} }
@ -15,6 +17,7 @@ pub enum Cmd {
pub fn exec(cmd: Cmd) -> miette::Result<()> { pub fn exec(cmd: Cmd) -> miette::Result<()> {
match cmd { match cmd {
Cmd::Address(args) => address::exec(args), Cmd::Address(args) => address::exec(args),
Cmd::Policy(args) => policy::exec(args),
Cmd::Apply(args) => apply::exec(args), Cmd::Apply(args) => apply::exec(args),
Cmd::Convert(args) => convert::exec(args), Cmd::Convert(args) => convert::exec(args),
} }

View File

@ -0,0 +1,55 @@
use crate::with_project;
use aiken_lang::ast::Tracing;
use std::path::PathBuf;
/// Compute a validator's address.
#[derive(clap::Args)]
pub struct Args {
/// Path to project
directory: Option<PathBuf>,
/// Name of the validator's module within the project. Optional if there's only one validator
#[clap(short, long)]
module: Option<String>,
/// Name of the validator within the module. Optional if there's only one validator
#[clap(short, long)]
validator: Option<String>,
/// Force the project to be rebuilt, otherwise relies on existing artifacts (i.e. plutus.json)
#[clap(long)]
rebuild: bool,
}
pub fn exec(
Args {
directory,
module,
validator,
rebuild,
}: Args,
) -> miette::Result<()> {
with_project(directory, |p| {
if rebuild {
p.build(false, Tracing::NoTraces)?;
}
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)?;
println!("{}", policy);
Ok(())
})
}