feat: Add multivalidator as an AIR tree opcode.
feat: Add uplc eval optimization
This commit is contained in:
parent
06ac851458
commit
dfce9c1d96
|
@ -132,6 +132,46 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let mut validator_args_tree =
|
let mut validator_args_tree =
|
||||||
self.check_validator_args(&fun.arguments, true, air_tree_fun, src_code, lines);
|
self.check_validator_args(&fun.arguments, true, air_tree_fun, src_code, lines);
|
||||||
|
|
||||||
|
if let Some(other) = other_fun {
|
||||||
|
let mut air_tree_fun_other = self.build(&other.body, module_name, &[]);
|
||||||
|
|
||||||
|
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
|
||||||
|
|
||||||
|
let validator_args_tree_other = self.check_validator_args(
|
||||||
|
&other.arguments,
|
||||||
|
true,
|
||||||
|
air_tree_fun_other,
|
||||||
|
src_code,
|
||||||
|
lines,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (spend, spend_name, mint, mint_name) =
|
||||||
|
if other.arguments.len() > fun.arguments.len() {
|
||||||
|
(
|
||||||
|
validator_args_tree_other,
|
||||||
|
other.name.clone(),
|
||||||
|
validator_args_tree,
|
||||||
|
fun.name.clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
validator_args_tree,
|
||||||
|
fun.name.clone(),
|
||||||
|
validator_args_tree_other,
|
||||||
|
other.name.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
validator_args_tree = AirTree::multi_validator(mint_name, mint, spend_name, spend);
|
||||||
|
|
||||||
|
// Special Case with multi_validators
|
||||||
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
|
||||||
|
|
||||||
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
validator_args_tree = AirTree::no_op(validator_args_tree);
|
validator_args_tree = AirTree::no_op(validator_args_tree);
|
||||||
|
|
||||||
let full_tree = self.hoist_functions_to_validator(validator_args_tree);
|
let full_tree = self.hoist_functions_to_validator(validator_args_tree);
|
||||||
|
@ -142,48 +182,6 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
let mut term = self.uplc_code_gen(full_vec);
|
let mut term = self.uplc_code_gen(full_vec);
|
||||||
|
|
||||||
if let Some(other) = other_fun {
|
|
||||||
self.reset(false);
|
|
||||||
|
|
||||||
let mut air_tree_fun_other = self.build(&other.body, module_name, &[]);
|
|
||||||
|
|
||||||
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
|
|
||||||
|
|
||||||
let mut validator_args_tree_other = self.check_validator_args(
|
|
||||||
&other.arguments,
|
|
||||||
true,
|
|
||||||
air_tree_fun_other,
|
|
||||||
src_code,
|
|
||||||
lines,
|
|
||||||
);
|
|
||||||
|
|
||||||
validator_args_tree_other = AirTree::no_op(validator_args_tree_other);
|
|
||||||
|
|
||||||
let full_tree_other = self.hoist_functions_to_validator(validator_args_tree_other);
|
|
||||||
|
|
||||||
// optimizations on air tree
|
|
||||||
|
|
||||||
let full_vec_other = full_tree_other.to_vec();
|
|
||||||
|
|
||||||
let other_term = self.uplc_code_gen(full_vec_other);
|
|
||||||
|
|
||||||
let (spend, spend_name, mint, mint_name) =
|
|
||||||
if other.arguments.len() > fun.arguments.len() {
|
|
||||||
(other_term, other.name.clone(), term, fun.name.clone())
|
|
||||||
} else {
|
|
||||||
(term, fun.name.clone(), other_term, other.name.clone())
|
|
||||||
};
|
|
||||||
|
|
||||||
// Special Case with multi_validators
|
|
||||||
self.special_functions
|
|
||||||
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
|
|
||||||
|
|
||||||
self.special_functions
|
|
||||||
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
|
|
||||||
|
|
||||||
term = wrap_as_multi_validator(spend, mint, self.tracing, spend_name, mint_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
term = cast_validator_args(term, params);
|
term = cast_validator_args(term, params);
|
||||||
|
|
||||||
self.finalize(term)
|
self.finalize(term)
|
||||||
|
@ -5700,6 +5698,24 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Air::NoOp => None,
|
Air::NoOp => None,
|
||||||
|
Air::MultiValidator {
|
||||||
|
two_arg_name,
|
||||||
|
three_arg_name,
|
||||||
|
} => {
|
||||||
|
let two_arg = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
let three_arg = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
let term = wrap_as_multi_validator(
|
||||||
|
three_arg,
|
||||||
|
two_arg,
|
||||||
|
self.tracing,
|
||||||
|
three_arg_name,
|
||||||
|
two_arg_name,
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(term)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,4 +224,8 @@ pub enum Air {
|
||||||
NoOp,
|
NoOp,
|
||||||
FieldsEmpty,
|
FieldsEmpty,
|
||||||
ListEmpty,
|
ListEmpty,
|
||||||
|
MultiValidator {
|
||||||
|
two_arg_name: String,
|
||||||
|
three_arg_name: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,6 +402,12 @@ pub enum AirTree {
|
||||||
then: Box<AirTree>,
|
then: Box<AirTree>,
|
||||||
},
|
},
|
||||||
// End Expressions
|
// End Expressions
|
||||||
|
MultiValidator {
|
||||||
|
two_arg_name: String,
|
||||||
|
two_arg: Box<AirTree>,
|
||||||
|
three_arg_name: String,
|
||||||
|
three_arg: Box<AirTree>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AirTree {
|
impl AirTree {
|
||||||
|
@ -1001,26 +1007,19 @@ impl AirTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn hoist_over(mut self, next_exp: AirTree) -> AirTree {
|
pub fn multi_validator(
|
||||||
// match &mut self {
|
two_arg_name: String,
|
||||||
// AirTree::Statement { hoisted_over, .. } => {
|
two_arg: AirTree,
|
||||||
// assert!(hoisted_over.is_none());
|
three_arg_name: String,
|
||||||
// *hoisted_over = Some(next_exp.into());
|
three_arg: AirTree,
|
||||||
// self
|
) -> AirTree {
|
||||||
// }
|
AirTree::MultiValidator {
|
||||||
|
two_arg_name,
|
||||||
// AirTree::Expression(_) => {
|
two_arg: two_arg.into(),
|
||||||
// unreachable!("Trying to hoist an expression onto an expression.")
|
three_arg_name,
|
||||||
// }
|
three_arg: three_arg.into(),
|
||||||
// AirTree::UnhoistedSequence(seq) => {
|
}
|
||||||
// let mut final_exp = next_exp;
|
}
|
||||||
// while let Some(assign) = seq.pop() {
|
|
||||||
// final_exp = assign.hoist_over(final_exp);
|
|
||||||
// }
|
|
||||||
// final_exp
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn expect_on_list() -> AirTree {
|
pub fn expect_on_list() -> AirTree {
|
||||||
let list_var = AirTree::local_var("__list_to_check", list(data()));
|
let list_var = AirTree::local_var("__list_to_check", list(data()));
|
||||||
|
@ -1595,6 +1594,20 @@ impl AirTree {
|
||||||
msg.create_air_vec(air_vec);
|
msg.create_air_vec(air_vec);
|
||||||
then.create_air_vec(air_vec);
|
then.create_air_vec(air_vec);
|
||||||
}
|
}
|
||||||
|
AirTree::MultiValidator {
|
||||||
|
two_arg,
|
||||||
|
three_arg,
|
||||||
|
two_arg_name,
|
||||||
|
three_arg_name,
|
||||||
|
} => {
|
||||||
|
air_vec.push(Air::MultiValidator {
|
||||||
|
two_arg_name: two_arg_name.clone(),
|
||||||
|
three_arg_name: three_arg_name.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
two_arg.create_air_vec(air_vec);
|
||||||
|
three_arg.create_air_vec(air_vec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,6 +1662,7 @@ impl AirTree {
|
||||||
| AirTree::FieldsEmpty { then, .. }
|
| AirTree::FieldsEmpty { then, .. }
|
||||||
| AirTree::ListEmpty { then, .. }
|
| AirTree::ListEmpty { then, .. }
|
||||||
| AirTree::NoOp { then } => then.return_type(),
|
| AirTree::NoOp { then } => then.return_type(),
|
||||||
|
AirTree::MultiValidator { .. } => void(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1725,7 +1739,8 @@ impl AirTree {
|
||||||
| AirTree::Fn { .. }
|
| AirTree::Fn { .. }
|
||||||
| AirTree::UnOp { .. }
|
| AirTree::UnOp { .. }
|
||||||
| AirTree::WrapClause { .. }
|
| AirTree::WrapClause { .. }
|
||||||
| AirTree::Finally { .. } => vec![],
|
| AirTree::Finally { .. }
|
||||||
|
| AirTree::MultiValidator { .. } => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2055,7 +2070,8 @@ impl AirTree {
|
||||||
| AirTree::Constr { .. }
|
| AirTree::Constr { .. }
|
||||||
| AirTree::RecordUpdate { .. }
|
| AirTree::RecordUpdate { .. }
|
||||||
| AirTree::ErrorTerm { .. }
|
| AirTree::ErrorTerm { .. }
|
||||||
| AirTree::Trace { .. } => {}
|
| AirTree::Trace { .. }
|
||||||
|
| AirTree::MultiValidator { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !apply_with_func_last {
|
if !apply_with_func_last {
|
||||||
|
@ -2711,6 +2727,27 @@ impl AirTree {
|
||||||
apply_with_func_last,
|
apply_with_func_last,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
AirTree::MultiValidator {
|
||||||
|
two_arg_name: _,
|
||||||
|
two_arg,
|
||||||
|
three_arg_name: _,
|
||||||
|
three_arg,
|
||||||
|
} => {
|
||||||
|
two_arg.do_traverse_tree_with(
|
||||||
|
tree_path,
|
||||||
|
current_depth + 1,
|
||||||
|
Fields::SecondField,
|
||||||
|
with,
|
||||||
|
apply_with_func_last,
|
||||||
|
);
|
||||||
|
three_arg.do_traverse_tree_with(
|
||||||
|
tree_path,
|
||||||
|
current_depth + 1,
|
||||||
|
Fields::FourthField,
|
||||||
|
with,
|
||||||
|
apply_with_func_last,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if apply_with_func_last {
|
if apply_with_func_last {
|
||||||
|
@ -3077,6 +3114,16 @@ impl AirTree {
|
||||||
| AirTree::ErrorTerm { .. } => {
|
| AirTree::ErrorTerm { .. } => {
|
||||||
panic!("A tree node with no children was encountered with a longer tree path.")
|
panic!("A tree node with no children was encountered with a longer tree path.")
|
||||||
}
|
}
|
||||||
|
AirTree::MultiValidator {
|
||||||
|
two_arg_name: _,
|
||||||
|
two_arg,
|
||||||
|
three_arg_name: _,
|
||||||
|
three_arg,
|
||||||
|
} => match field {
|
||||||
|
Fields::SecondField => two_arg.as_mut().do_find_air_tree_node(tree_path_iter),
|
||||||
|
Fields::FourthField => three_arg.as_mut().do_find_air_tree_node(tree_path_iter),
|
||||||
|
_ => panic!("Tree Path index outside tree children nodes"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub fn aiken_optimize_and_intern(program: Program<Name>) -> Program<Name> {
|
||||||
.inline_reducer()
|
.inline_reducer()
|
||||||
.force_delay_reducer()
|
.force_delay_reducer()
|
||||||
.cast_data_reducer()
|
.cast_data_reducer()
|
||||||
|
.builtin_eval_reducer()
|
||||||
.convert_arithmetic_ops()
|
.convert_arithmetic_ops()
|
||||||
.builtin_curry_reducer()
|
.builtin_curry_reducer()
|
||||||
.lambda_reducer()
|
.lambda_reducer()
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
|
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
|
||||||
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
|
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
|
||||||
builtins::DefaultFunction,
|
builtins::DefaultFunction,
|
||||||
|
machine::cost_model::ExBudget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::interner::CodeGenInterner;
|
use super::interner::CodeGenInterner;
|
||||||
|
@ -360,7 +361,7 @@ pub enum BuiltinArgs {
|
||||||
|
|
||||||
impl BuiltinArgs {
|
impl BuiltinArgs {
|
||||||
fn args_from_arg_stack(stack: Vec<(usize, Term<Name>)>, func: DefaultFunction) -> Self {
|
fn args_from_arg_stack(stack: Vec<(usize, Term<Name>)>, func: DefaultFunction) -> Self {
|
||||||
let error_safe = func.is_error_safe(&stack.iter().map(|(_, term)| term).collect_vec());
|
let error_safe = false;
|
||||||
|
|
||||||
let mut ordered_arg_stack = stack.into_iter().sorted_by(|(_, arg1), (_, arg2)| {
|
let mut ordered_arg_stack = stack.into_iter().sorted_by(|(_, arg1), (_, arg2)| {
|
||||||
// sort by constant first if the builtin is order agnostic
|
// sort by constant first if the builtin is order agnostic
|
||||||
|
@ -986,6 +987,27 @@ impl Term<Name> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pierce_no_inlines<'a>(&'a self) -> &'a Self {
|
||||||
|
let mut term = self;
|
||||||
|
loop {
|
||||||
|
match term {
|
||||||
|
Term::Lambda {
|
||||||
|
parameter_name,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
if parameter_name.as_ref().text == NO_INLINE {
|
||||||
|
term = body;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
term
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program<Name> {
|
impl Program<Name> {
|
||||||
|
@ -1708,6 +1730,56 @@ impl Program<Name> {
|
||||||
|
|
||||||
step_b
|
step_b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn builtin_eval_reducer(self) -> Self {
|
||||||
|
let mut applied_ids = vec![];
|
||||||
|
|
||||||
|
self.traverse_uplc_with(false, &mut |id, term, arg_stack, _scope| match term {
|
||||||
|
Term::Builtin(func) => {
|
||||||
|
let args = arg_stack
|
||||||
|
.iter()
|
||||||
|
.map(|(_, term)| term.pierce_no_inlines())
|
||||||
|
.collect_vec();
|
||||||
|
if func.can_curry_builtin()
|
||||||
|
&& arg_stack.len() == func.arity()
|
||||||
|
&& func.is_error_safe(&args)
|
||||||
|
{
|
||||||
|
let applied_term =
|
||||||
|
arg_stack
|
||||||
|
.into_iter()
|
||||||
|
.fold(Term::Builtin(*func), |acc, item| {
|
||||||
|
applied_ids.push(item.0);
|
||||||
|
acc.apply(item.1.pierce_no_inlines().clone())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check above for is error safe
|
||||||
|
let eval_term: Term<Name> = Program {
|
||||||
|
version: (1, 0, 0),
|
||||||
|
term: applied_term,
|
||||||
|
}
|
||||||
|
.to_named_debruijn()
|
||||||
|
.unwrap()
|
||||||
|
.eval(ExBudget::max())
|
||||||
|
.result()
|
||||||
|
.unwrap()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
*term = eval_term;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Apply { function, .. } => {
|
||||||
|
let id = id.unwrap();
|
||||||
|
|
||||||
|
if applied_ids.contains(&id) {
|
||||||
|
*term = function.as_ref().clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Constr { .. } => todo!(),
|
||||||
|
Term::Case { .. } => todo!(),
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_vec_function_to_var(func_name: &str, id_vec: &[usize]) -> String {
|
fn id_vec_function_to_var(func_name: &str, id_vec: &[usize]) -> String {
|
||||||
|
|
|
@ -13,4 +13,4 @@ requirements = []
|
||||||
source = "github"
|
source = "github"
|
||||||
|
|
||||||
[etags]
|
[etags]
|
||||||
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1719353073, nanos_since_epoch = 642619000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]
|
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1721828369, nanos_since_epoch = 230591000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]
|
||||||
|
|
|
@ -13,4 +13,4 @@ requirements = []
|
||||||
source = "github"
|
source = "github"
|
||||||
|
|
||||||
[etags]
|
[etags]
|
||||||
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1719352929, nanos_since_epoch = 982815000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]
|
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1721828402, nanos_since_epoch = 276492000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"plutusVersion": "v2",
|
"plutusVersion": "v2",
|
||||||
"compiler": {
|
"compiler": {
|
||||||
"name": "Aiken",
|
"name": "Aiken",
|
||||||
"version": "v1.0.29-alpha+e856fc6"
|
"version": "v1.0.29-alpha+06ac851"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validators": [
|
"validators": [
|
||||||
|
@ -31,8 +31,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"compiledCode": "590347010000323232323232323232323223222323232322533300c323232533300f3007301137540022646464a66602c002020264a66602e603400426464a66602a601a602e6ea803854ccc054c8cc004004018894ccc06c004528099299980c19baf301e301b3754603c00402629444cc00c00c004c07800454ccc054c0300044cdc78010088a5015330164901536578706563740a202020202020202020206c6973742e616e7928696e707574732c20666e28696e70757429207b20696e7075742e6f75747075745f7265666572656e6365203d3d207574786f5f726566207d2900161533016491046275726e0016375a602e0046eb8c054004044c060004c94ccc048c024c050dd50008a5eb7bdb1804dd5980c180a9baa001323233001001323300100137566034603660366036603600a44a666032002297adef6c6013232323253330193372291100002153330193371e9101000021003100513301e337606ea4008dd3000998030030019bab301b003375c6032004603a004603600244a666030002298103d87a800013232323253330183372200e0042a66603066e3c01c0084cdd2a40006603a6e980052f5c02980103d87a8000133006006003375660340066eb8c060008c070008c068004dd7180b980a1baa0033758602c00260246ea800854cc041241236578706563742074782e4d696e7428706f6c6963795f696429203d20707572706f73650016301430150023013001300f37540022930a99806a491856616c696461746f722072657475726e65642066616c7365001365632533300b30030011533300f300e37540082930050a99980598010008a99980798071baa0041498028028c030dd50019b8748008dc3a4000a66666601e002200200c00c00c00c6eb800454cc00d24018f657870656374205b506169722861737365745f6e616d652c20616d6f756e74295d203d0a2020202020206d696e740a20202020202020207c3e2076616c75652e66726f6d5f6d696e7465645f76616c75650a20202020202020207c3e2076616c75652e746f6b656e7328706f6c6963795f6964290a20202020202020207c3e20646963742e746f5f70616972732829001615330024910c72646d723a20416374696f6e00165734ae7155ceaab9e5573eae815d0aba257481",
|
"compiledCode": "5901e4010000323232323232323223222323232322533300a323232533300d3007300e37540022646464a6660260022c264a666028602e00426464a666026601a60286ea803854ccc04cc8cc004004018894ccc060004528099299980b19baf301b30183754603600402629444cc00c00c004c06c00454ccc04cc0300044cdc78010088a501616375a60280046eb8c04800458c054004c94ccc040c024c044dd50008a5eb7bdb1804dd5980a98091baa00132323300100132330010013756602e603060306030603000a44a66602c002297adef6c60132323232533301733722910100002153330173371e9101000021003100513301b337606ea4008dd3000998030030019bab3018003375c602c0046034004603000244a66602a002298103d87a800013232323253330163372200e0042a66602c66e3c01c0084cdd2a4000660346e980052f5c02980103d87a80001330060060033756602e0066eb8c054008c064008c05c004dd7180a18089baa00337586026002601e6ea800858c044c048008c040004c030dd50008a4c26cac64a66601260060022a66601860166ea8010526161533300930020011533300c300b37540082930b0b18049baa003370e90011b87480014cccccc030004400458585858dd7000ab9a5573aaae7955cfaba05742ae895d201",
|
||||||
"hash": "e8c0b6d7c88bce7578f598ed61f172854b78a78c4ec251b45d6da4c4"
|
"hash": "41f948bc5dcda5f8813cc42ffde5c8d981f37be1475a5d242d44e2fd"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "spend2.backtrace",
|
"title": "spend2.backtrace",
|
||||||
|
@ -48,8 +48,8 @@
|
||||||
"$ref": "#/definitions/Void"
|
"$ref": "#/definitions/Void"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"compiledCode": "5901680100003232323232323232323232232322322533300953330093005300b375464660020026eb0c040c044c044c034dd5180818069baa00222533300f00114c0103d87a800013232533300d4a2266e952000330120024bd70099802002000980980118088008a51153300a4914765787065637420536f6d65285f29203d206c6973742e66696e6428636f6e746578742e7472616e73616374696f6e2e6f7574707574732c20666e285f29207b2054727565207d290016149854cc0292411856616c696461746f722072657475726e65642066616c73650013656533333300f001153330073003300937540022a66601660146ea8004526005005005005005005533333300d002153330053001300737540042a66601260106ea8008526004004004004004004370e90000a99801a4810f5f72656465656d65723a20566f6964001615330024910c5f646174756d3a20566f696400165734ae7155ceaab9e5573eae815d0aba257481",
|
"compiledCode": "58c401000032323232323232322323223225333007533300730053008375464660020026eb0c034c038c038c028dd5180698051baa00222533300c00114c0103d87a800013232533300b4a2266e9520003300f0024bd70099802002000980800118070008a511614984d9594cccccc03000454ccc014c00cc018dd50008a99980418039baa00114985858585858594cccccc02800854ccc00cc004c010dd50010a99980318029baa0021498585858585858dc3a4000ae6955ceaab9e5573eae815d0aba25749",
|
||||||
"hash": "f86c88144df93f3925cf048d1b50615fa95249f063a07242f70a6bd8"
|
"hash": "419ffec4259b90352f8fde4cd451f98d01132f6cb7b1c382c9a3e810"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
Loading…
Reference in New Issue