Byteball的地址、脚本及合约
地址的定义
Byteball中用户使用地址进行收发交易。地址本质上对应的是一段具有特定含义的脚本,该脚本称为地址的定义。任何能够使地址定义脚本输出为真(也称作解锁该脚本)的人具有使用该地址资产的权限。与Bitcoin类似,最常用的地址定义脚本是公钥(采用BASE64编码),即具有相应私钥的人可以使用该地址的资产,比如
["sig",{"pubkey":"Ald9tkgiUZQQ1djpZgv2ez7xf1ZvYAsTLhudhvn0931w"}]
对于地址定义脚本进行哈希,再加上校验位就得到了地址,Byteball的地址采用BASE32编码。Byteball地址的校验位并不是全部放在尾部,而是穿插着放在哈希值中间,防止有攻击者在地址中间进行恶意修改。
按照此流程,上面公钥脚本对应的地址为:
A2WWHN7755YZVMXCBLMFWRSLKSZJN3FU
如果地址仅用于接收交易,其定义脚本可以不对外公布。但是当用户首次使用该地址进行发送交易时,他需要在发送的单元中声明该地址的定义脚本,比如
unit: {
...
authors: [ {
address: 'DJ6LV5GPCLMGRW7ZB55IVGJRPDJPOQU6',
definition: [
"sig", {"pubkey":"AsnvZ3w7N1lZGJ+P+bDZU0DgOwJcGJ51bjsWpEqfqBg6"}
],
authentifiers: {
r: '3eQPIFiPVLRwBwEzxUR5thqn+zlFfLXUrzAmgemAqOk35UvDpa4h79Fd6TbPbGfb8VMiJzqdNGHCKyAjl786mw=='
}
} ],
...
}
其中,authentifiers
是用户采用私钥对除authentifiers
之外的数据进行的签名。在用户使用该地址首次发送单元之后,它不允许再发送地址的定义。当然,只有在该地址的第一个单元到达稳定后,用户才可以发送后续单元。
用户可以在保持地址不变的条件下修改地址的定义脚本,用户需要发送消息
unit: {
...
messages: [
...
{
app: "address_definition_change",
definition_chash: "I4Z7KFNIYTPHPJ5CA5OFC273JQFSZPOX"
},
...
],
...
}
definition_chash
为新的地址定义脚本生成的地址。那么,下一个从原地址发出的单元有以下两条要求:
- 必须把
address_definition_change
这个单元作为其last_ball
; - 在修改地址定义脚本后发出第一个单元时,需要把新的定义脚本作为第一条
message
。
显然,新的地址定义脚本生成的地址跟原地址是不相同的。当用户迁移到新的设备上,同时想保持地址不变时,可以使用这种方式来修改地址定义脚本。
地址定义脚本中必须显式地(使用sig
)或隐式地(使用address
)包含至少一个sig
。为了防止消耗过量的资源,脚本的操作总数限制在100以内,包括授权地址及脚本模板中的所有操作。
相比于Ethereum,Byteball的脚本语言的解释能力有限,它定义的几乎都是逻辑判断语句。但是,Byteball本身是为了提供给那些并不太懂编程的人群使用的,其语言必须便于理解且不容易出错。
逻辑运算脚本
与运算:当多个条件同时满足时,脚本输出为真。比如,同时需要两个私钥签名的脚本
["and", [
["sig", {pubkey: "one pubkey in base64"}],
["sig", {pubkey: "another pubkey in base64"}]
]]
或运算:多个条件中有一个满足时,脚本输出为真。比如,仅需要laptop
、smartphone
或者talet
中某一个私钥就可以解锁的脚本
["or", [
["sig", {pubkey: "laptop pubkey"}],
["sig", {pubkey: "smartphone pubkey"}],
["sig", {pubkey: "tablet pubkey"}]
]]
非运算:脚本中不含sig
、hash
、address
、cosigned by
或者in merkle
的条件可以进行非运算,比如
["not", [
"in data feed",
[["NOAA ADDRESS"], "wind_speed", ">", "200"]
]]
逻辑嵌套:逻辑运算可以嵌套使用。比如,必须同时拥有smartphone
私钥以及laptop
或者tablet
中某一个私钥就可以解锁的脚本
["and", [
["or", [
["sig", {pubkey: "laptop pubkey"}],
["sig", {pubkey: "tablet pubkey"}]
]],
["sig", {pubkey: "smartphone pubkey"}]
]]
最小数量运算:当满足条件的个数超过门限时,脚本输出为真。比如,具有2个以上私钥就可以解锁的脚本
["r of set", {
required: 2,
set: [
["sig", {pubkey: "laptop pubkey"}],
["sig", {pubkey: "smartphone pubkey"}],
["sig", {pubkey: "tablet pubkey"}]
]
}]
最低权重运算:当满足条件的权重值超过门限时,脚本输出为真。比如,当几个私钥签名的权重之和大于50时可以解锁的脚本
["weighted and", {
required: 50,
set: [
{weight: 40, value: ["sig", {pubkey: "CEO pubkey"}] },
{weight: 20, value: ["sig", {pubkey: "COO pubkey"}] },
{weight: 20, value: ["sig", {pubkey: "CFO pubkey"}] },
{weight: 20, value: ["sig", {pubkey: "CTO pubkey"}] }
]
}]
地址授权脚本
授权使用其它地址来解锁脚本,其定义的语法为
["and", [
["address", "ADDRESS 1 IN BASE32"],
["address", "ADDRESS 2 IN BASE32"]
]]
这可以很方便地用来构造共享控制的地址。比如,上面给出的地址定义脚本生成的地址将由ADDRESS1
和ADDRESS2
共同控制。
共同签名脚本
要求与另一个地址共同签名才可以解锁脚本
["cosigned by", "ANOTHER ADDRESS IN BASE32"]
地址已用脚本
要求由某个地址发出的单元至少有一个成为last_ball_unit
["seen address", "ANOTHER ADDRESS IN BASE32"]
数据订阅脚本
通过订阅的数据是否符合条件来解锁脚本,其语法格式为
["in data feed", [
["ADDRESS1", "ADDRESS2", ...],
"data feed name",
"=",
"expected value"
]]
上述脚本表示:当数据源地址ADDRESS1
、ADDRESS2
等中某个地址发出的消息中订阅数据data feed name
等于expected value
时,脚本输出为真。
地址发出的数据订阅消息格式为
unit: {
...
messages: [
...
{
app: "data_feed",
payload_location: "inline",
payload_hash: "hash of payload",
payload: {
"data feed name": "value",
"another data feed name": "value2",
...
}
},
...
],
...
}
对赌合约
当某个地址可以作为可靠的数据订阅源时,用户可以使用其作为外部数据条件来构造合约。比如,
["or", [
["and", [
["address", "ADDRESS 1"],
["in data feed", [["EXCHANGE ADDRESS"], "EURUSD", ">", "1.1500"]]
]],
["and", [
["address", "ADDRESS 2"],
["in data feed", [["TIMESTAMPER ADDRESS"], "datetime", ">", "2016-10-01 00:00:00"]]
]]
]]
上述脚本给出了ADDRESS 1
和ADDRESS 2
之间的一个简单合约,假设其对应的地址为ADDRESS X
。当EXCHANGE ADDRESS
发布的汇率数据EURUSD
大于1.1500
时,仅使用ADDRESS 1
的私钥就可以取走ADDRESS X
中的资产。而当TIMESTAMPER ADDRESS
发布的时间数据datetime
大于2016-10-01 00:00:00
时,仅使用ADDRESS 2
的私钥就可以取走ADDRESS X
中的资产。也就是说,上述脚本定义的是对赌合约:如果2016-10-01 00:00:00
之前EURUSD
汇率超过1.1500
,地址ADDRESS 1
获胜,否则地址ADDRESS 2
获胜。
商品合约
当顾客购买商品时,也可以使用上述方式来制定合约,比如
["or", [
["and", [
["address", "MERCHANT ADDRESS"],
["in data feed", [["FEDEX ADDRESS"], "tracking", "=", "123456"]]
]],
["and", [
["address", "BUYER ADDRESS"],
["in data feed", [["TIMESTAMPER ADDRESS"], "datetime", ">", "2016-10-01 00:00:00"]]
]]
]]
上述脚本给出了顾客BUYER ADDRESS
和商户MERCHANT ADDRESS
之间的合约,假设其对应的地址为ADDRESS Y
。顾客在购买商品时,将款项打入地址ADDRESS Y
。如果快递公司FEDEX ADDRESS
发布数据表明相应的快递已签收,则商户MERCHANT ADDRESS
可以从ADDRESS Y
中取走货款;如果TIMERSTAMPER ADDRESS
发布的时间数据datetime
大于2016-10-01 00:00:00
时,则顾客BUYER ADDRESS
可以从ADDRESS Y
中取回货款。
上述场景中,快递公司需要对每一个快递都发布其签收状态数据,这将需要发布大量的数据。Merkle数据订阅可以降低需要发布的数据量。只需要核实关心的hash
值出现在数据源地址发布的Merkle树中时,即可证明该事件已发生。其定义语法如下:
["in merkle", [
["ADDRESS1", "ADDRESS2", ...],
"data feed name",
"hash of expected value"
]]
此时,快递公司只需要定期将一大批快递状态构造Merkle树,并发布Merkle根即可。商户可以通过相应快递的Merkle路径来解锁Merkle数据订阅的脚本。
单元约束脚本
脚本可以对相应地址发出的单元数据进行约束,其定义格式为:
['has', {
what: 'input'|'output',
asset: 'assetID in base64 or "base" for bytes',
type: 'transfer'|'issue',
own_funds: true,
amount_at_least: 123,
amount_at_most: 123,
amount: 123,
address: 'INPUT OR OUTPUT ADDRESS IN BASE32'
}]
上述脚本要求单元至少有一个输入/输出满足后续定义所有的条件。特别地,可以使用has one
来强制要求有且仅有一个输入/输出满足后续所有条件。
其它类似的约束还有求和约束,要求输入/输出之和满足特定条件,其格式为
['sum', {
filter: {
what: 'input'|'output',
asset: 'asset or base',
type: 'transfer'|'issue',
own_funds: true,
address: 'ADDRESS IN BASE32'
},
at_least: 120,
at_most: 130,
equals: 123
}]
交易合约
单元约束脚本可以用来实现去中心化交易。假设用户USER ADDRESS
希望使用不高于1000bytes
的价格购买1200units
的其它资产。用户可以发送1000bytes
至如下脚本定义的地址上:
["or", [
["address", "USER ADDRESS"],
["and", [
["address", "EXCHANGE ADDRESS"],
["has", {
what: "output",
asset: "ID of alternative asset",
amount_at_least: 1200,
address: "USER ADDRESS"
}]
]]
]]
或逻辑or
的第一个条件表明,在未成交之前,用户可以随时取回他的1000bytes
。或逻辑or
的第二个条件表明,其他用户可以使用EXCHANGE ADDRESS
地址私钥来取走着1000bytes
,只要他同时在同一单元中将1200units
其它资产输出到USER ADDRESS
。通过这种方式,用户之间可以实现不同资产之间的交易。
借贷合约
单元约束脚本还可以用来实现抵押借贷。假设借款人抵押某种资产借贷10000bytes
,那么借款人和借贷人可以共同签名一笔交易,其中借贷人将bytes
发送给借款人,同时借款人将抵押资产转入以下脚本定义的地址上:
["or", [
["and", [
["address", "LENDER ADDRESS"],
["in data feed", [["TIMESTAMPER ADDRESS"], "datetime", ">", "2017-06-01 00:00:00"]]
]],
["and", [
["address", "BORROWER ADDRESS"],
["has", {
what: "output",
asset: "base",
amount: 10000,
address: "LENDER ADDRESS"
}]
]],
["and", [
["address", "LENDER ADDRESS"],
["address", "BORROWER ADDRESS"]
]]
]]
上述脚本包括了三层含义:
- 当时间超过
2017-06-01 00:00:00
时,借贷人可以取走抵押资产; - 当借款人归还
10000bytes
至借贷人地址LENDER ADDRESS
时,借款人可以取回抵押资产; - 借贷人和借款人可以协商解除合约。
脚本模板
通过预先设定的脚本模板可以很方便地定义脚本,只需要对模板中的参数进行修改即可
["definition template", [
"hash of unit where the template was defined",
{param1: "value1", param2: "value2"}
]]
脚本模板需要在单元中发送app=’definition_template’
的消息,并且需要单元到达稳定状态后,脚本模板才可以使用。消息内容与普通的地址定义脚本相同,参数使用@param1
及@param2
表示。
版权所有。发布者:guantau,转载请注明出处:https://bbfans.org/2018/05/21/byteball-address-script-contract/
评论列表(1条)