By George Z. September 7, 2021
7. 使用基准和赎回者
Plutus 中的两个重要元素是数据和赎回者,在这里我们将了解它们是什么以及在提交交易时如何使用这些元素。数据是一条可以与 UTXO 相关联的信息,用于携带脚本状态信息,例如其所有者或时间细节(定义何时可以使用 UTXO)。 它经常与赎回者结合使用——赎回者是交易中包含的任意信息,以向脚本提供输入。
7.1 两阶段交易验证
输入是来自先前交易的未花费输出。数据散列和值(由 ada 数量和可选的额外原生代币数量组成)存储在 UTXO 中的某个地址(公钥或公钥散列)。当脚本地址的 UTXO 是有效交易的输入时,脚本会确定是否“解锁”资金。这可以在脚本定义的特定条件(包括数据、赎回者和脚本上下文等因素的任意组合)下执行。在第一个验证阶段,交易必须由地址对应的私钥的所有者签名。
在赎回者交易的上下文中理解以下概念很重要:
- 脚本地址——存储资金的 Cardano 地址,由 Plutus 脚本保护,可以进一步解锁。它是 Plutus 脚本的散列。
- 数据哈希——在 Cardano 中,数据哈希需要附加到脚本地址处的 UTXO。这样做是为了减少内存需求并在验证交易的同时实现快速访问。
- Plutus 脚本 — 账本中用于执行额外(第二阶段)交易验证的可执行程序。
- 数据值——发送交易赎回时,我们需要发送与锁定交易中发送的数据哈希匹配的数据值。
- Redeemer 值 — 使用与数据相同的任意数据格式。 Redeemer 值附加到输入交易以从脚本解锁资金,并由脚本用于验证交易。
- 脚本上下文——交易的摘要,在 Plutus 脚本中也需要它来验证交易。
使用数据和赎回者的过程如下:假设 Alice 有一个带有 100 个 ADA 的 UTXO:
figure-5
她想将 20 个 ADA 锁定到“datum-redeemer”脚本。 她可以通过向新的 UTXO 发送附加任意数据(即数字 42)的交易来做到这一点:
figure-6
最后,有人可以用 20 个 adas 进行新的交易,这次必须指定一个赎回者:
figure-7
Hello world 使用基准和赎回者的最简单示例是:
- 通过将数据的哈希附加到 UTXO 来锁定脚本地址中的 UTXO。
- 使用与数据对应的赎回者解锁资金。 这在合同中表述如下:
helloWorld :: Data -> Data -> Data -> ()
helloWorld datum redeemer context = if datum P.== redeemer
then ()
else (P.error ())
上面的函数接收三个参数:数据(datum)、赎回者(redeemer), 和脚本上下文(context)。但是,我们只使用数据和救赎者。
7.1.1 先决条件
Plutus 脚本需要使用 GHC 8.10.4 构建。安装此特定版本的一种方法是使用 ghcup。或者,您可以使用 Nix。
7.1.2 编写合同
在本教程中,我们将修改 Alonzo 测试网存储库的示例之一。按着这些次序:
- 克隆 Alonzo 测试网存储库。
- 转到 Alonzo-testnet/resources/plutus-sources/plutus-helloworld 目录。
- 在文件 src/cardano/PlutusExample/HelloWorld.hs 中,将行 helloWorld datum Rededer context = if datum P.== hello then () else (P.error ()) 更改为: helloWorld datum depositer context = if datum P.= = 救赎者 then () else (p.error())。
- 使用命令 cabal run plutus-helloworld – 42 datum-redeemer.plutus 编译代码。
- 结果是一个名为 datum-redeemer.plutus 的文件。
7.1.3 创建脚本地址
现在我们已经编译了合约,我们需要创建脚本地址。为此,请使用以下 cardano-cli 命令:
$ cardano-cli address build --payment-script-file datum-redeemer.plutus --mainnet --out-file datum-redeemer.addr
7.1.4 查询脚本地址
现在我们有了脚本地址,我们可以使用以下命令查看和查询它:
$ cat datum-redeemer.addr
addr_test1wrj2yjcjnpl37fnv74lcwgtc5meefznj490gp2kkuquwt0c84ezsu
$ cardano-cli query utxo --address $(cat datum-redeemer.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
7.1.5 计算数据哈希
要将交易发送到网络,我们需要数据哈希。 这是任意数据的散列。 在本例中,我们使用 42 作为基准。 要计算 42 的哈希值,我们使用以下命令:
$ cardano-cli transaction hash-script-data --script-data-value 42
9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b
7.1.6 查询个人UTXO
首先,我们需要查询将资金发送到脚本地址的地址。 在这个例子中,我们在文件 payment.addr 中有地址:
$ cardano-cli query utxo --address $(cat payment.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
cd4a34a97e8845631c57ec21fed3901e4a4f244e0673eb9e5d478437ec3e9bf4 1 1000000 lovelace + TxOutDatumHashNone
d26ccb16e17b7db97b9578c5f787baaeb63e36dc78134e926bdaeb58a512018d 0 999664124755 lovelace + TxOutDatumHashNone
d26ccb16e17b7db97b9578c5f787baaeb63e36dc78134e926bdaeb58a512018d 1 100000000 lovelace + TxOutDatumHashNone
我们使用包含 100 个 ada(d26ccb16e17b7db97b9578c5f787baaeb63e36dc78134e926bdaeb58a512018d#1)的 UTXO,并使用 build 命令自动计算费用。
7.1.7 下载协议参数文件
在发送我们的第一笔交易之前,我们需要下载包含协议参数的文件,我们使用以下命令执行此操作:
$ cardano-cli query protocol-parameters --out-file protocol.json --mainnet
7.1.8 锁定资金
要锁定由 Plutus 脚本保护的 UTXO,我们需要向 Cardano 网络发送一个带有附加数据哈希的交易。 为此,我们首先需要构建交易:
cardano-cli transaction build \
--alonzo-era \
--tx-in
d26ccb16e17b7db97b9578c5f787baaeb63e36dc78134e926bdaeb58a512018d#1 \
--tx-out $(cat datum-redeemer.addr)+20000000 \
--tx-out-datum-hash 9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b \
--change-address $(cat payment.addr) \
--protocol-params-file protocol.json \
--out-file tx.raw \
--mainnet
由于它是 Alonzo-era 交易,我们需要指定 –alonzo-era 标志。某些 CLI 选项的排序很重要。 当使用 Plutus 脚本锁定 UTXO 时,我们必须在提供给脚本地址的 –tx-out 之后立即提供 –tx-out-datum-hash。 在没有附加数据散列的脚本地址上的 UTXO 被永远锁定!您可以在 Plutus 交易中使用 cardano-cli 找到有关如何在 Alonzo 时代网络上创建交易的步骤。
7.1.9 查询脚本地址
这个脚本地址的 UTXO 现在应该有 8 个 ada 和附加的数据哈希。 要查询它,请运行:
$ cardano-cli query utxo --address $(cat datum-redeemer.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
49ac2bf6a40df00d071b649d1e5b5d3531ddb2bfe6c619f9506cacb557bcb8b6 1 20000000 lovelace + TxOutDatumHash ScriptDataInAlonzoEra "9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b"
7.1.10 错误的Redeemer
我们将通过发送错误的赎回者来测试合约。 让我们记住合同:
helloWorld :: Data -> Data -> Data -> ()
helloWorld datum redeemer context = if datum P.== redeemer
then ()
else (P.error ())
当数据等于赎回者时,该合约成功。 我们刚刚使用价值 42 的数据哈希锁定资金。我们现在将使用不同的赎回值,例如 43。
为此,我们需要发送一个尝试从脚本地址解锁资金的交易。 我们需要来自脚本地址的 UTXO:
$ cardano-cli query utxo --address $(cat datum-redeemer.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
49ac2bf6a40df00d071b649d1e5b5d3531ddb2bfe6c619f9506cacb557bcb8b6 1 20000000 lovelace + TxOutDatumHash ScriptDataInAlonzoEra "9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b"
$ cardano-cli query utxo --address $(cat payment.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
49ac2bf6a40df00d071b649d1e5b5d3531ddb2bfe6c619f9506cacb557bcb8b6 0 79831551 lovelace + TxOutDatumHashNone
cd4a34a97e8845631c57ec21fed3901e4a4f244e0673eb9e5d478437ec3e9bf4 1 1000000 lovelace + TxOutDatumHashNone
d26ccb16e17b7db97b9578c5f787baaeb63e36dc78134e926bdaeb58a512018d 0 999664124755 lovelace + TxOutDatumHashNone
我们还需要一个 UTXO 作为抵押品并支付费用。关于抵押品:在 Alonzo 交易中引入了抵押品,以支付验证节点执行失败脚本的成本。 在这种情况下,所提供的 UTXO 被消耗而不是费用。 最好为 UTXO 提供尽可能少的 ada 以满足抵押品数量。 请注意,抵押金额可能高于交易费用。 提供抵押品的 UTXO 必须只有 ada,没有其他原生资产。我们将使用 build 命令构建交易,看看会发生什么。 在这个例子中,我们使用包含 1 ada 的 UTXO 作为抵押品:
$ cardano-cli transaction build \
--alonzo-era \
--tx-in 49ac2bf6a40df00d071b649d1e5b5d3531ddb2bfe6c619f9506cacb557bcb8b6#1 \
--tx-in-script-file datum-redeemer.plutus \
--tx-in-datum-value 42 \
--tx-in-redeemer-value 43 \
--tx-in-collateral cd4a34a97e8845631c57ec21fed3901e4a4f244e0673eb9e5d478437ec3e9bf4#1 \
--change-address $(cat payment.addr) \
--protocol-params-file protocol.json \
--out-file tx.raw \
--mainnet
Command failed: transaction build Error: The following scripts have execution failures:
the script for transaction input 0 (in the order of the TxIds) failed with The Plutus script evaluation failed: An error has occurred: User error:
The provided Plutus code called 'error'.
现在我们可以看到,我们甚至不能创建错误的交易。 命令 build 首先尝试新事务,以查看在发送之前是否会对其进行验证。
7.1.11 解锁资金
UTXO 仍然被锁定,所以现在我们将解锁它。 与往常一样,我们通过发送交易来做到这一点。 在本次交易中,我们使用脚本地址处的 UTXO 作为输入。 您可以花费不在您地址中(而是在脚本地址中)的交易,因为只要脚本满足其所有条件,它所保护的 UTXO 将被解锁。 在常规地址中,我们使用我们的私钥来解锁资金,而在脚本地址中,我们满足脚本逻辑来解锁资金。 对于这个脚本,赎回者是解锁我们资金的关键。 为此,我们按如下方式构建交易:
$ cardano-cli transaction build \
--alonzo-era \
--tx-in 49ac2bf6a40df00d071b649d1e5b5d3531ddb2bfe6c619f9506cacb557bcb8b6#1 \
--tx-in-script-file datum-redeemer.plutus \
--tx-in-datum-value 42 \
--tx-in-redeemer-value 42 \
--tx-in-collateral cd4a34a97e8845631c57ec21fed3901e4a4f244e0673eb9e5d478437ec3e9bf4#1 \
--change-address $(cat payment.addr) \
--protocol-params-file protocol.json \
--out-file tx.raw \
--mainnet
在这种情况下,我们能够构建交易,因为它具有正确的值。 现在我们可以签署并提交此交易。
$ cardano-cli transaction sign --tx-body-file tx.raw --signing-key-file payment.skey --mainnet --out-file tx.sign
$ cardano-cli transaction submit --mainnet --tx-file tx.sign
Transaction successfully submitted.
7.1.12 查询支付地址和脚本地址
我们可以查询地址以确保我们已经解锁了资金。 现在脚本地址没有UTXO,收到了一个新的8ada:
$ cardano-cli query utxo --address $(cat datum-redeemer.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
$ cardano-cli query utxo --address $(cat payment.addr) --mainnet
TxHash TxIx Amount
--------------------------------------------------------------------------------------
30ccc513f63452602adf43140e5749a11c6ad14f592acda37bf9683e427971c4 0 19788721 lovelace + TxOutDatumHashNone
49ac2bf6a40df00d071b649d1e5b5d3531ddb2bfe6c619f9506cacb557bcb8b6 0 79831551 lovelace + TxOutDatumHashNone
cd4a34a97e8845631c57ec21fed3901e4a4f244e0673eb9e5d478437ec3e9bf4 1 1000000 lovelace + TxOutDatumHashNone
d26ccb16e17b7db97b9578c5f787baaeb63e36dc78134e926bdaeb58a512018d 0 999664124755 lovelace + TxOutDatumHashNone
JSON 格式指南 请考虑以下 JSON 格式指南:
- 为 –script-data-value 传递的 JSON 不能编码某些构造函数形式。
- -tx-in-datum-value 和 –tx-in-redeemer-value 的值被赋予相同的值 {“constructor”:0,“fields”:[{“int”:42}]}
- 注意锁定资金的数据哈希
CLI 支持 |CLI 支持两种输入数据模式:“精确”和“随意”。 –script-data-file 使用精确的样式。如示例中的 {“constructor”:0,“fields”:[{“int”:42}]},–script-data-value 使用休闲风格,例如42 而不是 {“int”: 42}。精确的模式样式需要特定的模式。无模式风格不能对构造函数进行编码。它拒绝浮点数(仅接受整数 (-2^64-1) .. 2^64-1),并拒绝超过 64 字节长的字符串。 它们是不同的 JSON 对象,因此当两者都以无模式风格进行解释时,它们对应于不同的数据值。一个是数字,另一个是一个对象,其中一个字段包含一个数字。在基于模式的方法下, 42 无效(不适合模式)并且 {“int”:42} 将有效并且对应于由单个整数组成的数据值。