Waves акылдуу эсептеринин тиркемелери: аукциондордон бонустук программаларга чейин

Waves акылдуу эсептеринин тиркемелери: аукциондордон бонустук программаларга чейин

Блокчейн көбүнчө криптовалюталар менен байланышкан, бирок DLT технологиясын колдонуу чөйрөлөрү алда канча кеңири. Блокчейнди колдонуунун эң келечектүү багыттарынын бири - бул автоматтык түрдө аткарылуучу жана ага кирген тараптардын ортосунда ишенимди талап кылбаган акылдуу келишим.

RIDE – акылдуу келишимдер үчүн тил

Waves акылдуу келишимдер үчүн атайын тилди иштеп чыккан - RIDE. Анын толук документтери жайгашкан бул жерде. Мынакей - бул тема боюнча макала Хабр боюнча.

RIDE келишими предикат болуп саналат жана натыйжа катары "чын" же "жалган" кайтарат. Демек, транзакция же блокчейнге жазылган же четке кагылган. Акылдуу келишим көрсөтүлгөн шарттардын аткарылышына толук кепилдик берет. RIDEде келишимден транзакцияларды түзүү учурда мүмкүн эмес.

Бүгүнкү күндө Waves акылдуу келишимдердин эки түрү бар: акылдуу эсептер жана акылдуу активдер. Акылдуу эсеп - бул кадимки колдонуучу каттоо эсеби, бирок ал үчүн бардык транзакцияларды көзөмөлдөгөн скрипт орнотулган. Акылдуу эсеп жазуусу мындай көрүнүшү мүмкүн, мисалы:

match tx {
  case t: TransferTransaction | MassTransferTransaction => false
  case _ => true
}

tx - бул трансфер транзакциясы болбосо, үлгү дал келүү механизмин колдонууга уруксат берген иштетилип жаткан транзакция. RIDE үлгүсүнүн дал келүүсү транзакциянын түрүн текшерүү үчүн колдонулат. Бардык учурдагы эсептерди акылдуу эсеп скриптинде иштетүүгө болот транзакциянын түрлөрү.

Скрипт ошондой эле өзгөрмөлөрдү жарыялай алат, шарттарды толук текшерүү үчүн "if-then-else" конструкцияларын жана башка ыкмаларды колдоно алат. Келишимдердин толуктугу жана татаалдыгы (баасы) болушун камсыз кылуу үчүн, келишимди аткаруу башталганга чейин алдын ала айтуу оңой, RIDE циклдерди же секирүү билдирүүлөрүн камтыбайт.

Waves эсептеринин башка өзгөчөлүктөрүнө "мамлекеттин" болушу кирет, башкача айтканда, эсептин абалы. Маалымат транзакцияларын (DataTransaction) колдонуу менен эсептин абалына чексиз сандагы түгөйлөрдү (ачкыч, маани) жаза аласыз. Бул маалымат андан кийин REST API аркылуу да, түз акылдуу келишимде да иштетилиши мүмкүн.

Ар бир транзакция бир катар далилдерди камтышы мүмкүн, ага катышуучунун колу, талап кылынган транзакциянын идентификатору ж.б. киргизилиши мүмкүн.

аркылуу RIDE менен иштөө ЖЕРДЕ келишимдин түзүлгөн көрүнүшүн көрүүгө (эгерде ал түзүлсө), жаңы эсептерди түзүүгө жана ага скрипттерди коюуга, ошондой эле буйрук сабы аркылуу транзакцияларды жөнөтүүгө мүмкүндүк берет.

Толук цикл үчүн, анын ичинде эсеп түзүү, ага акылдуу келишимди орнотуу жана транзакцияларды жөнөтүү, сиз REST API менен иштешүү үчүн китепкананы да колдоно аласыз (мисалы, C#, C, Java, JavaScript, Python, Rust, Elixir) . IDE менен иштөө үчүн ЖАҢЫ баскычты чыкылдатыңыз.

Акылдуу контракттарды колдонуу мүмкүнчүлүктөрү кеңири: транзакцияларга тыюу салуудан белгилүү даректерге («кара тизме») чейин татаал dApps.

Эми бизнесте акылдуу келишимдерди колдонуунун конкреттүү мисалдарын карап көрөлү: аукциондорду өткөрүүдө, камсыздандырууда жана лоялдуулук программаларын түзүүдө.

Аукциондор

Аукционду ийгиликтүү өткөрүүнүн шарттарынын бири ачык-айкындуулук болуп саналат: катышуучулар тендердик табыштамаларды манипуляциялоо мүмкүн эмес экенине ишениши керек. Буга блокчейндин аркасында жетишүүгө болот, мында бардык коюмдар жана алар жасалган убакыт жөнүндө өзгөрүлгүс маалыматтар бардык катышуучуларга жеткиликтүү болот.

Waves блокчейнинде тендердик билдирмелер аукциондук эсептин абалында DataTransaction аркылуу катталышы мүмкүн.

Сиз ошондой эле блоктук номерлерди колдонуу менен аукциондун башталышы жана аяктоо убактысын орното аласыз: Waves блокчейниндеги блокторду түзүү жыштыгы болжол менен 60 секунд.

1. Англисче өсүүчү баа аукциону

Англис аукционунун катышуучулары бири-бири менен атаандашып, сунуштарды жайгаштырышат. Ар бир жаңы коюм мурункусунан ашуусу керек. Аукцион акыркы сунуштан ашкан катышуучулар калбай калганда аяктайт. Мында эң жогорку бааны сунуштаган катышуучу көрсөтүлгөн сумманы бериши керек.

Ошондой эле аукциондук вариант бар, анда сатуучу лот үчүн минималдуу бааны белгилейт жана акыркы баа андан жогору болушу керек. Болбосо, лот сатылбай калат.

Бул мисалда биз аукцион үчүн атайын түзүлгөн эсеп менен иштеп жатабыз. Аукциондун узактыгы 3000 блок, ал эми лоттун баштапкы баасы 0,001 ТОЛКУНУ түзөт. Катышуучу ачкыч "баасы" жана алардын тендердик баасы менен DataTransaction жөнөтүү аркылуу тендерди жайгаштыра алат.

Жаңы сунуштун баасы бул ачкычтын учурдагы баасынан жогору болушу керек жана катышуучунун эсебинде жок дегенде [new_bid + комиссия] энбелгилери болушу керек. Катышуучунун дареги DataTransactionдагы "жөнөтүүчү" талаасында жазылышы керек жана учурдагы сунуш блогунун бийиктиги аукциондун мөөнөтүндө болушу керек.

Эгерде аукциондун аягында катышуучу эң жогорку бааны койгон болсо, анда ал көрсөтүлгөн баа жана валюта жуптары боюнча тиешелүү лот боюнча төлөө үчүн 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. Голландиялык аукцион баанын төмөндөшү

Голландиялык аукциондо көп нерсе алгач сатып алуучу төлөөгө даяр болгон баадан жогору баада сунушталат. Катышуучулардын бири лотту учурдагы баада сатып алууга макул болмоюнча баа этап-этабы менен төмөндөйт.

Бул мисалда биз мурункудай эле константаларды, ошондой эле дельта азайганда баа кадамын колдонобуз. Каттоо эсебинин сценарийи катышуучу чындап эле биринчи болуп коюмду койгонбу же жокпу текшерет. Болбосо, DataTransaction блокчейн тарабынан кабыл алынбайт.

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 аркылуу (ачкыч, маани)* = (“жеңүүчү”, дарек), (“баа”, баа) менен сунуш киргизет. Мындай DataTransaction эгерде бул катышуучунун колтамгасы бар TransferTransaction болсо жана анын сунушу мурункулардын баарынан жогору болсо гана бекитилет. Аукцион 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 гана аткарылат.

Кош чыгымга жол бербөө үчүн колдонуучудан полис ээсинин эсебине алдын ала (ачкыч, маани) = (purchaseTransactionId, sellOrderId) менен DataTransaction жөнөтүүнү суранышыңыз керек жана буга чейин колдонулган ачкыч менен DataTransaction жөнөтүүгө тыюу салышыңыз керек.

Ошондуктан, колдонуучунун далилдери камсыздандыруу энбелгисин сатып алуу транзакциясынын идентификаторун камтышы керек. Валюта жубу сатып алуу транзакциясындагыдай болушу керек. Наркы, ошондой эле камсыздандыруу баасын алып салуу менен сатып алуу учурунда белгиленгенге барабар болушу керек.

Кийинчерээк камсыздандыруу эсеби колдонуучудан камсыздандыруу белгилерин ал сатып алган баадан төмөн эмес баада сатып алат деп түшүнүлөт: камсыздандыруу эсеби 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
}

Кэшбэк жана лоялдуулук программалары

Cashback - бул лоялдуулук программасынын бир түрү, анда сатып алуучу өнүмгө же кызматка жумшалган сумманын бир бөлүгүн кайтарып алат.

Бул ишти акылдуу эсепти колдонуу менен ишке ашырууда, биз камсыздандыруу ишинде болгондой, далилдерди текшеришибиз керек. Кош чыгымды болтурбоо үчүн, колдонуучу кэшбэк алуудан мурун (ачкыч, маани) = (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
}

Кийинки макалада биз опциондор, фьючерстер жана векселдер сыяктуу финансылык инструменттерде акылдуу эсептерди колдонууну карайбыз.

Source: www.habr.com

Комментарий кошуу