【Chialisp手册】Chialisp 中的常用函数
5 - Chialisp 中的常用函数
当您开始编写完整的智能硬币时,您将开始意识到在许多谜题中您将需要某些通用功能。让我们来看看如何包含它们以及其中一些是什么:
包括
如果您想导入一些您经常使用的功能而不必在文件之间复制/粘贴它,您可以使用include
:
;; condition_codes.clvm
(
(defconstant AGG_SIG_ME 50)
(defconstant CREATE_COIN 51)
)
;;main.clvm
(mod (pubkey msg puzzle_hash amount)
(include "condition_codes.clvm")
(list (list AGG_SIG_ME pubkey msg) (list CREATE_COIN puzzle_hash amount))
)
使用 运行 main.clvm 时run
,请确保使用该-i
选项指定要在哪些目录中查找可包含文件。如果我们的 condition_codes.clvm 文件在目录中,./libraries/chialisp/
那么您将把它传递给run
它,以便它知道在哪里可以找到它:
run -i ./libraries/chialisp/ main.clvm
另请注意,包含文件是一种特殊格式。定义的所有内容都放入一组括号中,就像上面的 condition_codes.clvm 一样。然后,您可以在编写程序时使用这些常量/函数中的任何一个,而无需单独导入每个常量/函数。编译器将只包含您使用的内容,因此在尝试优化程序大小时不要担心包含大型库文件。
sha256tree1
当拼图被散列时,它们不会被简单地序列化并传递给 sha256。相反,我们采用拼图的树哈希。
回想一下,每个 clvm 程序都可以表示为二叉树。每个对象要么是一个原子(树的叶子),要么是一个 cons 框(树的一个分支)。当我们对拼图进行散列时,我们从树的叶子开始并向上散列,连接一个 1 或一个 2 来表示它是一个原子或一个 cons 框。一旦 cons 框被散列,它就会成为一个新的叶子,被散列到它的父 cons 框中,并且这个过程会递归。这是 Chialisp 中的样子:
(defun sha256tree1
(TREE)
(if (l TREE)
(sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
(sha256 1 TREE)
)
)
在 Chialisp 拼图中计算树哈希非常有用。您可以断言其他硬币的谜题,压缩谜题以便于签名,并根据一些传入的数据创建 CREATE_COIN 条件。
Currying(咖喱)
Currying 是 Chialisp 中一个极其重要的概念,它几乎负责整个状态如何存储在硬币中。这个想法是在散列之前将参数传递给谜题。当你Currying时,你承诺解决方案的价值,这样解决难题的个人就不能改变它们。我们来看看这是如何在 Chialisp 中实现的:
;; utility function used by curry
(defun fix_curry_args (items core)
(if items
(qq (c (q . (unquote (f items))) (unquote (fix_curry_args (r items) core))))
core
)
)
; (curry sum (list 50 60)) => returns a function that is like (sum 50 60 ...)
(defun curry (func list_of_args) (qq (a (q . (unquote func)) (unquote (fix_curry_args list_of_args (q . 1))))))
这如此有用的原因是因为您可能想要创建拼图的蓝图,但每次创建时对某些参数使用不同的值。您不能依赖解谜器诚实、正确地传递您想要使用的信息,因此您需要确保在他们有机会解决它之前传递了它。
上面的函数可能看起来很复杂,但它真正做的就是将函数包装在 an 中,a
并在其前面添加参数1
(当编译为 clvm 时)将引用其余的拼图参数。没有所有的引号,上面的代码简化为这样的:
(a func (c curry_arg_1 (c curry_arg_2 1)))
你也可以做相反的操作。给定一个程序,你可以用一个简单的方法解开参数列表(f (r (r )))
:
(f (r (r curried_func)))
; (c curry_arg_1 (c curry_arg_2 1))
让我们以之前的密码锁定硬币为例,这次是作为Chialisp迷题:
(mod (password new_puzhash amount)
(defconstant CREATE_COIN 51)
(defun check_password (password new_puzhash amount)
(if (= (sha256 password) (q . 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824))
(list (list CREATE_COIN new_puzhash amount))
(x)
)
)
; main
(check_password password new_puzhash amount)
)
您可以看到密码散列被融入到谜题的来源中。这意味着每次您想用新密码锁定硬币时,您都必须重新创建包含代码源的文件。如果我们完全概括它会更好:
(mod (PASSWORD_HASH password new_puzhash amount)
(defconstant CREATE_COIN 51)
(defun check_password (PASSWORD_HASH password new_puzhash amount)
(if (= (sha256 password) PASSWORD_HASH)
(list (list CREATE_COIN new_puzhash amount))
(x)
)
)
; main
(check_password PASSWORD_HASH password new_puzhash amount)
)
但是,现在我们遇到的问题是任何人都可以输入他们喜欢的任何密码/哈希组合并解锁该硬币。当我们创建这个硬币时,我们需要提交密码哈希。在确定我们将要创建的硬币的拼图哈希之前,我们需要在哈希中使用如下内容:
; curry_password_coin.clvm
(mod (password_hash password_coin_mod)
(include "curry.clvm") ; From above
(curry password_coin_mod (list password_hash))
)
如果我们编译这个函数并像这样传递参数:
brun <curry password coin mod> '((q . 0xcafef00d) (q . <password coin mod>))'
我们将收到一个看起来与我们的密码硬币模块非常相似的谜题,但已扩展为包含我们传入的哈希值。您现在可以使用不同的密码哈希值运行上面的柯里化模块,它每次都会输出一个新的谜题。然后我们可以对拼图进行哈希处理,并使用返回的拼图哈希创建一个硬币。
请注意,这要求我们brun
在自己的链下环境中运行柯里化模块,以创建我们将用来锁定硬币的拼图。很多时候这种柯里化会发生在 python 或创建硬币的软件使用的任何包装语言中。但是,在某些用例中,我们希望在拼图的范围内使用柯里化。现在让我们看一看。
外部和内部谜题
一个常见的设计模式,也是 Chialisp 最强大的功能之一,是能够拥有一个外部谜题“包裹”内部谜题。这个概念非常方便,因为它允许硬币在内部拼图内保留它的所有标准功能和可编程性,但由外部拼图绑定到一组额外的规则。
对于这个例子,我们将继续密码锁定,但这次我们将要求每次花费硬币时,都需要设置一个新密码。让我们看一下所有代码,然后我们将其分解:
(mod (
MOD_HASH ;; curried in
PASSWORD_HASH ;; curried in
INNER_PUZZLE ;; curried in
inner_solution
password
new_password_hash
)
(include "condition_codes.clvm")
(include "sha256tree1.clvm")
(include "curry-and-treehash.clvm")
(defun pw-puzzle-hash (MOD_HASH mod_hash_hash new_password_hash_hash inner_puzzle_hash)
(puzzle-hash-of-curried-function
MOD_HASH
inner_puzzle_hash new_password_hash_hash mod_hash_hash ; parameters must be passed in reverse order
)
)
;; tweak `CREATE_COIN` condition by wrapping the puzzle hash, forcing it to be a password locked coin
(defun-inline morph-condition (condition new_password_hash MOD_HASH)
(if (= (f condition) CREATE_COIN)
(list CREATE_COIN
(pw-puzzle-hash MOD_HASH (sha256tree1 MOD_HASH) (sha256tree1 new_password_hash) (f (r condition)))
(f (r (r condition)))
)
condition
)
)
;; tweak all `CREATE_COIN` conditions, enforcing created coins to be locked by passwords
(defun morph-conditions (conditions new_password_hash MOD_HASH)
(if conditions
(c
(morph-condition (f conditions) new_password_hash MOD_HASH)
(morph-conditions (r conditions) new_password_hash MOD_HASH)
)
()
)
)
; main
(if (= (sha256 password) PASSWORD_HASH)
(morph-conditions (a INNER_PUZZLE inner_solution) new_password_hash MOD_HASH)
(x "wrong password")
)
)
您可能会注意到我们导入了一个名为curry-and-treehash
. 我们将在几个步骤中讨论这个问题。
首先,让我们谈谈论点。当你第一次创建这个谜题时,你需要在 3 件事中进行柯里化:MOD_HASH
这是没有柯里化参数的代码的树哈希,PASSWORD_HASH
这是解锁这个硬币的密码的哈希,以及INNER_PUZZLE
这是一个完全独立的谜题这将有自己的关于如何使用硬币的规则。
Chialisp 谜题倾向于自下而上阅读,所以让我们从这一块开始:
; main
(if (= (sha256 password) PASSWORD_HASH)
(morph-conditions (a INNER_PUZZLE inner_solution) new_password_hash MOD_HASH)
(x "wrong password")
)
这里发生的所有事情是我们确保密码正确,如果正确,我们将INNER_PUZZLE
使用传入的inner_solution
. 这将返回我们将传递给下一个函数的条件列表以及新密码哈希和MOD_HASH
。
;; tweak all `CREATE_COIN` conditions, enforcing created coins to be locked by passwords
(defun morph-conditions (conditions new_password_hash MOD_HASH)
(if conditions
(c
(morph-condition (f conditions) new_password_hash MOD_HASH)
(morph-conditions (r conditions) new_password_hash MOD_HASH)
)
()
)
)
递归是 Chialisp 的基础,编写它时经常会出现这样的函数。为了遍历条件列表,我们首先检查是否还有剩余的项目(请记住,空列表()
或nil 的计算结果为 false)。然后,我们对第一个条件进行变形并将其与列表其余部分的递归输出连接起来。最后,我们将以相同的顺序拥有相同的项目列表,但它们都将通过morph-condition
.
;; tweak `CREATE_COIN` condition by wrapping the puzzle hash, forcing it to be a password locked coin(defun-inline morph-condition (condition new_password_hash MOD_HASH) (if (= (f condition) CREATE_COIN) (list CREATE_COIN (pw-puzzle-hash MOD_HASH (sha256tree1 MOD_HASH) (sha256tree1 new_password_hash) (f (r condition))) (f (r (r condition))) ) condition ))
这个功能也很简单。我们首先检查操作码(列表中的第一项)是否为 CREATE_COIN。如果不是,只需像往常一样返回条件。如果是,则返回一个几乎完全相同的条件,除了我们将拼图哈希传递给将修改它的函数:
(defun pw-puzzle-hash (MOD_HASH mod_hash_hash new_password_hash_hash inner_puzzle_hasr
(puzzle-hash-of-curried-function
MOD_HASH
inner_puzzle_hash new_password_hash_hash mod_hash_hash ; parameters must be passed in reverse order
)
)
这就是令人兴奋的事情发生的地方。由于我们不知道内部拼图,只知道它的哈希值,因此无法将其直接放入我们要创建的下一个拼图中。此外,如果我们不想在每次使用当前模块时都传入它的整个源代码,那么我们也没有什么难题可以将其放入其中。
然而,我们所关心的只是为下一个谜题生成正确的谜题哈希,我们确实有这个模块和内部谜题的树哈希。我们可以使用puzzle-hash-of-curried-function
which 允许我们创建给定函数的拼图哈希:a) 该函数的拼图哈希和 b) 其所有参数的拼图哈希以相反的顺序,就好像它们是树哈希的一部分一样。这意味着原子和数字的参数应该采用树哈希形式,带有 1 前缀,(sha256 (q . 1) my-argument-value)
并且 的输出sha256tree
适用于任何涉及 cons 单元的内容。这将是可能的puzzle-hash-of-curried-function
猜测,这些如果花的参数值本身,而是可能需要昂贵的哈希重新计算。
这个库的其他实现细节在本教程的这一部分中有点多,但本质上,它允许我们恢复除最后一步之外我们已经完成的树哈希。
就是这样!创建此币时,只能通过散列到 PASSWORD_HASH 中的柯里的密码来使用它。内部拼图可以是您想要的任何东西,包括其他具有自己内部拼图的外部拼图。由于该内部拼图而产生的任何硬币都将被该相同的外部拼图“包裹”,以确保该硬币的每个子代都被密码永久锁定。
我们创建了一个简单的硬币,但您可以看到它的潜力。您不仅可以对您锁定的硬币执行一组规则,还可以对每个后代硬币执行一组规则。不仅如此,这些规则还可以在其他智能代币之上执行。在 Chialisp 生态系统中,除非堆栈中的某个谜题另有说明,否则所有智能币都可以相互操作。可能性是无限的,代表了 Chialisp 为硬币提供的巨大可编程性。
在下一节中,我们将讨论 Chia 网络上的标准交易格式。
谢谢
观看