Giter Club home page Giter Club logo

clvm_tools's Introduction

Introduction

This is the in-development version of clvm_tools for clvm, which implements, a LISP-like language for encumbering and releasing funds with smart-contract capabilities.

Set up

Set up your virtual environments

$ python3 -m venv venv
$ . ./venv/bin/activate (windows: venv\Scripts\activate.bat)
$ pip install -e .

If you run into any issues, be sure to check out this section of the wiki

Optionally, run unit tests for a sanity check.

$ pip install pytest
$ py.test tests

Quick examples

The language has two components: the higher level language and the compiled lower level language which runs on the clvm. To compile the higher level language into the lower level language use:

$ run '(mod ARGUMENT (+ ARGUMENT 3))'
(+ 1 (q . 3))

To execute this code:

$ brun '(+ 1 (q . 3))' '2'
5

The Compiler

Basic example

The high level language is a superset of clvm, adding several operators. The main supported operator is mod which lets you define a set of macros and functions, and an entry point that calls them. Here's an example.

(mod (INDEX)
     (defun factorial (VALUE) (if (= VALUE 1) 1 (* VALUE (factorial (- VALUE 1)))))
     (factorial INDEX)
     )

You can copy this to a file fact.clvm, then compile it with run fact.clvm and you'll see output like

(a (q 2 2 (c 2 (c 5 ()))) (c (q 2 (i (= 5 (q . 1)) (q 1 . 1) (q 18 5 (a 2 (c 2 (c (- 5 (q . 1)) ()))))) 1) 1))

You can then run this code with brun, passing in a parameter. Or pipe it using this bash quoting trick:

$ brun "`run fact.clvm`" "(5)"
120

This affirms that 5! = 120.

Auto-quoting of literals

Note that the 1 is not quoted. The compiler recognizes and auto-quotes constant values.

$ run 15
15
$ brun 15
FAIL: not a list 15

Known operators

Besides mod and defun, the compiler has a few more built-in operators:

@

Instead of evaluating 1 to return the arguments, you should use @ in the higher level language. This is easier for humans to read, and calling (f @) will be compiled to 2, etc.

    $ run '@' '("example" 200)'
    ("example" 200)
    
    $ run '(mod ARGS (f (r @)))'
    5

You generally won't need to use @; it's better to use mod and named arguments.

(if)

(if A B C) This operator is similar to lone condition in clvm i, except it actually does a lazy evaluation of either B or C (depending upon A). This allows you to put expensive or failing (like x) operator within branches, knowing they won't be executed unless required.

This is implemented as a macro, and expands out to ((c (i A (q B) (q C)) (a))).

(qq) and (unquote)

(qq EXPR) for expanding templates. This is generally for creating your own operators that end up being inline functions.

Everything in EXPR is quoted literally, unless it's wrapped by a unary unquote operator, in which case, it's evaluated. So

(qq (+ 5 (a))) would expand to (+ 5 (a))

But (qq (+ 5 (unquote (+ 9 10)))) would expand to (+ 5 19) because (+ 9 10) is 19.

And (qq (+ 5 (unquote (+ 1 (a))))) expands to something that depends on what (a) is in the context it's evaluated. (It'd better be a number so 1 can be added to it!)

If you have a template expression and you want to substitute values into it, this is what you use.

Macros

You can also define macros within a module, which act as inline functions. When a previously defined macro operator is encountered, it "rewrites" the existing statement using the macro, passing along the arguments as literals (ie. they are not evaluated).

A Simple Example

(mod (VALUE1 VALUE2)
     (defmacro sum (A B) (qq (+ (unquote A) (unquote B))))
     (sum VALUE1 VALUE2)
     )

When run, this produces the following output:

(+ 2 5)

Compare to the function version:

(mod (VALUE1 VALUE2)
     (defun sum (A B) (+ A B))
     (sum VALUE1 VALUE2)
     )

which produces

(a (q 2 2 (c 2 (c 5 (c 11 ())))) (c (q 16 5 11) 1))

There's a lot more going on here, setting up an environment where sum would be allowed to call itself recursively.

Inline functions

If you want to write a function that is always inlined, use defun-inline.

(mod (VALUE1 VALUE2)
     (defun-inline sum (A B) (+ A B))
     (sum VALUE1 VALUE2)
     )

This produces the much more compact output (+ 2 5).

Inline functions must not be recursive.

A More Complex Example

Here's an example, demonstrating how if is defined.

(mod (VALUE1 VALUE2)
     (defmacro my_if (A B C)
       (qq ((c
	    (i (unquote A)
	       (function (unquote B))
	       (function (unquote C)))
	    (a)))))
     (my_if (= (+ VALUE1 VALUE2) 10) "the sum is 10" "the sum is not 10")
     )

This produces

((c (i (= (+ 2 5) (q 10)) (q (q "the sum is 10")) (q (q "the sum is not 10"))) 1))

which is not much code, for how much source there is. This also demonstrates the general notion that macros (and inline functions) cause much less code bloat than functions. The main disadvantages is that macros are not recursive (since they run at compile time) and they're messier to write.

clvm_tools's People

Contributors

altendky avatar aqk avatar arvidn avatar chiaautomation avatar cmmarslender avatar devrandom avatar emlowe avatar hoffmang9 avatar matt-o-how avatar mongolsteppe avatar pmaslana avatar prozacchiwawa avatar quexington avatar richardkiss avatar wallentx avatar yostra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clvm_tools's Issues

Deeply nested clvm program cannot run due to Windows10's poor stack memory

Test environment

- -
OS Windows10 Pro
CPU AMD Ryzen9 5900X
MEM 64GB@3200MHz
Python 3.7.8

How to reproduce

# powershell
C:\Users\XXX\PycharmProjects\> git clone https://github.com/Chia-Network/clvm_tools
C:\Users\XXX\PycharmProjects\> cd clvm_tools
C:\Users\XXX\PycharmProjects\clvm_tools> python3 -m venv ven
C:\Users\XXX\PycharmProjects\clvm_tools> . venv\Scripts\activate.bat
C:\Users\XXX\PycharmProjects\clvm_tools> pip install -e .
C:\Users\XXX\PycharmProjects\clvm_tools> pip install clvm_rs
C:\Users\XXX\PycharmProjects\clvm_tools> python costs\generate-benchmark.py
C:\Users\XXX\PycharmProjects\clvm_tools> brun .\test-programs\all\all_nest-1-962.clvm
1
C:\Users\XXX\PycharmProjects\clvm_tools> echo $LastExitCode
0
C:\Users\XXX\PycharmProjects\clvm_tools> brun .\test-programs\all\all_nest-1-1002.clvm
C:\Users\XXX\PycharmProjects\clvm_tools> echo $LastExitCode
-1073741571

Maybe sys.setrecursionlimit(20000) does not increase stack size, and AFIK there are no easy way to increase the size on Windows.

It is highly unlikely that such a deep nested clvm program is used on coin spend, but at least you should put this fact into your brain.

But there is another approach. Replace recursive function call into loop. This can utilize heap memory instead of stack memory.
If you're interested, I already wrote such a conversion in my javascript implementation of clvm_tools.
Here is my work

In nodejs, it failed to run all_nest-1-1502.clvm due to Maximum call stack size exceeded error.
So I really tried hard to convert the recursion into loop in order to reduce stack memory consumed.
Fortunately as I develop javascript version of clvm_tools really looking like Python's code, you can also apply my work back into the Python's clvm_tools

Missing tarball on Pypi

I am trying to build an RPM from this but see no tar.gz file on pypi. Can you please upload the source tarball to pypi ?

warning: Downloading https://pypi.io/packages/source/c/clvm_tools/clvm_tools-0.4.3.tar.gz
curl: (22) The requested URL returned error: 404
error: Couldn't download https://pypi.io/packages/source/c/clvm_tools/clvm_tools-0.4.3.tar.gz

https://pypi.org/project/clvm-tools/#files only shows a .whl file.

$python3 setup.py --help-commands | grep sdist
sdist create a source distribution (tarball, zip file, etc.)

so if you can first generate this sdist tar ball then upload it

version issue in 0.4.3 and test isn't written for setup.py?

Here is a patch that fixes a compile issue that makes it not able to find clvm_tools in chia-blockchain-1.1.6:

--- pyproject.toml.orig	2021-05-30 14:12:11.254703000 -0400
+++ pyproject.toml	2021-05-30 14:11:35.404486000 -0400
@@ -3,5 +3,5 @@
 requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"]

 [tool.setuptools_scm]
-fallback_version = "unknown"
+fallback_version = "0.4.3"
 local_scheme = "no-local-version"

Setup on Ubuntu 18.04

Hi,

This isn't really a bug but I recently had some issues getting a functional clvm environment on my server running Ubuntu 18.04 and I thought that I should share the steps that I had to follow here.

First I've wasted a lot of time trying to make things work without really checking the version of Python I was using. Turns out that I was using python 3.6 and that IIUC for the blspy dependency to be resolved properly you need at least 3.7.

Then I still had a few issues, here's the commands I had to run:

git clone https://github.com/Chia-Network/clvm_tools.git
cd clvm_tools
python3 -m venv clvm_env
. ./clvm_env/bin/activate
pip install -e .
git clone https://github.com/Chia-Network/clvm.git
cd clvm
python3 -m venv venv
. ./venv/bin/activate
pip install -e '.[dev]'
cd ..
run '(mod ARGUMENT (+ ARGUMENT 3))'

I could probably create only one venv but once I got it to work I focused on playing with Chialisp :).

Let me know if you want me to try something, I really believe that providing some really clear setup instructions that work on all platforms will help with the adoption of Chialisp.

Thanks

Compiler fails sometimes when parameter deconstruction not used

I believe that I maybe found a bug in the compiler.

Steps to reproduce:

cat > puzzle.cl <<EOF
(mod params
 (defun ident (arg)
  (f (list arg))
 )

 (ident (ident params))
)
EOF

run puzzle.cl

Expected result:

I expect a CLVM puzzle to be written to stdout, which would output its solution when passed to brun (i.e. "identity puzzle").

Actual results:

The run command fails with the following message:

FAIL: first of non-cons 1

Some more insight:

When I modify the puzzle in either one of the following ways, then it translates and the resulting CLVM behaves as expected:

  1. (f (list arg)) -> (f arg)
  2. (f (list arg)) -> (list arg)
  3. (f (list arg)) -> (f (f arg))
  4. (f (list arg)) -> (list (list arg))
  5. (f (list arg)) -> (list (f arg))
  6. (ident (ident params)) -> (ident params)
  7. (mod params ...) -> (mod (params) ...)

Please note that the documentation admits any of the following possibilities:

  • (mod params ...)
  • (mod (param_one param_two) ...)
  • (mod ((param)) ...)

Quoting https://chialisp.com/docs/high_level_lang#squaring-a-list:

You can name each parameter in a list or you can name the list itself. This works at any place where you name parameters, and allows you to handle lists where you aren't sure of the size.

Decentralized Identity

hi @matt-o-how
there was a workshop at Jul 17, 2020 about Decentralized Identity in chia with Matt Howard , but I cannot find any further news about this topic .

is it possible to use chia block chain as a DIDs method ?
is there any progress or implementation in this field?

thank you

`NodePath(-129)` raises an error, while `NodePath(-256)` does not

How to reproduce the error

n = NodePath(-129)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "c:\users\chiaminejp\pycharmprojects\clvm_tools\clvm_tools\NodePath.py", line 67, in __init__
    blob = index.to_bytes(byte_count, byteorder="big", signed=True)
OverflowError: int too big to convert

NodePath(-256)

n = NodePath(-256)
str(n)
'NodePath: 65280'

Feel strange on comparing `run_program` implementation of stage_0, stage_1, stage_2

stage_0

def run_program(
    program,
    args,
    operator_lookup=OPERATOR_LOOKUP,    # <== operator_lookup can be changed by parameter
    max_cost=None,
    pre_eval_f=None,
    strict=False,
):
    ...

stage_1

class RunProgram:
    def __init__(self):
        operator_lookup = OperatorDict(OPERATOR_LOOKUP)
        operator_lookup.update((k.encode("utf8"), v) for (k, v) in BINDINGS.items())
        self.operator_lookup = operator_lookup

    # __call__  does not accept `operator_lookup` here. So `operator_lookup` can't be changed outside by parameter.
    def __call__(self, program, args, max_cost=None, pre_eval_f=None, strict=False):
        return run_program_0(
            program,
            args,
            operator_lookup=self.operator_lookup,
            max_cost=max_cost,
            pre_eval_f=pre_eval_f,
            strict=strict,
        )

stage_2

    # As well as stage_0, operator_lookup can be changed by parameter, but this is different from stage_1.
    def run_program(
        program, args, operator_lookup=operator_lookup, max_cost=None, pre_eval_f=None, strict=False
    ):
        return run_program_0(
            program,
            args,
            operator_lookup=operator_lookup,
            max_cost=max_cost,
            pre_eval_f=pre_eval_f,
            strict=strict
        )

The strangeness comes from the facts that:
stage_0: run_program accepts operator_lookup by parameter. It can be modified from outside by parameter.
stage_1: run_program DOES NOT accepts operator_lookup by parameter. It cannot be modified from outside by parameter.
stage_2: Does not inherit stage_1 spec. Equipping stage_0 style argument.

I feel inconsistency of run_program specification between stage_1 and stage_2.
It is clean if stage_1 also accepts operator_lookup parameter as well as other stages, or cut operator_lookup parameter from stage_2 as well as stage_1.

I don't know whether accepting operator_lookup parameter is important or not.
Maybe the best advise to my feeling is to just ignore and walk away.

But I want to hear from original author/developer, about whether this is important for entire specs or just a tiny small thing I should pass through.

Possible impact

In clvm_tools/cmds.py, user can change stage by specifying -s or --stage option.
And I found the code below, which switches run_program implemantation according to -s option.

    if hasattr(args.stage, "run_program_for_search_paths"):
        run_program = args.stage.run_program_for_search_paths(args.include)
    else:
        run_program = args.stage.run_program

And later, it is invoked without operator_lookup parameter.

            cost, result = run_program(
                run_script, input_sexp, max_cost=max_cost, pre_eval_f=pre_eval_f, strict=args.strict)

So , in current clvm_tools/cmds.py there is no issue at all.

But what if run_program above is invoked with operator_lookup parameter in future?
If it is then, it becomse that stage_1 implementation of run_program ignores operator_lookup while stage_2's impl doesn't.
Is this expected behaviour?

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.