区块链通常只与加密货币联系在一起,但 DLT 技术的应用领域要广泛得多。 区块链最有前途的应用领域之一是自动执行的智能合约,并且不需要签订合约的各方之间的信任。
RIDE——智能合约语言
Waves 为智能合约开发了一种特殊的语言——RIDE。 其完整文档位于
RIDE 合约是一个谓词,返回“true”或“false”作为输出。 因此,交易要么被记录在区块链中,要么被拒绝。 智能合约充分保证指定条件的满足。 目前无法从 RIDE 中的合约生成交易。
目前,Waves 智能合约有两种类型:智能账户和智能资产。 智能账户是一个普通用户账户,但为其设置了一个脚本来控制所有交易。 智能帐户脚本可能如下所示,例如:
match tx {
case t: TransferTransaction | MassTransferTransaction => false
case _ => true
}
tx 是一个正在处理的交易,只有当它不是转账交易时,我们才允许使用模式匹配机制。 RIDE 中的模式匹配用于检查事务类型。 所有现有帐户都可以在智能帐户脚本中处理
该脚本还可以声明变量,使用“if-then-else”结构和其他方法来全面检查条件。 为了确保合约具有可证明的完整性和复杂性(成本),并且在合约执行开始之前易于预测,RIDE 不包含循环或跳转语句。
Waves 帐户的其他功能包括“状态”的存在,即帐户的状态。 您可以使用数据事务(DataTransaction)将无限数量的对(键,值)写入帐户状态。 然后可以通过 REST API 和直接在智能合约中处理此信息。
每笔交易都可以包含一系列证明,可以在其中输入参与者的签名、所需交易的 ID 等。
通过 RIDE 工作
对于完整的周期,包括创建帐户、在其上安装智能合约和发送交易,您还可以使用与 REST API 交互的库(例如,C#、C、Java、JavaScript、Python、Rust、Elixir) 。 要开始使用 IDE,只需单击“新建”按钮。
使用智能合约的可能性很广泛:从禁止某些地址(“黑名单”)的交易到复杂的 dApp。
现在让我们看一下在商业中使用智能合约的具体示例:进行拍卖、保险和创建忠诚度计划时。
拍卖会
成功拍卖的条件之一是透明度:参与者必须确信不可能操纵出价。 这要归功于区块链,所有参与者都可以获得有关所有投注及其时间的不可变数据。
在Waves区块链上,出价可以通过DataTransaction记录在拍卖账户状态中。
您还可以使用区块编号设置拍卖的开始和结束时间:Waves 区块链中区块生成的频率大约等于 60 秒。
1. 英式升价拍卖
英式拍卖的参与者相互竞争出价。 每个新的赌注必须超过之前的赌注。 当没有更多的投标人超过最后的出价时,拍卖结束。 在这种情况下,最高出价者必须提供规定的金额。
还有一种拍卖选项,卖方为拍品设定最低价格,最终价格必须超过该价格。 否则,该拍品仍无法售出。
在此示例中,我们正在使用专门为拍卖创建的帐户。 拍卖时长为3000个区块,起始价格为0,001 WAVES。 参与者可以通过发送包含关键“价格”和出价价值的数据交易来出价。
新出价的价格必须高于该密钥的当前价格,并且参与者的账户中必须至少有 [new_bid + Commission] 代币。 投标人的地址必须记录在DataTransaction中的“sender”字段中,并且当前投标块高度必须在拍卖期间内。
如果在拍卖结束时,参与者设定了最高价格,他可以发送一个ExchangeTransaction,以指定的价格和货币对支付相应的手数。
let startHeight = 384120
let finishHeight = startHeight + 3000
let startPrice = 100000
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
case d : DataTransaction =>
#проверяем, задана ли в стейте цена
let currentPrice = if isDefined(getInteger(this, "price"))
#извлекаем цену из стейта
then extract(getInteger(this, "price"))
else startPrice
#извлекаем цену из транзакции
let newPrice = extract(getInteger(d.data, "price"))
let priceIsBigger = newPrice > currentPrice
let fee = 700000
let hasMoney = wavesBalance(tx.sender) + fee >= newPrice
#убеждаемся, что в текущей транзакции два поля и что отправитель совпадает с указанным в транзакции
let correctFields = size(d.data) == 2 &&
d.sender == addressFromString(extract(getString(d.data,"sender")))
startHeight <= height && height <= finishHeight && priceIsBigger && hasMoney && correctFields
case e : ExchangeTransaction =>
let senderIsWinner = e.sender == addressFromString(extract(getString(this, "sender"))) #убеждаемся, что лот обменивает тот, кто его выиграл
let correctAssetPair = e.sellOrder.assetPair.amountAsset == token && ! isDefined(e.sellOrder.assetPair.priceAsset)
let correctAmount = e.amount == 1
let correctPrice = e.price == extract(getInteger(this, "price"))
height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice
case _ => false
}
2. 荷兰式拍卖价格递减
在荷兰式拍卖中,拍卖品最初的报价高于买家愿意支付的价格。 价格逐步下降,直到其中一位参与者同意以当前价格购买该批次为止。
在此示例中,我们使用与前一个示例相同的常量,以及 Delta 减小时的价格步长。 帐户脚本检查参与者是否确实是第一个下注的人。 否则,数据交易不会被区块链接受。
let startHeight = 384120
let finishHeight = startHeight + 3000
let startPrice = 100000000
let delta = 100
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
case d : DataTransaction =>
let currentPrice = startPrice - delta * (height - startHeight)
#извлекаем из поступившей дата-транзакции поле "price"
let newPrice = extract(getInteger(d.data, "price"))
#убеждаемся, что в стейте текущего аккаунта не содержится поля "sender"
let noBetsBefore = !isDefined(getInteger(this, "sender"))
let fee = 700000
let hasMoney = wavesBalance(tx.sender) + fee >= newPrice
#убеждаемся, что в текущей транзакции только два поля
let correctFields = size(d.data) == 2 && newPrice == currentPrice && d.sender == addressFromString(extract(getString(d.data, "sender")))
startHeight <= height && height <= finishHeight && noBetsBefore && hasMoney && correctFields
case e : ExchangeTransaction =>
#убеждаемся, что отправитель текущей транзакции указан в стейте аккаунта по ключу sender
let senderIsWinner = e.sender == addressFromString(extract(getString(this, "sender")))
#убеждаемся, что аmount ассета указан корректно, и что прайс-ассет - waves
let correctAssetPair = e.sellOrder.assetPair.amountAsset == token && ! isDefined(e.sellOrder.assetPair.priceAsset)
let correctAmount = e.amount == 1
let correctPrice = e.price == extract(getInteger(this, "price"))
height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice
case _ => false
}
3. 拍卖“全款”
“全付”是指所有参与者都支付出价的拍卖,无论谁赢得了拍品。 每个新参与者支付出价,出价最高的参与者获胜。
在我们的示例中,每个拍卖参与者通过 DataTransaction 使用 (key, value)* = (“获胜者”, 地址),(“价格”, 价格) 出价。 仅当该参与者已经拥有带有其签名的 TransferTransaction 并且其出价高于所有先前的出价时,此类 DataTransaction 才会被批准。 拍卖一直持续到达到 endHeight。
let startHeight = 1000
let endHeight = 2000
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
case d: DataTransaction =>
#извлекаем из поступившей дата-транзакции поле "price"
let newPrice = extract(getInteger(d.data, "price"))
#извлекаем из пруфов транзакции публичный ключ аккаунта
let pk = d.proofs[1]
let address = addressFromPublicKey(pk)
#извлекаем транзакцию доказательство из пруфов поступившей дата транзакции
let proofTx = extract(transactionById(d.proofs[2]))
height > startHeight && height < endHeight
&& size(d.data) == 2
#убеждаемся, что адрес победителя, извлеченный из текущей транзакции, совпадает с адресом, извлеченным из пруфов
&& extract(getString(d.data, "winner")) == toBase58String(address.bytes)
&& newPrice > extract(getInteger(this, "price"))
#проверяем, что транзакция подписана
&& sigVerify(d.bodyBytes, d.proofs[0], d.proofs[1])
#проверяем корректность транзакции, указанной в пруфах
&& match proofTx {
case tr : TransferTransaction =>
tr.sender == address &&
tr.amount == newPrice
case _ => false
}
case t: TransferTransaction =>
sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
|| (
height > endHeight
&& extract(getString(this, "winner")) == toBase58String((addressFromRecipient(t.recipient)).bytes)
&& t.assetId == token
&& t.amount == 1
)
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
保险/众筹
让我们考虑一种情况,您需要确保用户的资产免受财务损失。 例如,用户希望得到保证,如果代币贬值,他将能够拿回为这些代币支付的全额金额,并愿意支付合理金额的保险。
为了实现这一点,需要发行“保险代币”。 然后,在保单持有人的帐户上安装一个脚本,仅允许执行满足特定条件的那些 ExchangeTransactions。
为了防止双重支出,您需要请求用户提前向投保人账户发送DataTransaction(key,value)=(purchaseTransactionId,sellOrderId),并禁止发送已使用key的DataTransaction。
因此,用户的证明中必须包含购买保险代币的交易ID。 货币对必须与购买交易中的货币对相同。 费用还必须等于购买时确定的费用减去保险价格。
据了解,随后保险账户以不低于用户购买时的价格向用户购买保险代币:保险账户创建一个 ExchangeTransaction,用户签署订单(如果交易正确完成),保险账户签署第二个订单和整个交易并将其发送到区块链。
如果没有发生购买,用户可以根据脚本中描述的规则创建一个ExchangeTransaction,并将交易发送到区块链。 这样用户就可以退还购买保险代币所花的钱。
let insuranceToken = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let freezePeriod = 150000
let insurancePrice = 10000
match tx {
#убеждаемся, что, если поступила дата-транзакция, то у нее ровно одно поле и в стейте еще нет такого ключа
case d : DataTransaction => size(d.data) == 1 && !isDefined(getBinary(this, d.data[0].key))
case e : ExchangeTransaction =>
#если у транзакции нет седьмого пруфа, проверяем корректность подписи
if !isDefined(e.proofs[7]) then
sigVerify(e.bodyBytes, e.proofs[0], e.senderPublicKey)
else
#если у транзакции есть седьмой пруф, извлекаем из него транзакцию и узнаём её высоту
let purchaseTx = transactionById(e.proofs[7])
let purchaseTxHeight = extract(transactionHeightById(e.proofs[7]))
#обрабатываем транзакцию из пруфа
match purchaseTx {
case purchase : ExchangeTransaction =>
let correctSender = purchase.sender == e.sellOrder.sender
let correctAssetPair = e.sellOrder.assetPair.amountAsset == insuranceToken &&
purchase.sellOrder.assetPair.amountAsset == insuranceToken &&
e.sellOrder.assetPair.priceAsset == purchase.sellOrder.assetPair.priceAsset
let correctPrice = e.price == purchase.price - insurancePrice && e.amount == purchase.amount
let correctHeight = height > purchaseTxHeight + freezePeriod
#убеждаемся, что в транзакции-пруфе указан верный ID текущей транзакции
let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == e.sellOrder.id
correctSender && correctAssetPair && correctPrice && correctHeight && correctProof
case _ => false
}
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
例如,保险代币可以成为智能资产,以禁止其转让给第三方。
该方案也可以用于众筹代币,如果未筹集到所需金额,则将众筹代币退还给所有者。
交易税
智能合约也适用于需要对多种资产的每笔交易征税的情况。 这可以通过安装了新资产来完成
1.我们发行FeeCoin,以固定价格发送给用户:0,01 WAVES = 0,001 FeeCoin。
2. 设置赞助FeeCoin和汇率:0,001 WAVES = 0,001 FeeCoin。
3. 为智能资产设置以下脚本:
let feeAssetId = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
let taxDivisor = 10
match tx {
case t: TransferTransaction =>
t.feeAssetId == feeAssetId && t.fee == t.amount / taxDivisor
case e: ExchangeTransaction | MassTransferTransaction => false
case _ => true
}
现在,每当有人转移N个智能资产时,他们都会给你N/taxDivisor数量的FeeCoin(可以以10 *N/taxDivisor WAVES向你购买),你会给矿工N/taxDivisor WAVES。 因此,您的利润(税)将为 9*N /taxDivisor WAVES。
您还可以使用智能资产脚本和 MassTransferTransaction 执行征税:
let taxDivisor = 10
match tx {
case t : MassTransferTransaction =>
let twoTransfers = size(t.transfers) == 2
let issuerIsRecipient = t.transfers[0].recipient == addressFromString("3MgkTXzD72BTfYpd9UW42wdqTVg8HqnXEfc")
let taxesPaid = t.transfers[0].amount >= t.transfers[1].amount / taxDivisor
twoTransfers && issuerIsRecipient && taxesPaid
case _ => false
}
现金返还和忠诚度计划
现金返还是一种忠诚度计划,买家可以返还购买产品或服务的部分金额。
在使用智能账户实施本案例时,我们必须按照保险案例中相同的方式检查证据。 为了防止双重支出,用户在收到现金返还之前必须发送一个带有 (key, value) = (purchaseTransactionId, cashbackTransactionId) 的 DataTransaction。
我们还必须使用 DataTransaction 对现有密钥设置禁令。 cashbackDivisor - 单位除以现金返还份额。 那些。 如果现金返还份额为 0.1,则 cashbackDivisor 1 / 0.1 = 10。
let cashbackToken = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let cashbackDivisor = 10
match tx {
#убеждаемся, что, если поступила дата-транзакция, то у нее ровно одно поле и в стейте еще нет такого ключа
case d : DataTransaction => size(d.data) == 1 && !isDefined(getBinary(this, d.data[0].key))
case e : TransferTransaction =>
#если у транзакции нет седьмого пруфа, проверяем корректность подписи
if !isDefined(e.proofs[7]) then
sigVerify(e.bodyBytes, e.proofs[0], e.senderPublicKey)
else
#если у транзакции есть седьмой пруф, извлекаем из него транзакцию и узнаём её высоту
let purchaseTx = transactionById(e.proofs[7])
let purchaseTxHeight = extract(transactionHeightById(e.proofs[7]))
#обрабатываем транзакцию из пруфа
match purchaseTx {
case purchase : TransferTransaction =>
let correctSender = purchase.sender == e.sender
let correctAsset = e.assetId == cashbackToken
let correctPrice = e.amount == purchase.amount / cashbackDivisor
#убеждаемся, что в транзакции-пруфе указан верный ID текущей транзакции
let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == e.id
correctSender && correctAsset && correctPrice && correctProof
case _ => false
}
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
原子交换
原子交换允许用户在没有交易所帮助的情况下交换资产。 对于原子交换,交易双方都需要在一定时间内进行确认。
如果至少一个参与者没有在指定的交易时间内提供正确的交易确认,则交易将被取消并且交换不会发生。
在我们的示例中,我们将使用以下智能帐户脚本:
let Bob = Address(base58'3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8')
let Alice = Address(base58'3PNX6XwMeEXaaP1rf5MCk8weYeF7z2vJZBg')
let beforeHeight = 100000
let secret = base58'BN6RTYGWcwektQfSFzH8raYo9awaLgQ7pLyWLQY4S4F5'
match tx {
case t: TransferTransaction =>
let txToBob = t.recipient == Bob && sha256(t.proofs[0]) == secret && 20 + beforeHeight >= height
let backToAliceAfterHeight = height >= 21 + beforeHeight && t.recipient == Alice
txToBob || backToAliceAfterHeight
case _ => false
}
在下一篇文章中,我们将探讨智能账户在期权、期货和票据等金融工具中的使用。
来源: habr.com