Add PlutusV3 conformance tests and also control budgets

There were some odd discrepancy for `integerToByteString` on the mem
  side. Either 1 or about 1000 mem units off; which I couldn't quite
  figure out. Yet, it proves useful to validate builtin at large and
  ensure we have a valid cost model for v3.
This commit is contained in:
KtorZ
2024-08-14 02:42:04 +02:00
parent f879f6d183
commit 2cb87f4f8f
2893 changed files with 6385 additions and 13 deletions

View File

@@ -1,11 +1,12 @@
use pallas_primitives::conway::Language;
use std::{
ffi::OsStr,
fs,
path::{Path, PathBuf},
};
use uplc::{
ast::{Name, NamedDeBruijn, Program},
machine::cost_model::ExBudget,
parser,
};
use walkdir::WalkDir;
@@ -25,7 +26,30 @@ fn expected_to_program(expected_file: &PathBuf) -> Result<Program<Name>, String>
}
}
fn actual_evaluation_result(file: &Path) -> Result<Program<Name>, String> {
fn file_to_budget(expected_budget_file: &PathBuf) -> Result<ExBudget, String> {
fs::read_to_string(expected_budget_file)
.map_err(|e| format!("failed to read .uplc.budget.expected file: {e:?}"))
.and_then(|src| budget::ex_budget(&src).map_err(|e| format!("{e:?}")))
}
peg::parser! {
grammar budget() for str {
pub rule ex_budget() -> ExBudget
= "({" _* "cpu" _* ":" _* cpu:decimal() _* "|" _* "mem" _* ":" _* mem:decimal() _* "})" _* {
ExBudget { cpu, mem }
}
rule decimal() -> i64
= n:$(['0'..='9']+) {? n.parse().or(Err("decimal")) }
rule _ = [' ' | '\n' | '\r' | '\t'] / "--" $([^ '\n']*) "\n"
}
}
fn actual_evaluation_result(
file: &Path,
language: &Language,
) -> Result<(Program<Name>, ExBudget), String> {
let code = fs::read_to_string(file).expect("Failed to read .uplc file");
let program = parser::program(&code).map_err(|_| PARSE_ERROR.to_string())?;
@@ -36,30 +60,56 @@ fn actual_evaluation_result(file: &Path) -> Result<Program<Name>, String> {
let version = program.version;
let term = program
.eval(Default::default())
.result()
.map_err(|_| EVALUATION_FAILURE.to_string())?;
let eval = program.eval_version(Default::default(), language);
let cost = eval.cost();
let term = eval.result().map_err(|_| EVALUATION_FAILURE.to_string())?;
let program = Program { version, term };
Ok(program.try_into().unwrap())
Ok((program.try_into().unwrap(), cost))
}
#[test]
fn evaluation() {
let root = "test_data/conformance/evaluation";
fn plutus_conformance_tests(language: Language) {
let root = format!(
"test_data/conformance/{}",
match language {
Language::PlutusV1 => "v1",
Language::PlutusV2 => "v2",
Language::PlutusV3 => "v3",
}
);
for entry in WalkDir::new(root).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.extension().and_then(OsStr::to_str) == Some("uplc") {
let expected_file = path.with_extension("uplc.expected");
let expected_budget_file = path.with_extension("uplc.budget.expected");
let actual = actual_evaluation_result(path);
let eval = actual_evaluation_result(path, &language);
let expected = expected_to_program(&expected_file);
pretty_assertions::assert_eq!(expected, actual, "{}", path.display());
match eval {
Ok((actual, cost)) => {
pretty_assertions::assert_eq!(expected, Ok(actual), "{}", path.display());
if let Ok(budget) = file_to_budget(&expected_budget_file) {
pretty_assertions::assert_eq!(budget, cost, "{}", path.display());
}
}
Err(err) => pretty_assertions::assert_eq!(expected, Err(err), "{}", path.display()),
}
}
}
}
#[test]
fn plutus_conformance_tests_v2() {
plutus_conformance_tests(Language::PlutusV2)
}
#[test]
fn plutus_conformance_tests_v3() {
plutus_conformance_tests(Language::PlutusV3)
}