解释器接口¶
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
)。 根据这两者中的任何一个,都将导致某些论点不存在。 下表列出了在这两种情况下应该出现的论点。 CreateContract
与 InvokeContract
的区别在于 input_message.json
和 input_state.json
的存在。 如果这些参数不存在,那么解释器会将其评估为 CreateContract
。 否则,它会将其视为 InvokeContract
。 请注意,对于 CreateContract
,解释器仅执行基本检查,例如将合约的不可变参数与 init.json
匹配以及合约定义有没有语法错误。
当前值 |
|||
---|---|---|---|
输入 |
描述 |
|
|
|
不可变合约参数 |
Yes |
Yes |
|
可变合约状态 |
No |
Yes |
|
区块链状态 |
Yes |
Yes |
|
transition 和参数 |
No |
Yes |
|
输出 |
Yes |
Yes |
|
输入合约 |
Yes |
Yes |
除了上面提供的命令行参数之外,解释器还需要一个强制的 -gaslimit X
参数(其中 X
是一个正整数值)。 如果合约或库模块导入其他库(包括标准库),则必须提供 -libdir
选项,以目录列表(标准 PATH 格式)作为参数,指出要搜索的目录从而查找库。
初始化不可变状态¶
init.json
限定了合约的不可变参数的值。 它不会在调用之间改变。JSON 是一个包含以下字段的对象数组:
字段 |
描述 |
---|---|
|
不可变合约参数的名称 |
|
不可变合约参数的类型 |
|
不可变合约参数的值 |
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
合约片段,我们有三个不可变参数 owner
、max_block
和 goal
。
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 是一个包含以下四个对象的数组:
字段 |
描述 |
---|---|
|
要调用的 transition |
|
要转移的QA数量 |
|
调用者的地址(在链式调用中,这是直接调用者) |
|
发起交易的地址 |
|
参数对象数组 |
所有四个字段都是强制性的。如果 transition 不带任何参数,则 params
可以为空。
params
数组的编码方式类似于 init.json
的编码方式,每个参数指定必须传递给正在调用的 transition 的 (vname
、type
、value
)。
例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
。为了调用带有参数 East
和 2
的 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 语言的主要版本。 |
|
调用或部署合约后剩余的 gas。 |
|
传入的 QA 是否已被接受( |
|
要发送到另一个合约/非合约账户的消息(如果有)。 |
|
形成新合约状态的对象数组 |
|
由 transition 及其调用的 procedure 发出的事件数组。 |
message
是一个与input_message.json
格式类似的 JSON 对象,除了它有一个_recipient
字段而不是_sender
字段。 消息中的字段如下:字段
描述
_tag
要调用的 transition
_amount
要转移的QA数量
_recipient
接收者地址
params
要传递的参数对象数组
params
数组的编码方式类似于init.json
的编码方式,每个参数指定必须传递给正在调用的 transition 的 (vname
、type
、value
)。states
是一个对象数组,表示合约的可变状态。 状态数组的每个条目还指定了(vname
、type
、value
)。events
是一个对象数组,表示 transition 发出的事件。 事件数组中每个对象的字段如下:字段
描述
_eventname
事件名称
params
额外的事件字段数组
params
数组的编码方式类似于init.json
的编码方式,每个参数都需指定事件字段的 (vname
、type
、value
)。
例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
:类型实例的数组。对于List
和Option
类型,这个数组将包含一种类型,分别指示列表中的元素或可选值的类型。对于Pair
类型,数组将包含两种类型,表示该类型的 pair 中的两个值。对于所有其他抽象数据类型,数组都将是空数组。arguments
:该构造函数的参数。
以下示例显示了如何在输出 json 中表示 List
和 Option
类型的值:
{
"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.scilla
的 input_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"
}
]