解释器接口

Scilla 解释器提供了一个调用接口,使用户能够使用指定的输入调用 transition 并获得输出。 使用提供的输入执行 transition 将产生一组输出,以及智能合约可变状态的变化。

调用接口

在合约所定义的 transaction 可以通过发行事务,或者从其他合约消息调用而被调用。相同的调用接口将被用来调用通过外部交易和合约间的消息调用合约。

解释器 (scilla-runner) 的输入由四个输入 JSON 文件组成,如下所述。 每次调用解释器来执行 transition 都必须提供这四个 JSON 输入:

./scilla-runner -init init.json -istate input_state.json -iblockchain input_blockchain.json -imessage input_message.json -o output.json -i input.scilla

解释器可执行文件可以运行以创建合约(表示为 CreateContract)或调用合约中的 transition(InvokeContract)。 根据这两者中的任何一个,都将导致某些论点不存在。 下表列出了在这两种情况下应该出现的论点。 CreateContractInvokeContract 的区别在于 input_message.jsoninput_state.json 的存在。 如果这些参数不存在,那么解释器会将其评估为 CreateContract。 否则,它会将其视为 InvokeContract。 请注意,对于 CreateContract,解释器仅执行基本检查,例如将合约的不可变参数与 init.json 匹配以及合约定义有没有语法错误。

当前值

输入

描述

CreateContract

InvokeContract

init.json

不可变合约参数

Yes

Yes

input_state.json

可变合约状态

No

Yes

input_blockchain.json

区块链状态

Yes

Yes

input_message.json

transition 和参数

No

Yes

output.json

输出

Yes

Yes

input.scilla

输入合约

Yes

Yes

除了上面提供的命令行参数之外,解释器还需要一个强制的 -gaslimit X 参数(其中 X 是一个正整数值)。 如果合约或库模块导入其他库(包括标准库),则必须提供 -libdir 选项,以目录列表(标准 PATH 格式)作为参数,指出要搜索的目录从而查找库。

初始化不可变状态

init.json 限定了合约的不可变参数的值。 它不会在调用之间改变。JSON 是一个包含以下字段的对象数组:

字段

描述

vname

不可变合约参数的名称

type

不可变合约参数的类型

value

不可变合约参数的值

init.json 必须指定类型为 Uint32_scilla_version,指定一个与合约源中指定的值相同的值。 此外,区块链将提供两个隐式合约参数 _this_address,一个表示合约本身地址的 ByStr20 值,和 一个 BNum_creation_block,表示以前和现在创建合约的区块。 在使用离线解释器时,你可能需要自己在 init.json 中提供这些值。

例1

对于下面给出的 HelloWorld.scilla 合约片段,我们只有一个不可变参数 owner

contract HelloWorld
 (* Immutable parameters *)
 (owner: ByStr20)

此合约的示例 init.json 将如下所示:

[
    {
        "vname" : "_scilla_version",
        "type" : "Uint32",
        "value" : "0"
    },
    {
        "vname" : "owner",
        "type" : "ByStr20",
        "value" : "0x1234567890123456789012345678901234567890"
    },
    {
        "vname" : "_this_address",
        "type" : "ByStr20",
        "value" : "0xabfeccdc9012345678901234567890f777567890"
    },
    {
        "vname" : "_creation_block",
        "type" : "BNum",
        "value" : "1"
    }
]

例2

对于下面给出的 Crowdfunding.scilla 合约片段,我们有三个不可变参数 ownermax_blockgoal

contract Crowdfunding
    (* Immutable parameters *)
    (owner     : ByStr20,
     max_block : BNum,
     goal      : UInt128)

此合约的示例 init.json 将如下所示:

[
  {
      "vname" : "_scilla_version",
      "type" : "Uint32",
      "value" : "0"
  },
  {
      "vname" : "owner",
      "type" : "ByStr20",
      "value" : "0x1234567890123456789012345678901234567890"
  },
  {
      "vname" : "max_block",
      "type" : "BNum" ,
      "value" : "199"
  },
  {
      "vname" : "_this_address",
      "type" : "ByStr20",
      "value" : "0xabfeccdc9012345678901234567890f777567890"
  },
  {
      "vname" : "goal",
      "type" : "Uint128",
      "value" : "500000000000000"
  },
  {
      "vname" : "_creation_block",
      "type" : "BNum",
      "value" : "1"
  }
]

例3:使用地址类型

每当一个合约有一个地址类型的不可变参数时,类型 ByStr20 必须用于初始化参数。

对于 SimpleExchange,我们有一个不可变参数,它具有地址类型:

contract SimpleExchange
(
  initial_admin : ByStr20 with end
)

initial_admin 参数的 JSON 条目必须使用类型 ByStr20 而不是类型 ByStr20 with end,因此此合约的示例 init.json 可能如下所示:

[
  {
      "vname" : "_scilla_version",
      "type" : "Uint32",
      "value" : "0"
  },
  {
      "vname" : "_this_address",
      "type" : "ByStr20",
      "value" : "0xabfeccdc9012345678901234567890f777567890"
  },
  {
      "vname" : "_creation_block",
      "type" : "BNum",
      "value" : "1"
  },
  {
      "vname" : "initial_admin",
      "type" : "ByStr20",
      "value" : "0x1234567890123456789012345678901234567890"
  }
]

输入区块链状态

input_blockchain.json 将当前区块链状态提供给解释器。 它与 init.json 类似,不同之处在于它是一个固定大小的对象数组,其中每个对象只有来自预定集合(对应于实际区块链状态变量)的 vname 字段。

允许JSON字段: 目前,暴露于合约的唯一区块链值是当前 BLOCKNUMBER

[
    {
        "vname" : "BLOCKNUMBER",
        "type"  : "BNum",
        "value" : "3265"
    }
]

输入消息

input_message.json 包含调用 transition 所需的信息。 这个 json 是一个包含以下四个对象的数组:

字段

描述

_tag

要调用的 transition

_amount

要转移的QA数量

_sender

调用者的地址(在链式调用中,这是直接调用者)

_origin

发起交易的地址

params

参数对象数组

所有四个字段都是强制性的。如果 transition 不带任何参数,则 params 可以为空。

params 数组的编码方式类似于 init.json 的编码方式,每个参数指定必须传递给正在调用的 transition 的 (vnametypevalue)。

例1

对于以下 transition:

transition SayHello()

下面给出了一个示例 input_message.json

{
    "_tag"    : "SayHello",
    "_amount" : "0",
    "_sender" : "0x1234567890123456789012345678901234567890",
    "_origin" : "0x1234567890123456789012345678901234567890",
    "params"  : []
}

例2

对于以下 transition:

transition TransferFrom (from : ByStr20, to : ByStr20, tokens : Uint128)

下面给出了一个示例 input_message.json

{
  "_tag"    : "TransferFrom",
  "_amount" : "0",
  "_sender" : "0x64345678901234567890123456789012345678cd",
  "_origin" : "0x64345678901234567890123456789012345678cd",
  "params"  : [
    {
      "vname" : "from",
      "type"  : "ByStr20",
      "value" : "0x1234567890123456789012345678901234567890"
    },
    {
      "vname" : "to",
      "type"  : "ByStr20",
      "value" : "0x78345678901234567890123456789012345678cd"
    },
    {
      "vname" : "tokens",
      "type"  : "Uint128",
      "value" : "500000000000000"
    }
  ]
}

例3:使用用户自定义类型

注解

由于 Scilla 实现中的错误,本节中的信息仅从 Scilla 版本 0.10.0 开始有效。 使用 0.10.0 之前的 Scilla 版本编写并碰到此错误的合约必须重新编写和重新部署,因为它们将不再适用于 0.10.0 及更高版本。

当通过解释器接口传递用户自定义类型的值时,json 结构与前面示例中描述的相同。 但是,在解释器接口中,所有类型都必须是完全限定的,其定义如下:

  • 对于在地址 A 部署的模块中定义的用户自定义类型 T,完全限定名称是 A.T

  • 对于在地址 A 部署的模块中定义的用户自定义构造函数 C,完全限定名称是 A.C

注解

为了离线开发,模块的地址被定义为模块的文件名,没有文件扩展名。 也就是说,如果合约在文件 F.scilla 中定义了带有构造函数 C 的类型 T,则该类型的完全限定名称为 F.T,构造函数的完全限定名称为 F.C

例如,考虑一个实现简单棋盘游戏的合约。 合约可能定义一个类型 Direction 和一个 transition MoveAction,如下:

type Direction =
| East
| South
| West
| North

...

transition MoveAction (dir : Direction, spaces : Uint32)
  ...

假设合约已部署在地址 0x1234567890123456789012345678906784567890。为了调用带有参数 East2 的 transition,则需使用类型名称 0x1234567890123456789012345678906784567890.Direction 和在消息 JSON 中的构造名称 0x1234567890123456789012345678906784567890.East

{
    "_tag": "MoveAction",
    "_amount": "0",
    "_sender" : "0x64345678901234567890123456789012345678cd",
    "_origin" : "0x64345678901234567890123456789012345678cd",
    "params": [
        {
            "vname" : "dir",
            "type"  : "0x1234567890123456789012345678906784567890.Direction",
            "value" :
            {
                "constructor" : "0x1234567890123456789012345678906784567890.East",
                "argtypes"    : [],
                "arguments"   : []
            }
        },
        {
            "vname" : "spaces",
            "type"  : "Uint32",
            "value" : "2"
        }
    ]
}

如果合约具有用户自定义类型的不可变字段,则还必须使用关联的 init.json 中的完全限定名称初始化这些字段。

例4:使用地址类型

传递地址值时,必须使用类型 ByStr20。 不能在消息中使用地址类型(ByStr20 with ... end)。

这意味着对于接下来的 transition

transition ListToken(
  token_code : String,
  new_token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end
  )

所述 input_message.json 必须使用类型 ByStr20 作为 new_token 参数,例如,如下所示:

{
  "_tag"    : "ListToken",
  "_amount" : "0",
  "_sender" : "0x64345678901234567890123456789012345678cd",
  "_origin" : "0x64345678901234567890123456789012345678cd",
  "params"  : [
    {
      "vname" : "token_code",
      "type"  : "String",
      "value" : "XYZ"
    },
    {
      "vname" : "new_token",
      "type"  : "ByStr20",
      "value" : "0x78345678901234567890123456789012345678cd"
    }
  ]
}

解释器输出

解释器将返回一个带有以下字段的 JSON 对象 (output.json):

字段

描述

scilla_major_version

本合约的 Scilla 语言的主要版本。

gas_remaining

调用或部署合约后剩余的 gas。

_accepted

传入的 QA 是否已被接受("true""false")

message

要发送到另一个合约/非合约账户的消息(如果有)。

states

形成新合约状态的对象数组

events

由 transition 及其调用的 procedure 发出的事件数组。

  • message 是一个与 input_message.json 格式类似的 JSON 对象,除了它有一个 _recipient 字段而不是 _sender 字段。 消息中的字段如下:

    字段

    描述

    _tag

    要调用的 transition

    _amount

    要转移的QA数量

    _recipient

    接收者地址

    params

    要传递的参数对象数组

    params 数组的编码方式类似于 init.json 的编码方式,每个参数指定必须传递给正在调用的 transition 的 (vnametypevalue)。

  • states 是一个对象数组,表示合约的可变状态。 状态数组的每个条目还指定了(vnametypevalue)。

  • events 是一个对象数组,表示 transition 发出的事件。 事件数组中每个对象的字段如下:

    字段

    描述

    _eventname

    事件名称

    params

    额外的事件字段数组

    params 数组的编码方式类似于 init.json 的编码方式,每个参数都需指定事件字段的 (vnametypevalue)。

例1

下面给出了 Crowdfunding.scilla 生成的输出示例。 该示例还显示了合同状态中映射的格式。

{
  "scilla_major_version": "0",
  "gas_remaining": "7365",
  "_accepted": "false",
  "message": {
    "_tag": "",
    "_amount": "100000000000000",
    "_recipient": "0x12345678901234567890123456789012345678ab",
    "params": []
  },
  "states": [
    { "vname": "_balance", "type": "Uint128", "value": "300000000000000" },
    {
      "vname": "backers",
      "type": "Map (ByStr20) (Uint128)",
      "value": [
        { "key": "0x12345678901234567890123456789012345678cd", "val": "200000000000000" },
        { "key": "0x123456789012345678901234567890123456abcd", "val": "100000000000000" }
      ]
    },
    {
      "vname": "funded",
      "type": "Bool",
      "value": { "constructor": "False", "argtypes": [], "arguments": [] }
    }
  ],
  "events": [
    {
      "_eventname": "ClaimBackSuccess",
      "params": [
        {
          "vname": "caller",
          "type": "ByStr20",
          "value": "0x12345678901234567890123456789012345678ab"
        },
        { "vname": "amount", "type": "Uint128", "value": "100000000000000" },
        { "vname": "code", "type": "Int32", "value": "9" }
      ]
    }
  ]
}

例2

对于 ADT 类型的值,value 字段包含三个子字段:

  • constructor:用于构造值的构造函数的名称。

  • argtypes:类型实例的数组。对于 ListOption 类型,这个数组将包含一种类型,分别指示列表中的元素或可选值的类型。对于 Pair 类型,数组将包含两种类型,表示该类型的 pair 中的两个值。对于所有其他抽象数据类型,数组都将是空数组。

  • arguments:该构造函数的参数。

以下示例显示了如何在输出 json 中表示 ListOption 类型的值:

{
  "scilla_major_version": "0",
  "gas_remaining": "7733",
  "_accepted": "false",
  "message": null,
  "states": [
    { "vname": "_balance", "type": "Uint128", "value": "0" },
    {
      "vname": "gpair",
      "type": "Pair (List (Int64)) (Option (Bool))",
      "value": {
        "constructor": "Pair",
        "argtypes": [ "List (Int64)", "Option (Bool)" ],
        "arguments": [
          [],
          { "constructor": "None", "argtypes": [ "Bool" ], "arguments": [] }
        ]
      }
    },
    { "vname": "llist", "type": "List (List (Int64))", "value": [] },
    { "vname": "plist", "type": "List (Option (Int32))", "value": [] },
    {
      "vname": "gnat",
      "type": "Nat",
      "value": { "constructor": "Zero", "argtypes": [], "arguments": [] }
    },
    {
      "vname": "gmap",
      "type": "Map (ByStr20) (Pair (Int32) (Int32))",
      "value": [
        {
          "key": "0x12345678901234567890123456789012345678ab",
          "val": {
            "constructor": "Pair",
            "argtypes": [ "Int32", "Int32" ],
            "arguments": [ "1", "2" ]
          }
        }
      ]
    }
  ],
  "events": []
}

输入可变合约状态

input_state.json 包含可变状态变量的当前值。 它与 output.json 中的 states 字段具有相同的形式。 下面给出了 Crowdfunding.scillainput_state.json 示例。

[
  {
    "vname": "backers",
    "type": "Map (ByStr20) (Uint128)",
    "value": [
      {
        "key": "0x12345678901234567890123456789012345678cd",
        "val": "200000000000000"
      },
      {
        "key": "0x12345678901234567890123456789012345678ab",
        "val": "100000000000000"
      }
    ]
  },
  {
    "vname": "funded",
    "type": "Bool",
    "value": {
      "constructor": "False",
      "argtypes": [],
      "arguments": []
    }
  },
  {
    "vname": "_balance",
    "type": "Uint128",
    "value": "300000000000000"
  }
]