Blockchain pogosto povezujemo le s kriptovalutami, vendar so področja uporabe tehnologije DLT veliko širša. Eno najbolj obetavnih področij za uporabo blockchaina je pametna pogodba, ki se izvaja samodejno in ne zahteva zaupanja med strankami, ki so jo sklenile.
RIDE – jezik za pametne pogodbe
Waves je razvil poseben jezik za pametne pogodbe – RIDE. Celotna dokumentacija se nahaja
Pogodba RIDE je predikat in kot izhod vrne »true« ali »false«. V skladu s tem se transakcija zabeleži v verigi blokov ali zavrne. Pametna pogodba v celoti zagotavlja izpolnjevanje navedenih pogojev. Generiranje transakcij iz pogodbe v RIDE trenutno ni mogoče.
Danes obstajata dve vrsti pametnih pogodb Waves: pametni računi in pametna sredstva. Pametni račun je običajni uporabniški račun, vendar je zanj nastavljena skripta, ki nadzoruje vse transakcije. Skript pametnega računa je lahko na primer videti takole:
match tx {
case t: TransferTransaction | MassTransferTransaction => false
case _ => true
}
tx je transakcija v obdelavi, ki jo dovolimo z uporabo mehanizma za ujemanje vzorcev le, če ne gre za transakcijo prenosa. Ujemanje vzorcev v RIDE se uporablja za preverjanje vrste transakcije. Vse obstoječe račune je mogoče obdelati v skriptu pametnega računa
Skript lahko tudi deklarira spremenljivke, uporablja konstrukcije »if-then-else« in druge metode za popolno preverjanje pogojev. Da bi zagotovili, da imajo pogodbe dokazljivo popolnost in zapletenost (strošek), ki jo je enostavno predvideti, preden se začne izvajanje pogodbe, RIDE ne vsebuje zank ali stavkov o skokih.
Druge značilnosti računov Waves vključujejo prisotnost »stanja«, to je stanje računa. V stanje računa lahko s pomočjo podatkovnih transakcij (DataTransaction) zapišete neskončno število parov (ključ, vrednost). Te informacije je nato mogoče obdelati prek API-ja REST in neposredno v pametni pogodbi.
Vsaka transakcija lahko vsebuje niz dokazil, v katere se lahko vnese podpis udeleženca, ID zahtevane transakcije ipd.
Delo z RIDE prek
Za celoten cikel, vključno z ustvarjanjem računa, namestitvijo pametne pogodbe nanj in pošiljanjem transakcij, lahko uporabite tudi knjižnico za interakcijo z REST API (na primer C#, C, Java, JavaScript, Python, Rust, Elixir) . Če želite začeti delati z IDE, preprosto kliknite gumb NOVO.
Možnosti uporabe pametnih pogodb so široke: od prepovedi transakcij na določene naslove (»črna lista«) do kompleksnih dApps.
Zdaj pa poglejmo konkretne primere uporabe pametnih pogodb v poslu: pri izvajanju dražb, zavarovanju in ustvarjanju programov zvestobe.
Dražbe
Eden od pogojev za uspešno dražbo je transparentnost: udeleženci morajo biti prepričani, da je nemogoče manipulirati s ponudbami. To je mogoče doseči zahvaljujoč blockchainu, kjer bodo vsem udeležencem na voljo nespremenljivi podatki o vseh stavah in času, ko so bile sklenjene.
V verigi blokov Waves je mogoče ponudbe zabeležiti v stanju dražbenega računa prek DataTransaction.
Začetni in končni čas dražbe lahko nastavite tudi s številkami blokov: pogostost generiranja blokov v verigi blokov Waves je približno enaka 60 sekund.
1. Angleška dražba z naraščajočo ceno
Udeleženci angleške dražbe med seboj tekmujejo. Vsaka nova stava mora preseči prejšnjo. Dražba se zaključi, ko ni več dražiteljev, ki bi presegli zadnjo ponudbo. V tem primeru mora najvišji ponudnik zagotoviti navedeni znesek.
Obstaja tudi možnost dražbe, pri kateri prodajalec postavi minimalno ceno lota, končna cena pa jo mora preseči. V nasprotnem primeru parcela ostane neprodana.
V tem primeru delamo z računom, ustvarjenim posebej za dražbo. Trajanje dražbe je 3000 blokov, izklicna cena lota pa je 0,001 WAVES. Udeleženec lahko odda ponudbo tako, da pošlje DataTransaction s ključem "price" in vrednostjo svoje ponudbe.
Cena nove ponudbe mora biti višja od trenutne cene za ta ključ, udeleženec pa mora imeti na svojem računu vsaj žetone [nova_ponudba + provizija]. Naslov dražitelja mora biti zapisan v polju "pošiljatelj" v DataTransaction, trenutna višina bloka ponudb pa mora biti znotraj obdobja dražbe.
Če je udeleženec na koncu dražbe določil najvišjo ceno, lahko pošlje ExchangeTransaction za plačilo ustreznega sklopa po določeni ceni in valutnem paru.
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. Nizozemska dražba padajočih cen
Na nizozemski dražbi je lot na začetku ponujen po ceni, ki je višja od tiste, ki jo je kupec pripravljen plačati. Cena se postopoma znižuje, dokler se eden od udeležencev ne strinja z nakupom parcele po trenutni ceni.
V tem primeru uporabljamo iste konstante kot v prejšnjem, pa tudi cenovni korak, ko se delta zmanjša. Skript računa preveri, ali je udeleženec res prvi sklenil stavo. V nasprotnem primeru veriga blokov ne sprejme 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. Dražba "vse plačano"
»All-pay« je dražba, pri kateri vsi udeleženci plačajo ponudbo, ne glede na to, kdo zmaga. Vsak novi udeleženec plača ponudbo, žreb pa zmaga udeleženec, ki ponudi najvišjo vrednost.
V našem primeru vsak udeleženec dražbe odda ponudbo prek DataTransaction z (ključ, vrednost)* = (»zmagovalec«, naslov),(»cena«, cena). Taka DataTransaction je odobrena le, če ima ta udeleženec že TransferTransaction s svojim podpisom in je njegova ponudba višja od vseh prejšnjih. Dražba se nadaljuje, dokler ni dosežena 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)
}
Zavarovanje / množično financiranje
Poglejmo si situacijo, ko morate zavarovati sredstva uporabnikov pred finančnimi izgubami. Na primer, uporabnik želi jamstvo, da bo v primeru amortizacije žetona lahko dobil nazaj celoten znesek, plačan za te žetone, in je pripravljen plačati razumen znesek zavarovanja.
Za izvedbo tega je treba izdati "zavarovalne žetone". Nato se na račun zavarovanca namesti skript, ki omogoča izvedbo le tistih menjalnih transakcij, ki izpolnjujejo določene pogoje.
Da preprečite dvojno porabo, morate od uporabnika vnaprej zahtevati pošiljanje DataTransaction na račun zavarovanca s (ključ, vrednost) = (purchaseTransactionId, sellOrderId) in prepovedati pošiljanje DataTransaction z že uporabljenim ključem.
Zato morajo uporabnikova dokazila vsebovati ID transakcije nakupa zavarovalnega žetona. Valutni par mora biti enak kot pri nakupni transakciji. Tudi stroški morajo biti enaki tistim, določenim ob nakupu, zmanjšani za ceno zavarovanja.
Razume se, da nato zavarovalni račun od uporabnika kupi zavarovalne žetone po ceni, ki ni nižja od tiste, po kateri jih je kupil: zavarovalni račun ustvari ExchangeTransaction, uporabnik podpiše naročilo (če je transakcija pravilno zaključena), zavarovalni račun podpiše drugo naročilo in celotno transakcijo ter jo pošlje v blockchain .
Če do nakupa ne pride, lahko uporabnik ustvari ExchangeTransaction v skladu s pravili, opisanimi v skriptu, in pošlje transakcijo v verigo blokov. Tako lahko uporabnik vrne denar, porabljen za nakup zavarovanih žetonov.
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)
}
Zavarovalni žeton lahko postane pametno sredstvo, na primer, da se prepove njegov prenos tretjim osebam.
Ta shema se lahko izvaja tudi za žetone množičnega financiranja, ki se vrnejo lastnikom, če zahtevani znesek ni bil zbran.
Transakcijski davki
Pametne pogodbe so uporabne tudi v primerih, ko je treba pobrati davek na vsako transakcijo z več vrstami sredstev. To je mogoče storiti z novim sredstvom z nameščenim
1. Izdajamo FeeCoin, ki bo poslan uporabnikom po fiksni ceni: 0,01 WAVES = 0,001 FeeCoin.
2. Nastavite sponzorstvo za FeeCoin in menjalni tečaj: 0,001 WAVES = 0,001 FeeCoin.
3. Nastavite naslednji skript za pametno sredstvo:
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
}
Zdaj vsakič, ko nekdo prenese N pametnih sredstev, vam bo dal FeeCoin v višini N/taxDivisor (ki ga je mogoče kupiti pri vas pri 10 *N/taxDivisor WAVES), vi pa boste rudarju dali N/taxDivisor WAVES. Posledično bo vaš dobiček (davek) 9*N / taxDivisor WAVES.
Obdavčitev lahko izvedete tudi z uporabo skripte pametnega sredstva in 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
}
Vračilo denarja in programi zvestobe
Cashback je vrsta programa zvestobe, v katerem kupec dobi nazaj del zneska, porabljenega za izdelek ali storitev.
Pri izvedbi tega primera s pametnim računom moramo dokazila preveriti na enak način, kot smo to storili pri zavarovalnem primeru. Da prepreči dvojno porabo, mora uporabnik poslati DataTransaction s (ključ, vrednost) = (purchaseTransactionId, cashbackTransactionId), preden prejme vračilo denarja.
Nastaviti moramo tudi prepoved obstoječih ključev, ki uporabljajo DataTransaction. cashbackDivisor - enota, deljena z deležem cashbacka. Tisti. če je delež denarnega vračila 0.1, potem je 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)
}
Atomska zamenjava
Atomska zamenjava omogoča uporabnikom izmenjavo sredstev brez pomoči menjave. Pri atomski zamenjavi morata oba udeleženca v transakciji to potrditi v določenem časovnem obdobju.
Če vsaj eden od udeležencev ne poda pravilne potrditve transakcije v času, določenem za transakcijo, se transakcija razveljavi in menjava ne pride.
V našem primeru bomo uporabili naslednji skript pametnega računa:
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
}
V naslednjem članku si bomo ogledali uporabo pametnih računov v finančnih instrumentih, kot so opcije, terminske pogodbe in menice.
Vir: www.habr.com