Merge branch 'main' into lang
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "aiken"
|
||||
description = "Cardano smart contract language and toolchain"
|
||||
version = "0.0.11"
|
||||
version = "0.0.13"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/txpipe/aiken"
|
||||
homepage = "https://github.com/txpipe/aiken"
|
||||
@@ -13,4 +13,12 @@ authors = ["Lucas Rosa <x@rvcas.dev>", "Kasey White <kwhitemsg@gmail.com>"]
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
clap = { version = "3.1.14", features = ["derive"] }
|
||||
uplc = { path = '../uplc', version = "0.0.11" }
|
||||
hex = "0.4.3"
|
||||
pallas-addresses = "0.14.0-alpha.3"
|
||||
pallas-codec = "0.14.0-alpha.3"
|
||||
pallas-crypto = "0.14.0-alpha.3"
|
||||
pallas-primitives = "0.14.0-alpha.3"
|
||||
pallas-traverse = "0.14.0-alpha.3"
|
||||
serde = { version = "1.0.144", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
uplc = { path = '../uplc', version = "0.0.13" }
|
||||
|
||||
@@ -16,22 +16,58 @@ pub enum Args {
|
||||
/// Project name
|
||||
name: PathBuf,
|
||||
},
|
||||
/// A subcommand for working with transactions
|
||||
#[clap(subcommand)]
|
||||
Tx(TxCommand),
|
||||
/// A subcommand for working with Untyped Plutus Core
|
||||
#[clap(subcommand)]
|
||||
Uplc(UplcCommand),
|
||||
}
|
||||
|
||||
/// Commands for working with transactions
|
||||
#[derive(Subcommand)]
|
||||
pub enum TxCommand {
|
||||
/// Simulate a transaction by evaluating it's script
|
||||
Simulate {
|
||||
/// A file containing cbor hex for a transaction
|
||||
input: PathBuf,
|
||||
|
||||
/// Toggle whether input is raw cbor or a hex string
|
||||
#[clap(short, long)]
|
||||
cbor: bool,
|
||||
|
||||
/// A file containing cbor hex for the raw inputs
|
||||
raw_inputs: PathBuf,
|
||||
|
||||
/// A file containing cbor hex for the raw outputs
|
||||
raw_outputs: PathBuf,
|
||||
|
||||
/// Time between each slot
|
||||
#[clap(short, long, default_value_t = 1000)]
|
||||
slot_length: u64,
|
||||
|
||||
/// Time of shelley hardfork
|
||||
#[clap(long, default_value_t = 1596059091000)]
|
||||
zero_time: u64,
|
||||
|
||||
/// Slot number at the start of the shelley hardfork
|
||||
#[clap(long, default_value_t = 4492800)]
|
||||
zero_slot: u64,
|
||||
},
|
||||
}
|
||||
|
||||
/// Commands for working with Untyped Plutus Core
|
||||
#[derive(Subcommand)]
|
||||
pub enum UplcCommand {
|
||||
/// Evaluate an Untyped Plutus Core program
|
||||
Eval {
|
||||
/// Handle input as flat bytes
|
||||
script: PathBuf,
|
||||
|
||||
#[clap(short, long)]
|
||||
flat: bool,
|
||||
|
||||
/// File to load and evaluate
|
||||
input: PathBuf,
|
||||
/// Arguments to pass to the uplc program
|
||||
args: Vec<String>,
|
||||
},
|
||||
/// Encode textual Untyped Plutus Core to flat bytes
|
||||
Flat {
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
use std::{fmt::Write as _, fs};
|
||||
|
||||
use pallas_primitives::{
|
||||
babbage::{TransactionInput, TransactionOutput},
|
||||
Fragment,
|
||||
};
|
||||
use pallas_traverse::{Era, MultiEraTx};
|
||||
use uplc::{
|
||||
ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
|
||||
machine::cost_model::ExBudget,
|
||||
parser,
|
||||
tx::{
|
||||
self,
|
||||
script_context::{ResolvedInput, SlotConfig},
|
||||
},
|
||||
};
|
||||
|
||||
mod args;
|
||||
|
||||
use args::{Args, UplcCommand};
|
||||
use args::{Args, TxCommand, UplcCommand};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = Args::default();
|
||||
@@ -39,38 +48,142 @@ fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
Args::Uplc(uplc) => match uplc {
|
||||
UplcCommand::Flat { input, print, out } => {
|
||||
Args::Tx(tx_cmd) => match tx_cmd {
|
||||
TxCommand::Simulate {
|
||||
input,
|
||||
cbor,
|
||||
raw_inputs,
|
||||
raw_outputs,
|
||||
slot_length,
|
||||
zero_time,
|
||||
zero_slot,
|
||||
} => {
|
||||
let (tx_bytes, inputs_bytes, outputs_bytes) = if cbor {
|
||||
(
|
||||
fs::read(input)?,
|
||||
fs::read(raw_inputs)?,
|
||||
fs::read(raw_outputs)?,
|
||||
)
|
||||
} else {
|
||||
let cbor_hex = fs::read_to_string(input)?;
|
||||
let inputs_hex = fs::read_to_string(raw_inputs)?;
|
||||
let outputs_hex = fs::read_to_string(raw_outputs)?;
|
||||
|
||||
(
|
||||
hex::decode(cbor_hex.trim())?,
|
||||
hex::decode(inputs_hex.trim())?,
|
||||
hex::decode(outputs_hex.trim())?,
|
||||
)
|
||||
};
|
||||
|
||||
let tx = MultiEraTx::decode(Era::Babbage, &tx_bytes)
|
||||
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))?;
|
||||
|
||||
let inputs = Vec::<TransactionInput>::decode_fragment(&inputs_bytes).unwrap();
|
||||
let outputs = Vec::<TransactionOutput>::decode_fragment(&outputs_bytes).unwrap();
|
||||
|
||||
let resolved_inputs: Vec<ResolvedInput> = inputs
|
||||
.iter()
|
||||
.zip(outputs.iter())
|
||||
.map(|(input, output)| ResolvedInput {
|
||||
input: input.clone(),
|
||||
output: output.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
println!("Simulating: {}", tx.hash());
|
||||
|
||||
if let Some(tx_babbage) = tx.as_babbage() {
|
||||
let slot_config = SlotConfig {
|
||||
zero_time,
|
||||
zero_slot,
|
||||
slot_length,
|
||||
};
|
||||
|
||||
let result = tx::eval_phase_two(
|
||||
tx_babbage,
|
||||
&resolved_inputs,
|
||||
None,
|
||||
None,
|
||||
&slot_config,
|
||||
true,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(redeemers) => {
|
||||
println!("\nTotal Budget Used\n-----------------\n");
|
||||
|
||||
let total_budget_used = redeemers.iter().fold(
|
||||
ExBudget { mem: 0, cpu: 0 },
|
||||
|accum, curr| ExBudget {
|
||||
mem: accum.mem + curr.ex_units.mem as i64,
|
||||
cpu: accum.cpu + curr.ex_units.steps as i64,
|
||||
},
|
||||
);
|
||||
|
||||
println!("mem: {}", total_budget_used.mem);
|
||||
println!("cpu: {}", total_budget_used.cpu);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("\nError\n-----\n\n{}\n", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Args::Uplc(uplc_cmd) => match uplc_cmd {
|
||||
UplcCommand::Flat {
|
||||
input,
|
||||
print,
|
||||
out,
|
||||
cbor_hex,
|
||||
} => {
|
||||
let code = std::fs::read_to_string(&input)?;
|
||||
|
||||
let program = parser::program(&code)?;
|
||||
|
||||
let program = Program::<DeBruijn>::try_from(program)?;
|
||||
|
||||
let bytes = program.to_flat()?;
|
||||
if cbor_hex {
|
||||
let bytes = program.to_flat()?;
|
||||
|
||||
if print {
|
||||
let mut output = String::new();
|
||||
if print {
|
||||
let mut output = String::new();
|
||||
|
||||
for (i, byte) in bytes.iter().enumerate() {
|
||||
let _ = write!(output, "{:08b}", byte);
|
||||
for (i, byte) in bytes.iter().enumerate() {
|
||||
let _ = write!(output, "{:08b}", byte);
|
||||
|
||||
if (i + 1) % 4 == 0 {
|
||||
output.push('\n');
|
||||
} else {
|
||||
output.push(' ');
|
||||
if (i + 1) % 4 == 0 {
|
||||
output.push('\n');
|
||||
} else {
|
||||
output.push(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}", output);
|
||||
} else {
|
||||
let out_name = if let Some(out) = out {
|
||||
out
|
||||
println!("{}", output);
|
||||
} else {
|
||||
format!("{}.flat", input.file_stem().unwrap().to_str().unwrap())
|
||||
};
|
||||
let out_name = if let Some(out) = out {
|
||||
out
|
||||
} else {
|
||||
format!("{}.flat", input.file_stem().unwrap().to_str().unwrap())
|
||||
};
|
||||
|
||||
fs::write(&out_name, &bytes)?;
|
||||
fs::write(&out_name, &bytes)?;
|
||||
}
|
||||
} else {
|
||||
let cbor = program.to_hex()?;
|
||||
|
||||
if print {
|
||||
println!("{}", &cbor);
|
||||
} else {
|
||||
let out_name = if let Some(out) = out {
|
||||
out
|
||||
} else {
|
||||
format!("{}.cbor", input.file_stem().unwrap().to_str().unwrap())
|
||||
};
|
||||
|
||||
fs::write(&out_name, &cbor)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +200,24 @@ fn main() -> anyhow::Result<()> {
|
||||
fs::write(&input, pretty)?;
|
||||
}
|
||||
}
|
||||
UplcCommand::Unflat {
|
||||
input,
|
||||
print,
|
||||
out,
|
||||
cbor_hex,
|
||||
} => {
|
||||
let program = if cbor_hex {
|
||||
let cbor = std::fs::read_to_string(&input)?;
|
||||
|
||||
UplcCommand::Unflat { input, print, out } => {
|
||||
let bytes = std::fs::read(&input)?;
|
||||
let mut cbor_buffer = Vec::new();
|
||||
let mut flat_buffer = Vec::new();
|
||||
|
||||
let program = Program::<DeBruijn>::from_flat(&bytes)?;
|
||||
Program::<DeBruijn>::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer)?
|
||||
} else {
|
||||
let bytes = std::fs::read(&input)?;
|
||||
|
||||
Program::<DeBruijn>::from_flat(&bytes)?
|
||||
};
|
||||
|
||||
let program: Program<Name> = program.try_into()?;
|
||||
|
||||
@@ -110,21 +236,27 @@ fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
UplcCommand::Eval { input, flat } => {
|
||||
let program = if flat {
|
||||
let bytes = std::fs::read(&input)?;
|
||||
UplcCommand::Eval { script, flat, args } => {
|
||||
let mut program = if flat {
|
||||
let bytes = std::fs::read(&script)?;
|
||||
|
||||
let prog = Program::<FakeNamedDeBruijn>::from_flat(&bytes)?;
|
||||
|
||||
prog.into()
|
||||
} else {
|
||||
let code = std::fs::read_to_string(&input)?;
|
||||
let code = std::fs::read_to_string(&script)?;
|
||||
|
||||
let prog = parser::program(&code)?;
|
||||
|
||||
Program::<NamedDeBruijn>::try_from(prog)?
|
||||
};
|
||||
|
||||
for arg in args {
|
||||
let term: Term<NamedDeBruijn> = parser::term(&arg)?.try_into()?;
|
||||
|
||||
program = program.apply_term(&term);
|
||||
}
|
||||
|
||||
let (term, cost, logs) = program.eval();
|
||||
|
||||
match term {
|
||||
@@ -149,7 +281,10 @@ fn main() -> anyhow::Result<()> {
|
||||
"\nBudget\n------\ncpu: {}\nmemory: {}\n",
|
||||
cost.cpu, cost.mem
|
||||
);
|
||||
println!("\nLogs\n----\n{}", logs.join("\n"))
|
||||
|
||||
if !logs.is_empty() {
|
||||
println!("\nLogs\n----\n{}", logs.join("\n"))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user