fix: the refactor on discharge value env (#393)

This commit is contained in:
Kasey 2023-02-18 20:49:29 -05:00 committed by GitHub
parent 1aea586cab
commit f3cdc05875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 618 additions and 5 deletions

View File

@ -449,12 +449,12 @@ fn discharge_value(value: Rc<Value>) -> Rc<Term<NamedDeBruijn>> {
stack.push(DischargeStep::DischargeValueEnv(
lam_cnt,
env.clone(),
function.to_owned(),
argument.to_owned(),
));
stack.push(DischargeStep::DischargeValueEnv(
lam_cnt,
env,
argument.to_owned(),
function.to_owned(),
));
}
Term::Delay(body) => {

View File

@ -3,11 +3,11 @@
[[requirements]]
name = "aiken-lang/stdlib"
version = "1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b"
version = "3b47c89006e7580c2213370d7426ed2a38d2836e"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b"
version = "3b47c89006e7580c2213370d7426ed2a38d2836e"
requirements = []
source = "github"

View File

@ -2,5 +2,5 @@ name = "aiken-lang/acceptance_test_036"
version = "0.0.0"
dependencies = [
{ name = "aiken-lang/stdlib", version = "1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b", source = "github" },
{ name = "aiken-lang/stdlib", version = "3b47c89006e7580c2213370d7426ed2a38d2836e", source = "github" },
]

View File

@ -63,3 +63,13 @@ test expect_list3() {
expect [a, ..d]: List<Int> = initial_car
a == 5 && d == [6, 7]
}
type Redeemer {
CreateVoteBatch { id: ByteArray }
}
test single_field_expect() {
let redeemer = CreateVoteBatch { id: #"" }
expect CreateVoteBatch { id } = redeemer
id == #""
}

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 = "3b47c89006e7580c2213370d7426ed2a38d2836e"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "3b47c89006e7580c2213370d7426ed2a38d2836e"
requirements = []
source = "github"

View File

@ -0,0 +1,6 @@
name = "aiken-lang/acceptance_test_067"
version = "0.0.0"
dependencies = [
{ name = "aiken-lang/stdlib", version = "3b47c89006e7580c2213370d7426ed2a38d2836e", source = "github" },
]

View File

@ -0,0 +1,584 @@
use aiken/bytearray.{from_string}
use aiken/hash.{Hash, Sha2_256, sha2_256}
use aiken/list
use aiken/option.{choice, is_none}
// Construction of the merkle tree shouldn't be done by hand, but via
// 'from_list'.
/// A purely functional implementation of MerkleTrees that is suitable for
/// usage on-chain. Note, however, that the construction of 'MerkleTree' and
/// membership proofs are still expected to happen *off-chain* while only the
/// proof verification should be done on-chain.
///
/// Code ported to Aiken from Hydra (https://github.com/input-output-hk/hydra/blob/master/plutus-merkle-tree/src/Plutus/MerkleTree.hs)
/// A MerkleTree representation, suitable for on-chain manipulation.
pub type MerkleTree<a> {
Empty
Leaf { value: a, 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> }
}
/// Deconstruct a 'MerkleTree' back to a list of elements.
pub fn to_list(self: MerkleTree<a>) -> List<a> {
when self is {
Empty -> []
Leaf { value, .. } -> [value]
Node { left, right, .. } -> list.concat(to_list(left), to_list(right))
}
}
test to_list_1() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let items = [dog, cat, mouse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
items == to_list(mt)
}
// 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
}
}
test root_hash_1() {
let dog = "dog"
let items = [dog]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let node_hash = hash_fn(dog)
root_hash(mt) == node_hash
}
test root_hash_3() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let items = [dog, cat, mouse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let node_hash = sha2_256(bytearray.concat(hash_fn(cat), hash_fn(mouse)))
let rh = sha2_256(bytearray.concat(hash_fn(dog), node_hash))
expect Node { hash: root_hash, .. } = mt
rh == root_hash
}
test root_hash_2() {
let items = []
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let node_hash = #""
root_hash(mt) == node_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)
}
}
test size_1() {
let items = []
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
size(mt) == 0
}
test size_2() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let hash_fn = create_string_item_hash_fn()
let mt = from_list([dog, cat, mouse], hash_fn)
size(mt) == 3
}
test size_3() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let items = [dog, cat, mouse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
size(mt) == 3
}
test size_4() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let pig = "pig"
let bull = "bull"
let hash_fn = create_string_item_hash_fn()
let items = [dog, cat, mouse, horse, pig, bull]
let mt = from_list(items, hash_fn)
size(mt) == 6
}
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
}
}
test is_empty_1() {
let mt = Empty
is_empty(mt)
}
test is_empty_2() {
let dog = "dog"
let hash = create_string_item_hash_fn()
let mt = Leaf { value: dog, hash: hash(dog) }
is_empty(mt) == False
}
fn do_proof(
self: MerkleTree<a>,
item_hash: Hash<Sha2_256, ByteArray>,
proof: Proof,
hash_fn: fn(a) -> Hash<Sha2_256, a>,
) -> 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 }), hash_fn)
let go_right: Option<Proof> =
do_proof(right, item_hash, list.push(proof, Left { hash: lh }), hash_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,
hash_fn: fn(a) -> Hash<Sha2_256, a>,
) -> Option<Proof> {
let empty: Proof = []
do_proof(self, hash_fn(item), empty, hash_fn)
}
test get_proof_1() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let pig = "pig"
let bull = "bull"
let items = [dog, cat, mouse, horse, pig, bull]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let hash_fn = create_string_item_hash_fn()
let maybe_proof: Option<Proof> = get_proof(mt, "parrot", hash_fn)
is_none(maybe_proof)
}
test get_proof_2() {
let dog = "dog"
let items = [dog]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let maybe_proof: Option<Proof> = get_proof(mt, dog, hash_fn)
expect Some(proof) = maybe_proof
// when proof is empty list it actually means that root of the tree is in fact element
proof == []
}
test get_proof_3() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let items = [dog, cat, mouse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let node_hash = sha2_256(bytearray.concat(hash_fn(cat), hash_fn(mouse)))
let maybe_proof: Option<Proof> = get_proof(mt, dog, hash_fn)
expect Some(proof) = maybe_proof
let size_match = list.length(proof) == 1
expect Some(p1) = list.at(proof, 0)
let h1: ByteArray = get_proof_item_value(p1)
size_match && h1 == node_hash
}
test get_proof_4() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let items = [dog, cat, mouse, horse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let right_node_hash =
sha2_256(bytearray.concat(hash_fn(mouse), hash_fn(horse)))
let maybe_proof: Option<Proof> = get_proof(mt, dog, hash_fn)
expect Some(proof) = maybe_proof
let size_match = list.length(proof) == 2
expect Some(p1) = list.at(proof, 0)
expect Some(p2) = list.at(proof, 1)
let h1: ByteArray = get_proof_item_value(p1)
let h2: ByteArray = get_proof_item_value(p2)
size_match && h1 == hash_fn(cat) && h2 == right_node_hash
}
fn do_from_list(
items: List<a>,
len: Int,
hash_fn: fn(a) -> Hash<Sha2_256, a>,
) -> MerkleTree<a> {
when items is {
[] -> Empty
[item] -> {
let hashed_item = hash_fn(item)
Leaf { value: item, hash: hashed_item }
}
all -> {
let cutoff: Int = len / 2
let left =
all
|> list.take(cutoff)
|> do_from_list(cutoff, hash_fn)
let right =
all
|> list.drop(cutoff)
|> do_from_list(len - cutoff, hash_fn)
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_list(
items: List<a>,
hash_fn: fn(a) -> Hash<Sha2_256, a>,
) -> MerkleTree<a> {
do_from_list(items, list.length(items), hash_fn)
}
test from_1() {
let _a = -1
let items = []
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
Empty == mt
}
test from_2() {
let dog = "dog"
let items = [dog]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
Leaf { value: dog, hash: hash_fn(dog) } == mt
}
test from_3() {
let dog = "dog"
let cat = "cat"
let items = [dog, cat]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let root_hash = sha2_256(bytearray.concat(hash_fn(dog), hash_fn(cat)))
Node {
hash: root_hash,
left: Leaf { value: dog, hash: hash_fn(dog) },
right: Leaf { value: cat, hash: hash_fn(cat) },
} == mt
}
test from_4() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let items = [dog, cat, mouse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let node_hash = sha2_256(bytearray.concat(hash_fn(cat), hash_fn(mouse)))
let root_hash = sha2_256(bytearray.concat(hash_fn(dog), node_hash))
Node {
hash: root_hash,
left: Leaf { value: dog, hash: hash_fn(dog) },
right: Node {
hash: node_hash,
left: Leaf { value: cat, hash: hash_fn(cat) },
right: Leaf { value: mouse, hash: hash_fn(mouse) },
},
} == mt
}
test from_5() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let hash_fn = create_string_item_hash_fn()
let items = [dog, cat, mouse, horse]
let mt = from_list(items, hash_fn)
let left_node_hash = sha2_256(bytearray.concat(hash_fn(dog), hash_fn(cat)))
let right_node_hash =
sha2_256(bytearray.concat(hash_fn(mouse), hash_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 { value: dog, hash: hash_fn(dog) },
right: Leaf { value: cat, hash: hash_fn(cat) },
},
right: Node {
hash: right_node_hash,
left: Leaf { value: mouse, hash: hash_fn(mouse) },
right: Leaf { value: horse, hash: hash_fn(horse) },
},
} == mt
}
// 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,
hash_fn: fn(a) -> Hash<Sha2_256, a>,
) -> 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, hash_fn)
Right { hash: r } ->
member_from_hash(combine_hash(item_hash, r), root_hash, tail, hash_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,
hash_fn: fn(a) -> Hash<Sha2_256, a>,
) -> Bool {
let item_hash = hash_fn(item)
member_from_hash(item_hash, root_hash, proof, hash_fn)
}
test member_1() {
let dog = "dog"
let items = [dog]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let item = dog
let rh = root_hash(mt)
expect Some(proof) = get_proof(mt, item, hash_fn)
member(item: item, root_hash: rh, proof: proof, hash_fn: hash_fn)
}
test member_2() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let items = [dog, cat, mouse, horse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let item = cat
let rh = root_hash(mt)
expect Some(proof) = get_proof(mt, item, hash_fn)
member(item: item, root_hash: rh, proof: proof, hash_fn: hash_fn)
}
test member_3() {
let dog = "dog"
let cat = "cat"
let items = [dog, cat]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let item = cat
let rh = root_hash(mt)
expect Some(proof) = get_proof(mt, item, hash_fn)
member(item: item, root_hash: rh, proof: proof, hash_fn: hash_fn)
}
test member_4() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let items = [dog, cat, mouse]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let item = mouse
let rh = root_hash(mt)
expect Some(proof) = get_proof(mt, item, hash_fn)
member(item: item, root_hash: rh, proof: proof, hash_fn: hash_fn)
}
test member_5() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let pig = "pig"
let bull = "bull"
let items = [dog, cat, mouse, horse, pig, bull]
let hash_fn = create_string_item_hash_fn()
let mt = from_list(items, hash_fn)
let item = pig
let rh = root_hash(mt)
expect Some(proof) = get_proof(mt, item, hash_fn)
member(item: item, root_hash: rh, proof: proof, hash_fn: hash_fn)
}
test member_6() {
let dog = "dog"
let cat = "cat"
let mouse = "mouse"
let horse = "horse"
let pig = "pig"
let bull = "bull"
let hash_fn = create_string_item_hash_fn()
let items = [dog, cat, mouse, horse, pig, bull]
let mt = from_list(items, hash_fn)
let item = "parrot"
let proof = get_proof(mt, item, hash_fn)
proof == None
}
fn get_proof_item_value(proof_item: ProofItem) -> Hash<Sha2_256, ByteArray> {
when proof_item is {
Left(x) -> x
Right(y) -> y
}
}
fn create_string_item_hash_fn() -> fn(String) -> Hash<Sha2_256, String> {
fn(x: String) { sha2_256(from_string(x)) }
}