feat: output build assets
This commit is contained in:
@@ -69,6 +69,8 @@ impl UntypedModule {
|
||||
pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;
|
||||
pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
|
||||
|
||||
pub type TypedFunction = Function<Arc<Type>, TypedExpr>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Function<T, Expr> {
|
||||
pub arguments: Vec<Arg<T>>,
|
||||
|
||||
@@ -185,7 +185,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn recurse_scope_level(&mut self, body: &TypedExpr, scope_level: ScopeLevels) {
|
||||
match dbg!(body) {
|
||||
match body {
|
||||
TypedExpr::Int { .. } => {}
|
||||
TypedExpr::String { .. } => {}
|
||||
TypedExpr::ByteArray { .. } => {}
|
||||
@@ -393,7 +393,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
value: &TypedExpr,
|
||||
scope_level: ScopeLevels,
|
||||
) {
|
||||
match dbg!(pattern) {
|
||||
match pattern {
|
||||
Pattern::Int { .. } | Pattern::String { .. } | Pattern::Var { .. } => {
|
||||
self.recurse_scope_level(value, scope_level);
|
||||
}
|
||||
@@ -403,11 +403,11 @@ impl<'a> CodeGenerator<'a> {
|
||||
Pattern::Discard { .. } => todo!(),
|
||||
Pattern::List { .. } => todo!(),
|
||||
Pattern::Constructor {
|
||||
name: constructor_name,
|
||||
// name: constructor_name,
|
||||
tipo,
|
||||
arguments,
|
||||
constructor,
|
||||
module,
|
||||
// arguments,
|
||||
// constructor,
|
||||
// module,
|
||||
..
|
||||
} => {
|
||||
self.recurse_scope_level(value, scope_level.scope_increment_sequence(1));
|
||||
@@ -428,17 +428,17 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
}
|
||||
Type::Fn { .. } => {
|
||||
let mapping_index = match constructor {
|
||||
tipo::PatternConstructor::Record { name, field_map } => {
|
||||
if let Some(fields_mapping) = field_map {
|
||||
fields_mapping.fields.clone()
|
||||
} else {
|
||||
HashMap::new()
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut args = arguments.clone();
|
||||
let local_var_name = "";
|
||||
// let mapping_index = match constructor {
|
||||
// tipo::PatternConstructor::Record { name, field_map } => {
|
||||
// if let Some(fields_mapping) = field_map {
|
||||
// fields_mapping.fields.clone()
|
||||
// } else {
|
||||
// HashMap::new()
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// let mut args = arguments.clone();
|
||||
// let local_var_name = "";
|
||||
// arguments.iter().map(|x| {
|
||||
// let name = match &x.value {
|
||||
// Pattern::Var { location, name } => {
|
||||
@@ -480,7 +480,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
|
||||
fn recurse_code_gen(&mut self, body: &TypedExpr, scope_level: ScopeLevels) -> Term<Name> {
|
||||
match dbg!(body) {
|
||||
match body {
|
||||
TypedExpr::Int { value, .. } => {
|
||||
Term::Constant(Constant::Integer(value.parse::<i128>().unwrap()))
|
||||
}
|
||||
@@ -1090,6 +1090,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
}
|
||||
|
||||
// Pull out all uplc data holder and data usage, filter by Scope Level, Sort By Scope Depth, Then Apply
|
||||
#[allow(clippy::type_complexity)]
|
||||
let mut data_holder: Vec<((String, String, String), (bool, ScopeLevels, u64))> = self
|
||||
.uplc_data_usage_holder_lookup
|
||||
.iter()
|
||||
|
||||
@@ -16,7 +16,10 @@ miette = { version = "5.3.0", features = ["fancy"] }
|
||||
petgraph = "0.6.2"
|
||||
regex = "1.6.0"
|
||||
serde = { version = "1.0.144", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
serde_json = { version = "1.0.85", features = ["preserve_order"] }
|
||||
thiserror = "1.0.37"
|
||||
toml = "0.5.9"
|
||||
walkdir = "2.3.2"
|
||||
hex = "0.4.3"
|
||||
pallas = "0.14.0"
|
||||
pallas-traverse = "0.14.0"
|
||||
|
||||
@@ -8,15 +8,22 @@ pub mod config;
|
||||
pub mod error;
|
||||
pub mod format;
|
||||
pub mod module;
|
||||
pub mod script;
|
||||
|
||||
use aiken_lang::{
|
||||
ast::{Definition, Function, ModuleKind},
|
||||
ast::{Definition, Function, ModuleKind, TypedFunction},
|
||||
builtins,
|
||||
tipo::TypeInfo,
|
||||
uplc::CodeGenerator,
|
||||
IdGenerator,
|
||||
};
|
||||
use uplc::ast::{NamedDeBruijn, Program};
|
||||
use pallas::{
|
||||
codec::minicbor,
|
||||
ledger::{addresses::Address, primitives::babbage},
|
||||
};
|
||||
use pallas_traverse::ComputeHash;
|
||||
use script::Script;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
@@ -88,7 +95,9 @@ impl Project {
|
||||
let scripts = self.validate_scripts(&mut checked_modules)?;
|
||||
|
||||
if uplc_gen {
|
||||
self.code_gen(scripts, &checked_modules)?;
|
||||
let programs = self.code_gen(scripts, &checked_modules)?;
|
||||
|
||||
self.write_build_outputs(programs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -238,27 +247,19 @@ impl Project {
|
||||
fn validate_scripts(
|
||||
&self,
|
||||
checked_modules: &mut CheckedModules,
|
||||
) -> Result<Vec<CheckedModule>, Error> {
|
||||
) -> Result<Vec<(String, TypedFunction)>, Error> {
|
||||
let mut errors = Vec::new();
|
||||
let mut scripts = Vec::new();
|
||||
let mut indices_to_remove = Vec::new();
|
||||
|
||||
for module in checked_modules.scripts() {
|
||||
scripts.push(module.clone());
|
||||
|
||||
for def in module.ast.definitions() {
|
||||
if let Definition::Fn(Function {
|
||||
arguments,
|
||||
location,
|
||||
name,
|
||||
return_type,
|
||||
..
|
||||
}) = def
|
||||
{
|
||||
if VALIDATOR_NAMES.contains(&name.as_str()) {
|
||||
for (index, def) in module.ast.definitions().enumerate() {
|
||||
if let Definition::Fn(func_def) = def {
|
||||
if VALIDATOR_NAMES.contains(&func_def.name.as_str()) {
|
||||
// validators must return a Bool
|
||||
if !return_type.is_bool() {
|
||||
if !func_def.return_type.is_bool() {
|
||||
errors.push(Error::ValidatorMustReturnBool {
|
||||
location: *location,
|
||||
location: func_def.location,
|
||||
src: module.code.clone(),
|
||||
path: module.input_path.clone(),
|
||||
})
|
||||
@@ -266,35 +267,40 @@ impl Project {
|
||||
|
||||
// depending on name, validate the minimum number of arguments
|
||||
// if too low, push a new error on to errors
|
||||
if [MINT, CERT, WITHDRAWL].contains(&name.as_str()) && arguments.len() < 2 {
|
||||
if [MINT, CERT, WITHDRAWL].contains(&func_def.name.as_str())
|
||||
&& func_def.arguments.len() < 2
|
||||
{
|
||||
errors.push(Error::WrongValidatorArity {
|
||||
location: *location,
|
||||
location: func_def.location,
|
||||
src: module.code.clone(),
|
||||
path: module.input_path.clone(),
|
||||
name: name.clone(),
|
||||
name: func_def.name.clone(),
|
||||
at_least: 2,
|
||||
})
|
||||
}
|
||||
|
||||
if SPEND == name && arguments.len() < 3 {
|
||||
if SPEND == func_def.name && func_def.arguments.len() < 3 {
|
||||
errors.push(Error::WrongValidatorArity {
|
||||
location: *location,
|
||||
location: func_def.location,
|
||||
src: module.code.clone(),
|
||||
path: module.input_path.clone(),
|
||||
name: name.clone(),
|
||||
name: func_def.name.clone(),
|
||||
at_least: 3,
|
||||
})
|
||||
}
|
||||
|
||||
scripts.push((module.name.clone(), func_def.clone()));
|
||||
indices_to_remove.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for index in indices_to_remove.drain(0..) {
|
||||
module.ast.definitions.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
for script in &scripts {
|
||||
checked_modules.remove(&script.name);
|
||||
}
|
||||
|
||||
Ok(scripts)
|
||||
} else {
|
||||
Err(Error::List(errors))
|
||||
@@ -303,9 +309,9 @@ impl Project {
|
||||
|
||||
fn code_gen(
|
||||
&mut self,
|
||||
scripts: Vec<CheckedModule>,
|
||||
scripts: Vec<(String, TypedFunction)>,
|
||||
checked_modules: &CheckedModules,
|
||||
) -> Result<Vec<Program<NamedDeBruijn>>, Error> {
|
||||
) -> Result<Vec<Script>, Error> {
|
||||
let mut programs = Vec::new();
|
||||
let mut functions = HashMap::new();
|
||||
let mut type_aliases = HashMap::new();
|
||||
@@ -335,35 +341,106 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
for script in scripts {
|
||||
for def in script.ast.into_definitions() {
|
||||
if let Definition::Fn(Function {
|
||||
arguments,
|
||||
name,
|
||||
body,
|
||||
..
|
||||
}) = def
|
||||
{
|
||||
if VALIDATOR_NAMES.contains(&name.as_str()) {
|
||||
let mut generator = CodeGenerator::new(
|
||||
&functions,
|
||||
// &type_aliases,
|
||||
&data_types,
|
||||
// &imports,
|
||||
// &constants,
|
||||
);
|
||||
for (module_name, func_def) in scripts {
|
||||
let Function {
|
||||
arguments,
|
||||
name,
|
||||
body,
|
||||
..
|
||||
} = func_def;
|
||||
|
||||
let program = generator.generate(body, arguments);
|
||||
let mut generator = CodeGenerator::new(
|
||||
&functions,
|
||||
// &type_aliases,
|
||||
&data_types,
|
||||
// &imports,
|
||||
// &constants,
|
||||
);
|
||||
|
||||
programs.push(program.try_into().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
let program = generator.generate(body, arguments);
|
||||
|
||||
let script = Script::new(module_name, name, program.try_into().unwrap());
|
||||
|
||||
programs.push(script);
|
||||
}
|
||||
|
||||
Ok(programs)
|
||||
}
|
||||
|
||||
fn write_build_outputs(&self, programs: Vec<Script>) -> Result<(), Error> {
|
||||
let assets = self.root.join("assets");
|
||||
|
||||
for script in programs {
|
||||
let script_output_dir = assets.join(script.module).join(script.name);
|
||||
|
||||
fs::create_dir_all(&script_output_dir)?;
|
||||
|
||||
let cbor = script.program.to_cbor().unwrap();
|
||||
|
||||
// Create file containing just the script cbor hex
|
||||
let script_path = script_output_dir.join("script.txt");
|
||||
|
||||
let cbor_hex = hex::encode(&cbor);
|
||||
|
||||
fs::write(script_path, &cbor_hex)?;
|
||||
|
||||
// Create the payment script JSON file
|
||||
let payment_script_path = script_output_dir.join("payment_script.json");
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
let mut encoder = minicbor::Encoder::new(&mut bytes);
|
||||
|
||||
encoder.bytes(&cbor).unwrap();
|
||||
|
||||
let prefixed_cbor_hex = hex::encode(&bytes);
|
||||
|
||||
let payment_script = json!({
|
||||
"type": "PlutusScriptV2",
|
||||
"description": "Generated by Aiken",
|
||||
"cborHex": prefixed_cbor_hex
|
||||
});
|
||||
|
||||
fs::write(
|
||||
payment_script_path,
|
||||
serde_json::to_string_pretty(&payment_script).unwrap(),
|
||||
)?;
|
||||
|
||||
// Create mainnet and testnet addresses
|
||||
let plutus_script = babbage::PlutusV2Script(cbor.into());
|
||||
|
||||
let hash = plutus_script.compute_hash();
|
||||
|
||||
// mainnet
|
||||
let mainnet_path = script_output_dir.join("mainnet.txt");
|
||||
let mut mainnet_bytes: Vec<u8> = vec![0b01110001];
|
||||
|
||||
mainnet_bytes.extend(hash.iter());
|
||||
|
||||
let mainnet_addr = Address::from_bytes(&mainnet_bytes)
|
||||
.unwrap()
|
||||
.to_bech32()
|
||||
.unwrap();
|
||||
|
||||
fs::write(mainnet_path, mainnet_addr)?;
|
||||
|
||||
// testnet
|
||||
let testnet_path = script_output_dir.join("testnet.txt");
|
||||
let mut testnet_bytes: Vec<u8> = vec![0b01110000];
|
||||
|
||||
testnet_bytes.extend(hash.iter());
|
||||
|
||||
let testnet_addr = Address::from_bytes(&testnet_bytes)
|
||||
.unwrap()
|
||||
.to_bech32()
|
||||
.unwrap();
|
||||
|
||||
fs::write(testnet_path, testnet_addr)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {
|
||||
let paths = walkdir::WalkDir::new(dir)
|
||||
.follow_links(true)
|
||||
|
||||
@@ -181,8 +181,8 @@ impl From<CheckedModules> for HashMap<String, CheckedModule> {
|
||||
}
|
||||
|
||||
impl CheckedModules {
|
||||
pub fn scripts(&self) -> impl Iterator<Item = &CheckedModule> {
|
||||
self.0.values().filter(|module| module.kind.is_script())
|
||||
pub fn scripts(&mut self) -> impl Iterator<Item = &mut CheckedModule> {
|
||||
self.0.values_mut().filter(|module| module.kind.is_script())
|
||||
}
|
||||
|
||||
pub fn into_scripts(self) -> impl Iterator<Item = CheckedModule> {
|
||||
|
||||
18
crates/project/src/script.rs
Normal file
18
crates/project/src/script.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use uplc::ast::{NamedDeBruijn, Program};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Script {
|
||||
pub module: String,
|
||||
pub name: String,
|
||||
pub program: Program<NamedDeBruijn>,
|
||||
}
|
||||
|
||||
impl Script {
|
||||
pub fn new(module: String, name: String, program: Program<NamedDeBruijn>) -> Script {
|
||||
Script {
|
||||
module,
|
||||
name,
|
||||
program,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user