Skip to content

Commit

Permalink
optimize Program.curry() (#11162)
Browse files Browse the repository at this point in the history
* optimize Program.curry()

* Add a comment for this curry implementation

* optimize Program.curry()

* Add comment

Co-authored-by: arty <art.yerkes@gmail.com>
  • Loading branch information
arvidn and prozacchiwawa authored Apr 20, 2022
1 parent cd9beac commit 73cd386
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 4 deletions.
26 changes: 22 additions & 4 deletions chia/types/blockchain_format/program.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import io
from typing import List, Set, Tuple, Optional
from typing import List, Set, Tuple, Optional, Any

from clvm import SExp
from clvm.casts import int_from_bytes
from clvm.EvalError import EvalError
from clvm.serialize import sexp_from_stream, sexp_to_stream
from chia_rs import MEMPOOL_MODE, run_chia_program, serialized_length, run_generator
from clvm_tools.curry import curry, uncurry
from clvm_tools.curry import uncurry

from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.hash import std_hash
Expand Down Expand Up @@ -88,9 +88,27 @@ def run(self, args) -> "Program":
cost, r = self.run_with_cost(INFINITE_COST, args)
return r

# Replicates the curry function from clvm_tools, taking advantage of *args
# being a list. We iterate through args in reverse building the code to
# create a clvm list.
#
# Given arguments to a function addressable by the '1' reference in clvm
#
# fixed_args = 1
#
# Each arg is prepended as fixed_args = (c (q . arg) fixed_args)
#
# The resulting argument list is interpreted with apply (2)
#
# (2 (1 . self) rest)
#
# Resulting in a function which places its own arguments after those
# curried in in the form of a proper list.
def curry(self, *args) -> "Program":
cost, r = curry(self, list(args))
return Program.to(r)
fixed_args: Any = 1
for arg in reversed(args):
fixed_args = [4, (1, arg), fixed_args]
return Program.to([2, (1, self), fixed_args])

def uncurry(self) -> Tuple["Program", "Program"]:
r = uncurry(self)
Expand Down
28 changes: 28 additions & 0 deletions tests/clvm/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

from chia.types.blockchain_format.program import Program
from clvm.EvalError import EvalError
from clvm_tools.curry import uncurry
from clvm.operators import KEYWORD_TO_ATOM
from clvm_tools.binutils import assemble, disassemble


class TestProgram(TestCase):
Expand All @@ -19,3 +22,28 @@ def test_at(self):

self.assertRaises(ValueError, lambda: p.at("q"))
self.assertRaises(EvalError, lambda: p.at("ff"))


def check_idempotency(f, *args):
prg = Program.to(f)
curried = prg.curry(*args)

r = disassemble(curried)
f_0, args_0 = uncurry(curried)

assert disassemble(f_0) == disassemble(f)
assert disassemble(args_0) == disassemble(Program.to(list(args)))
return r


def test_curry_uncurry():
PLUS = KEYWORD_TO_ATOM["+"][0]
f = assemble("(+ 2 5)")
actual_disassembly = check_idempotency(f, 200, 30)
assert actual_disassembly == f"(a (q {PLUS} 2 5) (c (q . 200) (c (q . 30) 1)))"

f = assemble("(+ 2 5)")
args = assemble("(+ (q . 50) (q . 60))")
# passing "args" here wraps the arguments in a list
actual_disassembly = check_idempotency(f, args)
assert actual_disassembly == f"(a (q {PLUS} 2 5) (c (q {PLUS} (q . 50) (q . 60)) 1))"

0 comments on commit 73cd386

Please sign in to comment.