Merge branch 'main' into waalge/bump-cargo2nix

This commit is contained in:
waalge 2023-03-09 09:44:11 +00:00
commit 6817510e2d
21 changed files with 781 additions and 239 deletions

View File

@ -6,6 +6,12 @@
- **aiken**: new command `blueprint convert`
### Changed
- **aiken-lang**: block `Data` and `String` from unifying when casting
- **aiken-lang**: remove ability for a type with many variants with matching field labels and types to support field access
- **aiken-project**: tests filtering with `-m` during check now happens in `Project::collect_tests`
## [v0.0.29] - 2023-MM-DD
### Added

View File

@ -117,6 +117,11 @@ pub enum Air {
constr_index: usize,
},
AssertBool {
scope: Vec<u64>,
is_true: bool,
},
// When
When {
scope: Vec<u64>,
@ -268,6 +273,7 @@ impl Air {
| Air::UnWrapData { scope, .. }
| Air::WrapData { scope, .. }
| Air::AssertConstr { scope, .. }
| Air::AssertBool { scope, .. }
| Air::When { scope, .. }
| Air::Clause { scope, .. }
| Air::ListClause { scope, .. }
@ -375,6 +381,7 @@ impl Air {
| Air::Let { .. }
| Air::WrapClause { .. }
| Air::AssertConstr { .. }
| Air::AssertBool { .. }
| Air::Finally { .. }
| Air::FieldsExpose { .. } => None,

View File

@ -1200,7 +1200,7 @@ pub fn find_and_replace_generics(tipo: &mut Arc<Type>, mono_types: &IndexMap<u64
}
}
pub fn get_generics_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Arc<Type>)> {
pub fn get_generic_id_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Arc<Type>)> {
let mut generics_ids = vec![];
if let Some(id) = tipo.get_generic() {
@ -1213,7 +1213,7 @@ pub fn get_generics_and_type(tipo: &Type, param: &Type) -> Vec<(u64, Arc<Type>)>
.iter()
.zip(param.get_inner_types().iter())
{
generics_ids.append(&mut get_generics_and_type(tipo, param_type));
generics_ids.append(&mut get_generic_id_and_type(tipo, param_type));
}
generics_ids
}
@ -1990,7 +1990,7 @@ pub fn replace_opaque_type(t: &mut Arc<Type>, data_types: IndexMap<DataTypeKey,
for (tipo, param) in new_type_fields.iter().zip(t.arg_types().unwrap()) {
let mut map = mono_types.into_iter().collect_vec();
map.append(&mut get_generics_and_type(tipo, &param));
map.append(&mut get_generic_id_and_type(tipo, &param));
mono_types = map.into_iter().collect();
}

View File

@ -177,6 +177,7 @@ impl<'a> Environment<'a> {
},
);
}
Ok(Some(fields))
}
@ -1289,6 +1290,7 @@ impl<'a> Environment<'a> {
&& !(t1.is_unbound() || t2.is_unbound())
&& !(t1.is_function() || t2.is_function())
&& !(t1.is_generic() || t2.is_generic())
&& !(t1.is_string() || t2.is_string())
{
return Ok(());
}
@ -1779,44 +1781,22 @@ fn get_compatible_record_fields<A>(
) -> Vec<(usize, &str, &Annotation)> {
let mut compatible = vec![];
if constructors.len() > 1 {
return compatible;
}
let first = match constructors.get(0) {
Some(first) => first,
None => return compatible,
};
'next_argument: for (index, first_argument) in first.arguments.iter().enumerate() {
for (index, first_argument) in first.arguments.iter().enumerate() {
// Fields without labels do not have accessors
let label = match first_argument.label.as_ref() {
Some(label) => label.as_str(),
None => continue 'next_argument,
None => continue,
};
// Check each variant to see if they have an field in the same position
// with the same label and the same type
for constructor in constructors.iter().skip(1) {
// The field must exist in all variants
let argument = match constructor.arguments.get(index) {
Some(argument) => argument,
None => continue 'next_argument,
};
// The labels must be the same
if argument.label != first_argument.label {
continue 'next_argument;
}
// The types must be the same
if !argument
.annotation
.is_logically_equal(&first_argument.annotation)
{
continue 'next_argument;
}
}
// The previous loop did not find any incompatible fields in the other
// variants so this field is compatible across variants and we should
// generate an accessor for it.
compatible.push((index, label, &first_argument.annotation))
}

View File

@ -264,8 +264,9 @@ You can use '{discard}' and numbers to distinguish between similar names.
},
#[error(
"I saw a {} fields in a context where there should be {}.\n",
"I saw {} field{} in a context where there should be {}.\n",
given.if_supports_color(Stdout, |s| s.purple()),
if *given <= 1 { "" } else { "s"},
expected.if_supports_color(Stdout, |s| s.purple()),
)]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types"))]
@ -744,7 +745,7 @@ Perhaps, try the following:
#[diagnostic(code("unknown::record_field"))]
#[diagnostic(help(
"{}",
suggest_neighbor(label, fields.iter(), "Did you forget to make it public?")
suggest_neighbor(label, fields.iter(), "Did you forget to make it public?\n\nAlso record access is only supported on types with one constructor.")
))]
UnknownRecordField {
#[label]

View File

@ -26,7 +26,7 @@ use crate::{
builder::{
check_replaceable_opaque_type, check_when_pattern_needs, constants_ir,
convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor,
get_generics_and_type, handle_clause_guard, handle_func_dependencies_ir,
get_generic_id_and_type, handle_clause_guard, handle_func_dependencies_ir,
handle_recursion_ir, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize,
rearrange_clauses, replace_opaque_type, wrap_validator_args, AssignmentProperties,
ClauseProperties, DataTypeKey, FuncComponents, FunctionAccessKey,
@ -2056,14 +2056,26 @@ impl<'a> CodeGenerator<'a> {
scope: scope.clone(),
check_last_item: false,
});
} else {
} else if !tipo.is_bool() {
pattern_vec.push(Air::Let {
scope: scope.clone(),
name: "_".to_string(),
});
}
if matches!(assignment_properties.kind, AssignmentKind::Expect) {
match assignment_properties.kind {
AssignmentKind::Let => {
pattern_vec.append(values);
}
AssignmentKind::Expect => {
if tipo.is_bool() {
pattern_vec.push(Air::AssertBool {
scope,
is_true: constr_name == "True",
});
pattern_vec.append(values);
} else {
let data_type =
lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap();
@ -2114,8 +2126,8 @@ impl<'a> CodeGenerator<'a> {
name: constr_name,
variant_name: String::new(),
});
} else {
pattern_vec.append(values);
}
}
}
pattern_vec.append(&mut nested_pattern);
@ -3275,17 +3287,23 @@ impl<'a> CodeGenerator<'a> {
let param_types = constructor.tipo.arg_types().unwrap();
let mut mono_types: IndexMap<u64, Arc<Type>> = IndexMap::new();
let mut map = mono_types.into_iter().collect_vec();
for (index, arg) in function.arguments.iter().enumerate() {
if arg.tipo.is_generic() {
let mut map = mono_types.into_iter().collect_vec();
let param_type = &param_types[index];
map.append(&mut get_generics_and_type(&arg.tipo, param_type));
map.append(&mut get_generic_id_and_type(&arg.tipo, param_type));
}
}
if function.return_type.is_generic() {
if let Type::Fn { ret, .. } = &*constructor.tipo {
map.append(&mut get_generic_id_and_type(&function.return_type, ret))
}
}
mono_types = map.into_iter().collect();
}
}
let (variant_name, func_ir) =
monomorphize(func_ir, mono_types, &constructor.tipo);
@ -3347,23 +3365,32 @@ impl<'a> CodeGenerator<'a> {
} else if let (Some(function), Type::Fn { .. }) =
(function, &*tipo)
{
let param_types = tipo.arg_types().unwrap();
let mut mono_types: IndexMap<u64, Arc<Type>> =
IndexMap::new();
let param_types = tipo.arg_types().unwrap();
let mut map = mono_types.into_iter().collect_vec();
for (index, arg) in function.arguments.iter().enumerate() {
if arg.tipo.is_generic() {
let mut map = mono_types.into_iter().collect_vec();
map.append(&mut get_generics_and_type(
&arg.tipo,
&param_types[index],
let param_type = &param_types[index];
map.append(&mut get_generic_id_and_type(
&arg.tipo, param_type,
));
}
}
if function.return_type.is_generic() {
if let Type::Fn { ret, .. } = &*constructor.tipo {
map.append(&mut get_generic_id_and_type(
&function.return_type,
ret,
))
}
}
mono_types = map.into_iter().collect();
}
}
let mut func_ir = vec![];
self.build_ir(&function.body, &mut func_ir, scope.to_vec());
@ -4310,7 +4337,12 @@ impl<'a> CodeGenerator<'a> {
arg_vec.push(arg_stack.pop().unwrap());
}
for arg in arg_vec.iter() {
for (index, arg) in arg_vec.into_iter().enumerate() {
let arg = if matches!(func, DefaultFunction::ChooseData) && index > 0 {
Term::Delay(arg.into())
} else {
arg
};
term = apply_wrap(term, arg.clone());
}
@ -4425,6 +4457,58 @@ impl<'a> CodeGenerator<'a> {
DefaultFunction::MkCons => {
unimplemented!("Use brackets instead.");
}
DefaultFunction::IfThenElse
| DefaultFunction::ChooseList
| DefaultFunction::Trace => unimplemented!("{func:#?}"),
DefaultFunction::ChooseData => {
let temp_vars = (0..func.arity())
.into_iter()
.map(|_| format!("__item_{}", self.id_gen.next()))
.collect_vec();
if count == 0 {
for (index, temp_var) in temp_vars.iter().enumerate() {
term = apply_wrap(
term,
if index > 0 {
Term::Delay(
Term::Var(
Name {
text: temp_var.clone(),
unique: 0.into(),
}
.into(),
)
.into(),
)
} else {
Term::Var(
Name {
text: temp_var.clone(),
unique: 0.into(),
}
.into(),
)
},
);
}
}
term = term.force_wrap();
if count == 0 {
for temp_var in temp_vars.into_iter().rev() {
term = Term::Lambda {
parameter_name: Name {
text: temp_var,
unique: 0.into(),
}
.into(),
body: term.into(),
};
}
}
}
_ => {}
}
arg_stack.push(term);
@ -4828,7 +4912,7 @@ impl<'a> CodeGenerator<'a> {
Term::Builtin(DefaultFunction::Trace).force_wrap(),
Term::Constant(
UplcConstant::String(
"Asserted on incorrect constructor variant.".to_string(),
"Expected on incorrect constructor variant.".to_string(),
)
.into(),
),
@ -4851,6 +4935,31 @@ impl<'a> CodeGenerator<'a> {
arg_stack.push(term);
}
Air::AssertBool { is_true, .. } => {
let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let error_term = apply_wrap(
apply_wrap(
Term::Builtin(DefaultFunction::Trace).force_wrap(),
Term::Constant(
UplcConstant::String(
"Expected on incorrect boolean variant.".to_string(),
)
.into(),
),
),
Term::Delay(Term::Error.into()),
)
.force_wrap();
if is_true {
term = delayed_if_else(value, term, error_term);
} else {
term = delayed_if_else(value, error_term, term);
}
arg_stack.push(term);
}
Air::When {
subject_name, tipo, ..
} => {

View File

@ -250,21 +250,7 @@ impl Annotated<Schema> {
Err(Error::new(ErrorContext::UnboundTypeVariable, type_info))
}
},
Type::Tuple { elems } => match &elems[..] {
[left, right] => {
let left = Annotated::from_type(modules, left, type_parameters)?
.into_data(left)
.map_err(|e| e.backtrack(type_info))?;
let right = Annotated::from_type(modules, right, type_parameters)?
.into_data(right)
.map_err(|e| e.backtrack(type_info))?;
Ok(Schema::Data(Data::List(Items::Many(vec![
left.annotated,
right.annotated,
])))
.into())
}
_ => {
Type::Tuple { elems } => {
let elems = elems
.iter()
.map(|e| {
@ -273,13 +259,13 @@ impl Annotated<Schema> {
})
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.backtrack(type_info))?;
Ok(Annotated {
title: Some("Tuple".to_owned()),
description: None,
annotated: Schema::Data(Data::List(Items::Many(elems))),
})
}
},
Type::Fn { .. } => Err(Error::new(ErrorContext::UnexpectedFunction, type_info)),
}
}

View File

@ -470,6 +470,7 @@ mod test {
"title": "test_module.spend",
"hash": "3c6766e7a36df2aa13c0e9e6e071317ed39d05f405771c4f1a81c6cc",
"datum": {
"title": "Tuple",
"schema": {
"dataType": "list",
"items": [

View File

@ -575,22 +575,40 @@ fn new_timestamp() -> Duration {
}
fn find_modules_prefix(modules: &[DocLink]) -> String {
modules
do_find_modules_prefix("", modules)
}
fn do_find_modules_prefix(current_prefix: &str, modules: &[DocLink]) -> String {
let prefix = modules
.iter()
.fold(None, |previous_prefix, module| {
let prefix = module
.name
.split('/')
.next()
.unwrap_or_default()
.to_string();
let name = module.name.strip_prefix(current_prefix).unwrap_or_default();
let name = if name.starts_with('/') {
name.strip_prefix('/').unwrap_or_default()
} else {
name
};
let prefix = name.split('/').next().unwrap_or_default().to_string();
match previous_prefix {
None if prefix != module.name => Some(prefix),
Some(..) if Some(prefix) == previous_prefix => previous_prefix,
_ => Some(String::new()),
}
})
.unwrap_or_default()
.unwrap_or_default();
if prefix.is_empty() {
current_prefix.to_string()
} else {
let mut current_prefix = current_prefix.to_owned();
if !current_prefix.is_empty() {
current_prefix.push('/');
}
current_prefix.push_str(&prefix);
do_find_modules_prefix(&current_prefix, modules)
}
}
#[test]
@ -602,7 +620,7 @@ fn find_modules_prefix_test() {
name: "aiken/list".to_string(),
path: String::new()
}]),
"aiken".to_string()
"aiken/list".to_string()
);
assert_eq!(
@ -620,7 +638,7 @@ fn find_modules_prefix_test() {
path: String::new()
},
DocLink {
name: "aiken/byte_array".to_string(),
name: "aiken/bytearray".to_string(),
path: String::new(),
}
]),
@ -634,7 +652,56 @@ fn find_modules_prefix_test() {
path: String::new()
},
DocLink {
name: "foo/byte_array".to_string(),
name: "foo/bytearray".to_string(),
path: String::new(),
}
]),
"".to_string()
);
}
#[test]
fn find_modules_prefix_test_2() {
assert_eq!(
find_modules_prefix(&[
DocLink {
name: "aiken/trees/bst".to_string(),
path: String::new()
},
DocLink {
name: "aiken/trees/mt".to_string(),
path: String::new(),
}
]),
"aiken/trees".to_string()
);
assert_eq!(
find_modules_prefix(&[
DocLink {
name: "aiken/trees/bst".to_string(),
path: String::new()
},
DocLink {
name: "aiken/trees/mt".to_string(),
path: String::new(),
},
DocLink {
name: "aiken/sequences".to_string(),
path: String::new(),
}
]),
"aiken".to_string()
);
assert_eq!(
find_modules_prefix(&[
DocLink {
name: "aiken".to_string(),
path: String::new()
},
DocLink {
name: "aiken/prelude".to_string(),
path: String::new(),
}
]),

View File

@ -175,7 +175,10 @@ where
let doc_files = docs::generate_all(
&self.root,
&self.config,
self.checked_modules.values().collect(),
self.checked_modules
.values()
.filter(|CheckedModule { package, .. }| package == &self.config.name.to_string())
.collect(),
);
for file in doc_files {
@ -288,13 +291,13 @@ where
verbose,
exact_match,
} => {
let tests = self.collect_tests(verbose)?;
let tests = self.collect_tests(verbose, match_tests, exact_match)?;
if !tests.is_empty() {
self.event_listener.handle_event(Event::RunningTests);
}
let results = self.eval_scripts(tests, match_tests, exact_match);
let results = self.eval_scripts(tests);
let errors: Vec<Error> = results
.iter()
@ -586,20 +589,84 @@ where
Ok(())
}
fn collect_tests(&mut self, verbose: bool) -> Result<Vec<Script>, Error> {
fn collect_tests(
&mut self,
verbose: bool,
match_tests: Option<Vec<String>>,
exact_match: bool,
) -> Result<Vec<Script>, Error> {
let mut scripts = Vec::new();
for module in self.checked_modules.values() {
if module.package != self.config.name.to_string() {
let match_tests = match_tests.map(|mt| {
mt.into_iter()
.map(|match_test| {
let mut match_split_dot = match_test.split('.');
let match_module = if match_test.contains('.') || match_test.contains('/') {
match_split_dot.next().unwrap_or("")
} else {
""
};
let match_names = match_split_dot.next().map(|names| {
let names = names.replace(&['{', '}'][..], "");
let names_split_comma = names.split(',');
names_split_comma.map(str::to_string).collect()
});
(match_module.to_string(), match_names)
})
.collect::<Vec<(String, Option<Vec<String>>)>>()
});
for checked_module in self.checked_modules.values() {
if checked_module.package != self.config.name.to_string() {
continue;
}
for def in module.ast.definitions() {
for def in checked_module.ast.definitions() {
if let Definition::Test(func) = def {
scripts.push((module.input_path.clone(), module.name.clone(), func))
if let Some(match_tests) = &match_tests {
let is_match = match_tests.iter().any(|(module, names)| {
let matched_module =
module.is_empty() || checked_module.name.contains(module);
let matched_name = match names {
None => true,
Some(names) => names.iter().any(|name| {
if exact_match {
name == &func.name
} else {
func.name.contains(name)
}
}),
};
matched_module && matched_name
});
if is_match {
scripts.push((
checked_module.input_path.clone(),
checked_module.name.clone(),
func,
))
}
} else {
scripts.push((
checked_module.input_path.clone(),
checked_module.name.clone(),
func,
))
}
}
}
}
let mut programs = Vec::new();
for (input_path, module_name, func_def) in scripts {
let Function {
arguments,
@ -660,12 +727,7 @@ where
Ok(programs)
}
fn eval_scripts(
&self,
scripts: Vec<Script>,
match_tests: Option<Vec<String>>,
exact_match: bool,
) -> Vec<EvalInfo> {
fn eval_scripts(&self, scripts: Vec<Script>) -> Vec<EvalInfo> {
use rayon::prelude::*;
// TODO: in the future we probably just want to be able to
@ -675,55 +737,6 @@ where
cpu: i64::MAX,
};
let scripts = if let Some(match_tests) = match_tests {
let match_tests: Vec<(&str, Option<Vec<String>>)> = match_tests
.iter()
.map(|match_test| {
let mut match_split_dot = match_test.split('.');
let match_module = if match_test.contains('.') || match_test.contains('/') {
match_split_dot.next().unwrap_or("")
} else {
""
};
let match_names = match_split_dot.next().map(|names| {
let names = names.replace(&['{', '}'][..], "");
let names_split_comma = names.split(',');
names_split_comma.map(str::to_string).collect()
});
(match_module, match_names)
})
.collect();
scripts
.into_iter()
.filter(|script| -> bool {
match_tests.iter().any(|(module, names)| {
let matched_module = module == &"" || script.module.contains(module);
let matched_name = match names {
None => true,
Some(names) => names.iter().any(|name| {
if exact_match {
name == &script.name
} else {
script.name.contains(name)
}
}),
};
matched_module && matched_name
})
})
.collect::<Vec<Script>>()
} else {
scripts
};
scripts
.into_par_iter()
.map(|script| {

View File

@ -0,0 +1,5 @@
# This file was generated by Aiken
# You typically do not need to edit this file
requirements = []
packages = []

View File

@ -0,0 +1,3 @@
name = 'aiken-lang/acceptance_test_065'
version = '0.0.0'
description = ''

View File

@ -0,0 +1,49 @@
use aiken/builtin
type MyData {
Integer(Int)
Bytes(ByteArray)
}
test foo() {
inspect(42) == Integer(42) && inspect(#"ff") == Bytes(#"ff")
}
fn inspect(data: Data) -> MyData {
expect result: MyData =
builtin.choose_data(
data,
inspect_constr(data),
inspect_map(data),
inspect_list(data),
inspect_integer(data),
inspect_bytearray(data),
)
result
}
fn inspect_constr(_data: Data) -> Data {
todo
}
fn inspect_map(_data: Data) -> Data {
todo
}
fn inspect_list(_data: Data) -> Data {
todo
}
fn inspect_integer(data: Data) -> Data {
let result: Data =
builtin.un_i_data(data)
|> Integer
result
}
fn inspect_bytearray(data: Data) -> Data {
let result: Data =
builtin.un_b_data(data)
|> Bytes
result
}

View File

@ -164,8 +164,8 @@
]
}
},
"compiledCode": "59030501000032323232323232323232322225333006323232323232323232533300f3370e002900009925130090021533300f3370e0029001099191919299980999b87001480084c8c8cccc8888c8c8c8c8c9289812000980b19299980e99b8748000c080dd500088008a9980fa492a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e0016330120070033022001301432533301b3370e9000180f1baa0011001153301d49012a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300f005001300d48901ff00010001012005301b001300d00214a0602a6ea8004cc028c02c03120023017001300900213232323253330133370e0029001099191999911119191919192513024001301632533301d3370e900018101baa0011001153301f49012a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e0016330120070033022001301432533301b3370e9000180f1baa0011001153301d49012a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300f005001300d48901ff00010001012005301b001300d00214a0602a6ea8004cc028c02c031200230170013009002301137540026600c600e0129000119ba548000cc04ccdd2a4004660266ea40052f5c06602666e9520024bd7025eb8088cc010dd6198031803998031803801240009002119baf3300730080014800000888cc00cdd6198029803198029803001240009000119baf3300630073300630070014800920000023001001222533301000213374a900125eb804c8c94ccc034c00c0084cdd2a40006602600497ae013330050050010033014003301200222323330010014800000c888cccc030cdc3802001009919980200219b8000348008c0540040048c02cdd50008a4c2c6002002444a666012004293099802980098058011998018019806001000ab9a5736ae7155ceaab9e5573eae815d0aba201",
"hash": "c95b3842362b77afec21773b7c0b1f09e61bf5e4c58b685533e6d342"
"compiledCode": "59030501000032323232323232323232322225333006323232323232323232533300f3370e002900009925130090021533300f3370e0029001099191919299980999b87001480084c8c8cccc8888c8c8c8c8c9289812000980b19299980e99b8748000c080dd500088008a9980fa492a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e0016330120070033022001301432533301b3370e9000180f1baa0011001153301d49012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300f005001300d48901ff00010001012005301b001300d00214a0602a6ea8004cc028c02c03120023017001300900213232323253330133370e0029001099191999911119191919192513024001301632533301d3370e900018101baa0011001153301f49012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e0016330120070033022001301432533301b3370e9000180f1baa0011001153301d49012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300f005001300d48901ff00010001012005301b001300d00214a0602a6ea8004cc028c02c031200230170013009002301137540026600c600e0129000119ba548000cc04ccdd2a4004660266ea40052f5c06602666e9520024bd7025eb8088cc010dd6198031803998031803801240009002119baf3300730080014800000888cc00cdd6198029803198029803001240009000119baf3300630073300630070014800920000023001001222533301000213374a900125eb804c8c94ccc034c00c0084cdd2a40006602600497ae013330050050010033014003301200222323330010014800000c888cccc030cdc3802001009919980200219b8000348008c0540040048c02cdd50008a4c2c6002002444a666012004293099802980098058011998018019806001000ab9a5736ae7155ceaab9e5573eae815d0aba201",
"hash": "b79dffa847f2b9a55cb6cee2bd6057251f45e6a252587c7f6f3545d0"
}
]
}

View File

@ -0,0 +1,13 @@
# This file was generated by Aiken
# You typically do not need to edit this file
[[requirements]]
name = "aiken-lang/stdlib"
version = "main"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "main"
requirements = []
source = "github"

View File

@ -0,0 +1,7 @@
name = "aiken-lang/acceptance_test_074"
version = "0.0.0"
[[dependencies]]
name = 'aiken-lang/stdlib'
version = 'main'
source = 'github'

View File

@ -0,0 +1,282 @@
use aiken/bytearray.{from_string}
use aiken/hash.{Hash, Sha2_256, sha2_256}
use aiken/list
use aiken/option.{choice, is_none}
/// Variant of MerkleTree with only hash but without actual value
pub type MerkleTree<a> {
Empty
Leaf { hash: Hash<Sha2_256, ByteArray> }
Node {
hash: Hash<Sha2_256, ByteArray>,
left: MerkleTree<a>,
right: MerkleTree<a>,
}
}
pub type Proof =
List<ProofItem>
pub type ProofItem {
Left { hash: Hash<Sha2_256, ByteArray> }
Right { hash: Hash<Sha2_256, ByteArray> }
}
// Function returning a hash of a given Merkle Tree element
pub fn root_hash(self: MerkleTree<a>) -> Hash<Sha2_256, ByteArray> {
when self is {
Empty -> #""
Leaf { hash } -> hash
Node { hash, .. } -> hash
}
}
/// Function atests whether two Merkle Tress are equal, this is the case when their root hashes match.
pub fn is_equal(left: MerkleTree<a>, right: MerkleTree<a>) -> Bool {
root_hash(left) == root_hash(right)
}
/// Function returns a total numbers of leaves in the tree.
pub fn size(self: MerkleTree<a>) -> Int {
when self is {
Empty -> 0
Leaf{..} -> 1
Node { left, right, .. } -> size(left) + size(right)
}
}
fn combine_hash(
left: Hash<Sha2_256, a>,
right: Hash<Sha2_256, a>,
) -> Hash<Sha2_256, a> {
sha2_256(bytearray.concat(left, right))
}
/// Function that returns whether merkle tree has any elements
pub fn is_empty(self: MerkleTree<a>) -> Bool {
when self is {
Empty -> True
_ -> False
}
}
fn do_proof(
self: MerkleTree<a>,
item_hash: Hash<Sha2_256, ByteArray>,
proof: Proof,
serialise_fn: fn(a) -> ByteArray,
) -> Option<Proof> {
when self is {
Empty -> None
Leaf { hash } ->
if hash == item_hash {
Some(proof)
} else {
None
}
Node { left, right, .. } -> {
let rh = root_hash(right)
let lh = root_hash(left)
let go_left: Option<Proof> =
do_proof(
left,
item_hash,
list.push(proof, Right { hash: rh }),
serialise_fn,
)
let go_right: Option<Proof> =
do_proof(
right,
item_hash,
list.push(proof, Left { hash: lh }),
serialise_fn,
)
choice([go_left, go_right])
}
}
}
/// Construct a membership 'Proof' from an element and a 'MerkleTree'. Returns
/// 'None' if the element isn't a member of the tree to begin with.
/// Note function will return Some([]) in case root of the tree is also it's only one and only element
pub fn get_proof(
self: MerkleTree<a>,
item: a,
serialise_fn: fn(a) -> ByteArray,
) -> Option<Proof> {
let empty: Proof = []
do_proof(self, sha2_256(serialise_fn(item)), empty, serialise_fn)
}
fn do_from_list(
items: List<a>,
len: Int,
serialise_fn: fn(a) -> ByteArray,
) -> MerkleTree<a> {
when items is {
[] -> Empty
[item] -> {
let hashed_item = sha2_256(serialise_fn(item))
Leaf { hash: hashed_item }
}
all -> {
let cutoff: Int = len / 2
let left =
all
|> list.take(cutoff)
|> do_from_list(cutoff, serialise_fn)
let right =
all
|> list.drop(cutoff)
|> do_from_list(len - cutoff, serialise_fn)
let hash = combine_hash(root_hash(left), root_hash(right))
Node { hash, left, right }
}
}
}
/// Construct a 'MerkleTree' from a list of elements.
/// Note that, while this operation is doable on-chain, it is expensive and
/// preferably done off-chain.
pub fn from_list(
items: List<a>,
serialise_fn: fn(a) -> ByteArray,
) -> MerkleTree<a> {
do_from_list(items, list.length(items), serialise_fn)
}
fn do_from_hashes_list(
items: List<Hash<Sha2_256, a>>,
len: Int,
) -> MerkleTree<a> {
when items is {
[] -> Empty
[hashed_item] -> Leaf { hash: hashed_item }
all -> {
let cutoff: Int = len / 2
let left =
all
|> list.take(cutoff)
|> do_from_hashes_list(cutoff)
let right =
all
|> list.drop(cutoff)
|> do_from_hashes_list(len - cutoff)
let hash = combine_hash(root_hash(left), root_hash(right))
Node { hash, left, right }
}
}
}
/// Construct a 'MerkleTree' from a list of hashes.
/// Note that, while this operation is doable on-chain, it is expensive and
/// preferably done off-chain.
pub fn from_hashes_list(items: List<Hash<Sha2_256, a>>) -> MerkleTree<a> {
do_from_hashes_list(items, list.length(items))
}
// Check whether a hashed element is part of a 'MerkleTree' using only its root hash
// and a 'Proof'. The proof is guaranteed to be in log(n) of the size of the
// tree, which is why we are interested in such data-structure in the first
// place.
pub fn member_from_hash(
item_hash: Hash<Sha2_256, a>,
root_hash: Hash<Sha2_256, a>,
proof: Proof,
serialise_fn: fn(a) -> ByteArray,
) -> Bool {
when proof is {
[] -> root_hash == item_hash
[head, ..tail] ->
when head is {
Left { hash: l } ->
member_from_hash(
combine_hash(l, item_hash),
root_hash,
tail,
serialise_fn,
)
Right { hash: r } ->
member_from_hash(
combine_hash(item_hash, r),
root_hash,
tail,
serialise_fn,
)
}
}
}
// Check whether an element is part of a 'MerkleTree' using only its root hash
// and a 'Proof'.
pub fn member(
item: a,
root_hash: Hash<Sha2_256, ByteArray>,
proof: Proof,
serialise_fn: fn(a) -> ByteArray,
) -> Bool {
let item_hash = sha2_256(serialise_fn(item))
member_from_hash(item_hash, root_hash, proof, serialise_fn)
}
pub fn member_from_tree(
tree: MerkleTree<a>,
item: a,
serialise_fn: fn(a) -> ByteArray,
) -> Bool {
let proof: Option<Proof> = get_proof(tree, item, serialise_fn)
let rh = root_hash(tree)
when proof is {
Some(p) -> member(item, rh, p, serialise_fn)
None -> False
}
}
// needed only for tests
fn create_string_item_serialise_fn() -> fn(String) -> ByteArray {
fn(x: String) { from_string(x) }
}
test from_hashes_list_5() {
let dog = @"dog"
let cat = @"cat"
let mouse = @"mouse"
let horse = @"horse"
let serialise_fn = create_string_item_serialise_fn()
let items = [dog, cat, mouse, horse]
let hashes_items = list.map(items, fn(item) { sha2_256(serialise_fn(item)) })
let mt = from_hashes_list(hashes_items)
let left_node_hash =
sha2_256(
bytearray.concat(sha2_256(serialise_fn(dog)), sha2_256(serialise_fn(cat))),
)
let right_node_hash =
sha2_256(
bytearray.concat(
sha2_256(serialise_fn(mouse)),
sha2_256(serialise_fn(horse)),
),
)
let root_hash = sha2_256(bytearray.concat(left_node_hash, right_node_hash))
Node {
hash: root_hash,
left: Node {
hash: left_node_hash,
left: Leaf { hash: sha2_256(serialise_fn(dog)) },
right: Leaf { hash: sha2_256(serialise_fn(cat)) },
},
right: Node {
hash: right_node_hash,
left: Leaf { hash: sha2_256(serialise_fn(mouse)) },
right: Leaf { hash: sha2_256(serialise_fn(horse)) },
},
} == mt
}

View File

@ -0,0 +1,5 @@
# This file was generated by Aiken
# You typically do not need to edit this file
requirements = []
packages = []

View File

@ -0,0 +1,3 @@
name = 'aiken-lang/acceptance_test_076'
version = '0.0.0'
description = ''

View File

@ -0,0 +1,5 @@
test expect_positive() {
let val = 5
expect True = val > 0
True
}

View File

@ -6,45 +6,19 @@
},
"validators": [
{
"title": "basic.spend",
"title": "deploy.spend",
"datum": {
"title": "Unit",
"description": "The nullary constructor.",
"schema": {
"anyOf": [
{
"dataType": "constructor",
"index": 0,
"fields": []
}
]
}
"title": "Data",
"description": "Any Plutus data.",
"schema": {}
},
"redeemer": {
"title": "Unit",
"description": "The nullary constructor.",
"schema": {
"anyOf": [
{
"dataType": "constructor",
"index": 0,
"fields": []
}
]
}
},
"compiledCode": "5904600100003232323232323232323222253330063232323232300200132323232323233015333010323330113375e660146016002900b26126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff004a0944cc024c02802d20004c0103d87a80004c0103d879800033015333010323253330123370e002900109919299980a19baf3300d300e00148001300126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff0013370e6eb4cc034c038005200248000528180c80098060010b18099baa00133009300a00b48009300103d87a80004c0103d8798000330153330103232533301600116132533301700113232300c001330193330143375e6e98dd5998069807000a40046e98c0152080a8d6b9074c0103d87a80004c0103d8798000330193330143375e6601a601c6601a601c002900024000980122d8799f581c11111111111111111111111111111111111111111111111111111111ff004c0103d87a80004c0103d879800033019333014323253330163370e0029000099250301000214a2602e6ea8004cc034c038cc034c038005200048009300103d87a80004c0103d8798000330193330143375e6601a601c002900219ba5480012f5c098103d87a80004c0103d8798000330193330143375e6601a601c002900319ba5480092f5c098103d87a80004c0103d87980004bd70180c8010b180c8009bac3300a300b00148010cc024c02802d20004c0103d87a80004c0103d879800033015333010323375e6e98dd5998051805800a400c6e98c009205433009300a00b4800130103d87a80004c0103d87980004bd7011999111919000999991111999805002001801000a5eb7bdb180010004020cccc8888cccc03001000c0080052f5bded8c000400200e9101004881000013001001222225333016004133017337606ea400cdd300125eb7bdb1804c8c8c8c94ccc058cdd79980280380099ba5480012f5c026603666ec0dd48039ba6006008153330163371e00e00226603666ec0dd48039ba600600313301b337606ea4004dd3001199998048048018038030029bae30170033756602e004603400a603000844a66601c66e400080044cdd2a400097ae01533300e3371e004002266e9520024bd70099ba5480112f5c0600200244444a66602600826602866ec0dd48019ba80024bd6f7b630099191919299980999baf330050070013374a900025eb804cc060cdd81ba9007375000c0102a66602666e3c01c0044cc060cdd81ba9007375000c00626603066ec0dd48009ba800233333009009003007006005375c60280066eb4c050008c05c014c054010c004004894ccc0380045288991929998060010998020020008a5030120023370e900118061baa301000122323330010014800000c888cccc030cdc3802001009119980200219b8000348008c0500040048c028dd50008a4c2c6002002444a666010004293099802980098050011998018019805801000ab9a5736aae7555cf2ab9f5740ae855d101",
"hash": "42428cc55092a182161081f528491b8ec1fbd908c40eca9069c4d1be"
},
{
"title": "mint.mint",
"redeemer": {
"title": "Data",
"description": "Any Plutus data.",
"schema": {}
},
"compiledCode": "590488010000323232323232323232323222533300532323232323001003300100122533300f00114a226464a6660180042660080080022940c04c008cdc3a4004601a6ea8c044004cc034ccc01cc8c8c8c8c8c8c94ccc04cc0580084c8c8cdc78018009bae3016001300932533300f3370e900018091baa0011001153301149012a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300830090034800854cc0412401364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375c602800264646464600c00200200264640026644660100040020029110000137566600c600e6600c600e00290002401000e600200244a666020002297ae01323232323301537520026600c00c0066eb8c04400cdd59808801180a0011809000980080091129998078010a5eb7bdb1804c8c8c8c94ccc038cdc7802800880189980a19bb037520026e98008ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d333007323232323322323232323253330123370e00290010b0991919b87001483c850dd6980d0009806801180a1baa001332233008002001001488103666f6f0033223233223253330153370e00290010801099190009bab301d00130100033017375400400297adef6c6033223300b002001002001375666012601400690040009bae30150013008533300d3370e900018081baa0021002153300f49012a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633005300600748008cc014c01801d20003001001222533301100213374a900125eb804c8c8c8c94ccc040cdc7802800899ba548000cc058dd400125eb804ccc01c01c00c014dd718090019bad3012002301500330130023001001222533300f00213374a900125eb804c8c8c8c94ccc038cdc7802800899ba548000cc050dd300125eb804ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d3330073232323233223232533300f3375e006002266e1cc8c018004dd5998049805198049805002240009009240042940c054004c020c94ccc038cdc3a400060226ea8004400454cc0412412a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633223300700200137566600e60106600e60100049000240246600e6010004900100380418008009129998080008a400026466e0120023300300300130130013001001222533300f00213374a900125eb804c8c8c8c94ccc038cdd7802800899ba548000cc0500092f5c0266600e00e00600a6020006602000460260066022004980103d87a80004c0103d87980004bd701119199800800a4000006444666601666e1c0100080488ccc010010cdc0001a40046028002002460146ea8004526163001001222533300900214984cc014c004c02c008ccc00c00cc0300080055cd2b9b5738aae7555cf2ab9f5740ae855d11",
"hash": "dbc571e23778572680144ee7065334ab9545cc8111da1ea5dc85ac44"
"compiledCode": "5903e101000032323232323232323232322225333006323232323230020013301033300a32323375e0040026601893260103d87980000074c103d87a80004c0103d87980003301033300a32323232323232330123253330123370e002900009919299980c980e0010a4c2a6602c921364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375a603400260180042a660289212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e74001630143754002a66602266ebd30106d8799f182aff0000113370e64600a00200690020a50301700130093253330103370e900018099baa0011001153301249012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163322330060020010013237280026ecd30106d8799f182aff0037566600e60106600e6010012900024028600200244a6660260022900009919b8048008cc00c00c004c058004c0040048894ccc0480084cdd2a400497ae013232323253330113371e00a002266e952000330170024bd7009998038038018029bae30130033013002301600330140024c0103d87a80004c0103d87980003301033300a32533301000116132533301100116132323232533301032323009001330173330113375e660146016002900226126d87a9f5820fcaa61fb85676101d9e3398a484674e71c45c3fd41b492682f3b0054f4cf3273ff004c0103d87a80004c0103d8798000330173330113375e6601460160029003260122d8799f581ce37db487fbd58c45d059bcbf5cd6b1604d3bec16cf888f1395a4ebc4ff004c0103d87a80004c0103d87980004bd700010991918048009980b99980899baf3300a300b3300a300b0014800120024c012ad8799fd8799fd8799f581c66666666666666666666666666666666666666666666666666666666ffffff004c0103d87a80004c0103d879800033017333011323253330133370e0029002099251300d00216301537540026601460160029002260103d87a80004c0103d87980004bd700008a50301600430150041630140013013001375866006600866006600800a900024008980103d87a80004c0103d87980004bd7018008009129998078008a5113232533300c00213300400400114a0602600466e1d2002300d3754602200244646660020029000001911199980619b870040020132333004004337000069001180a800800918059baa001149858c0040048894ccc0240085261330053001300b002333003003300c0020015734ae6d5ce2ab9d5573caae7d5d02ba15745",
"hash": "3326c651fce284b443e23da9cc6f5864a1e496f2fc7774799fb897f9"
},
{
"title": "withdrawals.spend",
@ -78,19 +52,45 @@
"hash": "6917ce2313801b854e38507b68d2c61afd70fca721804235e4760056"
},
{
"title": "deploy.spend",
"datum": {
"title": "Data",
"description": "Any Plutus data.",
"schema": {}
},
"title": "mint.mint",
"redeemer": {
"title": "Data",
"description": "Any Plutus data.",
"schema": {}
},
"compiledCode": "5903e101000032323232323232323232322225333006323232323230020013301033300a32323375e0040026601893260103d87980000074c103d87a80004c0103d87980003301033300a32323232323232330123253330123370e002900009919299980c980e0010a4c2a6602c921364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375a603400260180042a660289212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e74001630143754002a66602266ebd30106d8799f182aff0000113370e64600a00200690020a50301700130093253330103370e900018099baa0011001153301249012a4173736572746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163322330060020010013237280026ecd30106d8799f182aff0037566600e60106600e6010012900024028600200244a6660260022900009919b8048008cc00c00c004c058004c0040048894ccc0480084cdd2a400497ae013232323253330113371e00a002266e952000330170024bd7009998038038018029bae30130033013002301600330140024c0103d87a80004c0103d87980003301033300a32533301000116132533301100116132323232533301032323009001330173330113375e660146016002900226126d87a9f5820fcaa61fb85676101d9e3398a484674e71c45c3fd41b492682f3b0054f4cf3273ff004c0103d87a80004c0103d8798000330173330113375e6601460160029003260122d8799f581ce37db487fbd58c45d059bcbf5cd6b1604d3bec16cf888f1395a4ebc4ff004c0103d87a80004c0103d87980004bd700010991918048009980b99980899baf3300a300b3300a300b0014800120024c012ad8799fd8799fd8799f581c66666666666666666666666666666666666666666666666666666666ffffff004c0103d87a80004c0103d879800033017333011323253330133370e0029002099251300d00216301537540026601460160029002260103d87a80004c0103d87980004bd700008a50301600430150041630140013013001375866006600866006600800a900024008980103d87a80004c0103d87980004bd7018008009129998078008a5113232533300c00213300400400114a0602600466e1d2002300d3754602200244646660020029000001911199980619b870040020132333004004337000069001180a800800918059baa001149858c0040048894ccc0240085261330053001300b002333003003300c0020015734ae6d5ce2ab9d5573caae7d5d02ba15745",
"hash": "6bfde537ea8bcb1b566489979896ecc8f88c6edb2f791a4197e2d51f"
"compiledCode": "590488010000323232323232323232323222533300532323232323001003300100122533300f00114a226464a6660180042660080080022940c04c008cdc3a4004601a6ea8c044004cc034ccc01cc8c8c8c8c8c8c94ccc04cc0580084c8c8cdc78018009bae3016001300932533300f3370e900018091baa0011001153301149012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e00163300830090034800854cc0412401364c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2069742065787065637465640016375c602800264646464600c00200200264640026644660100040020029110000137566600c600e6600c600e00290002401000e600200244a666020002297ae01323232323301537520026600c00c0066eb8c04400cdd59808801180a0011809000980080091129998078010a5eb7bdb1804c8c8c8c94ccc038cdc7802800880189980a19bb037520026e98008ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d333007323232323322323232323253330123370e00290010b0991919b87001483c850dd6980d0009806801180a1baa001332233008002001001488103666f6f0033223233223253330153370e00290010801099190009bab301d00130100033017375400400297adef6c6033223300b002001002001375666012601400690040009bae30150013008533300d3370e900018081baa0021002153300f49012a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633005300600748008cc014c01801d20003001001222533301100213374a900125eb804c8c8c8c94ccc040cdc7802800899ba548000cc058dd400125eb804ccc01c01c00c014dd718090019bad3012002301500330130023001001222533300f00213374a900125eb804c8c8c8c94ccc038cdc7802800899ba548000cc050dd300125eb804ccc01c01c00c014dd718080019bab3010002301300330110024c103d87a80004c0103d87980003300d3330073232323233223232533300f3375e006002266e1cc8c018004dd5998049805198049805002240009009240042940c054004c020c94ccc038cdc3a400060226ea8004400454cc0412412a4578706563746564206f6e20696e636f727265637420636f6e7374727563746f722076617269616e742e001633223300700200137566600e60106600e60100049000240246600e6010004900100380418008009129998080008a400026466e0120023300300300130130013001001222533300f00213374a900125eb804c8c8c8c94ccc038cdd7802800899ba548000cc0500092f5c0266600e00e00600a6020006602000460260066022004980103d87a80004c0103d87980004bd701119199800800a4000006444666601666e1c0100080488ccc010010cdc0001a40046028002002460146ea8004526163001001222533300900214984cc014c004c02c008ccc00c00cc0300080055cd2b9b5738aae7555cf2ab9f5740ae855d11",
"hash": "4621310698e9ee7830df98d79551eb5672cdee04247e28c8e1c1d494"
},
{
"title": "basic.spend",
"datum": {
"title": "Unit",
"description": "The nullary constructor.",
"schema": {
"anyOf": [
{
"dataType": "constructor",
"index": 0,
"fields": []
}
]
}
},
"redeemer": {
"title": "Unit",
"description": "The nullary constructor.",
"schema": {
"anyOf": [
{
"dataType": "constructor",
"index": 0,
"fields": []
}
]
}
},
"compiledCode": "59046f0100003232323232323232323222253330063232323232300200132323232323233015333010323330113375e660146016002900b26126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff004a0944cc024c02802d20004c0103d87a80004c0103d879800033015333010323253330123370e002900109919299980a19baf3300d300e00148001300126d8799f58200000000000000000000000000000000000000000000000000000000000000000ff0013370e6eb4cc034c038005200248000528180c80098060010b18099baa00133009300a00b48009300103d87a80004c0103d8798000330153330103232533301600116132533301700113232300c001330193330143375e6e98dd5998069807000a40046e98c0152080a8d6b9074c0103d87a80004c0103d8798000330193330143375e6601a601c6601a601c002900024000980122d8799f581c11111111111111111111111111111111111111111111111111111111ff004c0103d87a80004c0103d879800033019333014323253330163370e0029000099250301000214a2602e6ea8004cc034c038cc034c038005200048009300103d87a80004c0103d8798000330193330143375e6601a601c002900219ba5480012f5c098103d87a80004c0103d8798000330193330143375e6601a601c002900319ba5480092f5c098103d87a80004c0103d87980004bd70180c8010b180c8009bac3300a300b00148010cc024c02802d20004c0103d87a80004c0103d879800033015333010323375e6e98dd5998051805800a400c6e98c009205433009300a00b4800130103d87a80004c0103d87980004bd701199911299980999b870014800052f5bded8c02646400266664444666601400800600400297adef6c6000400100833332222333300c0040030020014bd6f7b630001000803a45004881000013001001222225333016004133017337606ea400cdd300125eb7bdb1804c8c8c8c94ccc058cdd79980280380099ba5480012f5c026603666ec0dd48039ba6006008153330163371e00e00226603666ec0dd48039ba600600313301b337606ea4004dd3001199998048048018038030029bae30170033756602e004603400a603000844a66601c66e400080044cdd2a400097ae01533300e3371e004002266e9520024bd70099ba5480112f5c0600200244444a66602600826602866ec0dd48019ba80024bd6f7b630099191919299980999baf330050070013374a900025eb804cc060cdd81ba9007375000c0102a66602666e3c01c0044cc060cdd81ba9007375000c00626603066ec0dd48009ba800233333009009003007006005375c60280066eb4c050008c05c014c054010c004004894ccc0380045288991929998060010998020020008a5030120023370e900118061baa301000122323330010014800000c888cccc030cdc3802001009119980200219b8000348008c0500040048c028dd50008a4c2c6002002444a666010004293099802980098050011998018019805801000ab9a5736aae7555cf2ab9f5740ae855d101",
"hash": "911eb2bc725e7a51670338f2a12102785984d2569fa75b6ba4054c49"
}
]
}