Skip to content

A chialisp library to develop smart coins on the Chia Blockchain

License

Notifications You must be signed in to change notification settings

hashgreen/cypher-chialisp

Repository files navigation

Cypher | chialisp library 🌱

A chialisp library to develop smart coins (contracts) on the Chia Blockchain by Hashgreen Labs.

  • Implements standards like Chia Asset Token (CAT) and singleton.
  • Object-oriented-like interface for easier development.
  • Reusable components for building complex contracts.

Overview

Installation From Source

Make sure you have Poetry first, then run:

$ git clone https://github.com/hashgreen/cypher.git && cd cypher
$ poetry shell && poetry install

Cypher is planned to be released as a package under PyPI once it reaches a stable version.

Quick Start

$ run --include . tests/test-hello-world.clsp

If dependencies are installed correctly, you should see "hello-world passed".

Usage

Once installed, you can use Cypher in a chialisp contract by importing them with include. The main Cypher library is imported via cypher.clsp, and the re-implemented Chia standards can be imported separately from chia/*.clsp.

(mod ()  ; add variables here
  (include cypher.clsp)
  (include chia/cat-lib.clsp)

  (defun main
    ()  ; begin to write your main logic here about your cool CAT
  )

  (main)
)

You can find a list of exported symbols at the end of each file. They look like:

;; Exports

(defmacro ... args (c ... args))

Testing

We have included tests for the library, written in chialisp as well under tests/. To run the test suite, simply run

$ make -j8 test

Standard Reimplementation

Check out our reimplementation of standard puzzles to understand better how you can use Cypher to achieve your goals.

What Is Cypher For?

The goals of Cypher are to achieve:

  • Boilerplate Reduction

    In many official implementations such as Chia Asset Token, we see countless utility functions copied and pasted over and over. By importing these utilities from Cypher, the chialisp community can reduce the excessive copy-and-pasting, significantly increase the readability in their implementation, and only focus on the critical logic of their smart coins.

  • Security

    As we all know, we learn smart contract security by avoiding others' mistakes, and the official standards are no exception. We aim to incorporate logical checks around some commonly used functions, and ensure the invoked usages do not fall for basic exploits. If you feel brave and completely understand what you are operating, you will have to explicitly opt out by using unsafe variations of the functions to remove these security checks.

  • Common Utilities

    It is annoying to do complex math, especially if you are writing a functional language. We incorporated an array of mathematical utilities ranging from fractional math to logarithms, to make decentralized finance easier. There are also condition-related, macro-related utilities waiting to be explored!

  • Implementation of Standards

    Learning from the official standards can be a great way to pick up chialisp knowledge, and we refactor the official implementation into chialisp scripts that uses Cypher and are much more reader-friendly.

Considerations

You will have a lot of questions to ask about the design of Cypher. We do not promise we have answers to all, and yet we strive hard to follow the conventions of the Lisp language and balance it in the context of chialisp.

  • Choice of Namespace

    If you dive deep enough, you will discover variables exported with different prefixes: no prefix, cf.-prefixed, @cf.-prefixed, and %cf.-prefixed, with the goal of reminding the user the underlying implementation. For example, it has been a practice to directly invoke clvm condition codes such as AGG_SIG_ME or CREATE_COIN with the bare names directly. Other constants are less standardized and putting them under the cf. namespace sounds more reasonable.

    As for @cf. exports like @cf.and, we highlight the fact that they are secretly macros which focus on modifying the bodies of parameters they are passed in.

    In the library, there are some other more complex functions (e.g., cf.log) that tries to use the argument multiple times, and passing arguments in a inline style would make the compiled program a literal hell as the arguments will be expanded everywhere. For these functions the default is to use defun to wrap around the arguments, and you can opt out and use defun-inline by doing %cf.log if you know what you are doing.

  • Object-Oriented-Like

    Lisp is an elegant functional language that has existed for decades. Why make it OOP -- you might ask. This generation of programmers are just too spoiled by object-oriented programming that they cannot simply awe the beauty of Lisp, or its descendant, chialisp. Hence OOP we must... We must...

  • .clsp, .clvm, or .clib?

    There is a long debate how one should name a chialisp implementation file, or a compiled chialisp script, or even a hex file. Across the board, there are who love .clvm extensions and who love .clsp extensions. That said, we decided to name chialisp implementations .clsp, compiled low-level language .clvm, and hex-serialized bytecode .clvm.hex. We will not include a commonly used extension clib, as everything we write are honestly library code, and it would look very much like we are writing library for C language for someone who does not have a sufficient context.

Caveats

There is a pretty significant caveat of using Cypher -- the compile time for your chialisp program may be excruciatingly long! This is because in the implementation of the official compiler reads all symbols and consider all of them in each step of the optimization. Importing Cypher by (import cypher.clsp) would trigger this by introducing all symbols into your program.

For example, compiling the refactored singleton v1.1 took ~70s on a reasonably well machine.

$ run --include . chia/singleton-v1-1.clsp > chia/singleton-v1-1.clvm

There are three options to go

  • You an either directly import the desired second-level library by (import cypher/math.clsp), or
  • You can wait until this new compiler to mature, and hopefully compiles everything blazingly fast!
  • Or... you can pull out pypy magic! See the section below.

Advanced Compilation with Pypy

Importing the full symbol soup from Cypher is known to introduce long compilation time due to the lack of optimization in the vanilla chialisp compiler. Specifically, as detailed in a comparison sheet, the python-based compiler can take up to 11 minutes on a decent linux workstation, while the polished rust-based compiler port can still take up to a minute.

To alleviate the problem, we have improved the vanilla compiler, and switched to pypy as the python interpreter for compilation, the two of which combined give a performance boost of > 50x.

To run compilation in pypy, make sure Anaconda is installed first, then enable pypy mode

$ conda env create -f environment.yml -n pypy
$ PYPY_MODE=1 make clean compile

Contribute

Be a contributor by opening up an issue or pull request!

License

Cypher is released under Apache 2.0.