通過例子和實(shí)踐來學(xué)習(xí)rho語言唧垦。下面的例子和練習(xí)都很值得去運(yùn)行捅儒、閱讀、修改和完善振亮。修改練習(xí)和教程中任何你感到有意思的代碼巧还,這樣能夠獲得最好的學(xué)習(xí)效果。該教程包含了rho語言最常見以及最重要的特性双炕,足以讓開發(fā)者快速入門狞悲。
課程0 -- 開發(fā)環(huán)境
配置你的開發(fā)環(huán)境
為了可以運(yùn)行這個教程里面的rholang代碼,你需要一些開發(fā)環(huán)境妇斤。 這不是一個會讓你感到疲憊的rholang開發(fā)工具或者技術(shù)棧摇锋。 然而它展示了一些基本的開發(fā)環(huán)境給你開始。
網(wǎng)上編譯器
RChain社區(qū)的成員提供了一個基于公共網(wǎng)站的在線rholang編譯器站超。 這個工具非常有前途荸恕,也是一種入門的簡單方式。 但是它還是開發(fā)節(jié)點(diǎn)死相,有時候會不穩(wěn)定融求。
本地節(jié)點(diǎn)
真正正確運(yùn)行rholang代碼的方法是在通過啟動你自己本地機(jī)子的RNode然后使用它的rholang編譯器。 首先你要為你自己的平臺安裝 RNode
對于初學(xué)者算撮,這里有詳細(xì)的一步一步指導(dǎo)你怎么使用AWS 或者Docker啟動你的節(jié)點(diǎn).
一旦你的RNode安裝好了生宛,你可以運(yùn)行基本的獨(dú)立節(jié)點(diǎn)县昂。
$ rnode run -s -n
在單獨(dú)的終端里,你可以在REPL模式下一次執(zhí)行一行rholang陷舅。
$ rnode repl
╦═╗┌─┐┬ ┬┌─┐┬┌┐┌ ╔╗╔┌─┐┌┬┐┌─┐ ╦═╗╔═╗╔═╗╦
╠╦╝│ ├─┤├─┤││││ ║║║│ │ ││├┤ ╠╦╝║╣ ╠═╝║
╩╚═└─┘┴ ┴┴ ┴┴┘└┘ ╝╚╝└─┘─┴┘└─┘ ╩╚═╚═╝╩ ╩═╝
rholang $ Nil
Deployment cost: CostAccount(0,Cost(0))
Storage Contents:
for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil }
rholang $ @"world"!("hello")
Deployment cost: CostAccount(5,Cost(64))
Storage Contents:
@{"world"}!("hello") | for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil }
當(dāng)你運(yùn)行更多行數(shù)的rholang代碼時候倒彰,你可以使用RNode的eval模式來執(zhí)行代碼。
$ rnode eval intersection.rho
Evaluating from intersection.rho
Result for intersection.rho:
Deployment cost: CostAccount(39,Cost(1132))
Storage Contents:
@{Unforgeable(0xb19519ab773d1ec4ce96f1b71b748552e4a084dfc9942371717f5cb87e818879)}!(@{"name"}!(Nil)) | @{Unforgeable(0xb19519ab773d1ec4ce96f1b71b748552e4a084dfc9942371717f5cb87e818879)}!(@{"age"}!(Nil)) | @{"world"}!("hello") | for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( @{{@{"name"}!() | _ /\ @{"age"}!() | _}} <= @{Unforgeable(0xb19519ab773d1ec4ce96f1b71b748552e4a084dfc9942371717f5cb87e818879)} ) { @{Unforgeable(0x00)}!("Both name and age were in the data") } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil }
有一些RNode的輸出會出現(xiàn)在你運(yùn)行代碼的同一個終端莱睁。但是其它一些代碼輸出會直接出現(xiàn)在第一個終端待讳。 所以在你熟悉什么輸出出現(xiàn)在哪里前請確定好檢查兩邊的終端。
Cryptofex IDE
一個叫做cryptofex 的開發(fā)環(huán)境已經(jīng)進(jìn)入了alpha版本仰剿。 Cryptofex可能最后最好的開發(fā)rholang的地方创淡,但是現(xiàn)在還是很早期的軟件。 Cryptofex提供rholang語法高亮特性并且可以在RChain集成節(jié)點(diǎn)上檢測dApps南吮。 IDE同時也提供環(huán)境創(chuàng)建和測試在以太網(wǎng)上琳彩,私人測試網(wǎng)上和單獨(dú)模式的EVM上的智能合約。
課程1 -- 發(fā)送與標(biāo)準(zhǔn)輸出(stdout)
發(fā)送與標(biāo)準(zhǔn)輸出(stdout)
說聲Hello
"Person waiving hello"
編程界有一個存在已久的傳統(tǒng)——輸出"Hello World"應(yīng)該是你學(xué)習(xí)的第一個程序部凑。下面是一個在屏幕上輸出"Hello World"的最簡單例子汁针。
hello.rho
練習(xí)
請讓程序輸出"Rholang rocks!" 而不是 "Hello World"。
練習(xí)
嘗試將"stdout"替換為別的語句砚尽。會得到什么結(jié)果?
嘗試一下這個有趣的通道名稱@"someChannel". 這里可以比較隨意辉词。請讓程序在屏幕上輸出 "Sup World"必孤。
標(biāo)準(zhǔn)輸出(stdout)到底是什么東西
Channels are like mailboxes for sending messages
rho語言的核心是通道(channel,下面都稱為通道)通信. 通道是你可以用來發(fā)送和接收消息的通信線路。你可以使用!字符來在通道中發(fā)送消息瑞躺。
Redo this diagram!
stdout 是一個特殊的通道敷搪,用于將文本發(fā)送至"標(biāo)準(zhǔn)輸出",通常指你的電腦屏幕。正因為它的特殊幢哨,我們不得不將它寫在第一段學(xué)習(xí)的代碼里面赡勘。
使用其他通道
Sent messages wait to be received here in "message purgatory"... JK, it's called the "tuplespace"
實(shí)際上你可以在很多通道中發(fā)送消息,而非只有stdout捞镰。 但其它通道不像 stdout 他們不會在屏幕上顯示闸与。
tupleSpace.rho
那么,在其他通道中的消息將被發(fā)送至哪里岸售?哪里都不會去践樱!這些消息暫時哪兒都不去,這些消息會繼續(xù)待在通道內(nèi)凸丸,等待其他人去取出它們拷邢。我們將在下一課程中學(xué)習(xí)如何獲取這些消息。同時屎慢,消息滯留所在的地方瞭稼,我們稱為 "元組空間"忽洛。
請確保你的信息保留在元組空間里。你應(yīng)該會看到像下面的信息环肘。
Storage Contents:
@{"RandoChannel"}!("This won't be on the screen") | for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil }
同時做兩件事
Rather than following an ordered list, all ingredients are added concurrently. Looks delicions
在rholang中欲虚,我們不會告訴計算機(jī)做完一件事,再到另一件廷臼。相反苍在,我們會告訴它需要做的所有事情,然后"并行地"執(zhí)行它們荠商,或者一次性全部執(zhí)行寂恬。
parallel.rho
| 的發(fā)音是 "parallel", 可簡稱為 "par"。
練習(xí)
向"pizza shop"通道發(fā)送消息"1 large pepperoni please"莱没。
練習(xí)
向"Mom's Phone"通道發(fā)送"Hi Mom"初肉。
練習(xí)
用一個程序在屏幕上輸出兩個消息,"Rick"和 "Morty"饰躲。
小測試
stdout!("Programming!") 將在屏幕上輸出什么牙咏?
Programming!
stdout!
Nothing
@"what"!("Up") 在什么通道上發(fā)送消息?
@"Up"
@"what"
what
rholang會先執(zhí)行哪一條語句嘹裂?
@"stdout"!("Dogs")
|
@"stdout"!("Cats")
輸出 "Dogs"
輸出 "Cats"
都不妄壶。 它們是并行的 PS. 有一個特殊的通道 stderr. 請嘗試一下看看往這個通道發(fā)送消息,會發(fā)生什么寄狼? 有什么區(qū)別?
課程2 -- 接收
消息檢查
// Dear future self, keys in freezer because...
在上一章我們學(xué)習(xí)了如何發(fā)送消息。現(xiàn)在是時候?qū)W習(xí)如何接收消息了泊愧。常規(guī)語法如下:
for(message <- channel){ // Do something here}
順便提一下, // 用于標(biāo)示注釋。 //后面的內(nèi)容程序并不會運(yùn)行。寫好注釋可以有利于其他開發(fā)者(包括你自己)閱讀代碼,并了解代碼的意圖梢莽,其他讀你代碼的開發(fā)者會感激你寫注釋的萧豆。
通信事件
Pizza shop can receive messages on its channel.
下面的代碼使用披薩店的通道發(fā)送了一個消息,披薩店收到了它昏名。pizza店通過將消息打印至標(biāo)準(zhǔn)輸出來表明其已收到涮雷。
pizzaOrder
練習(xí)
將上述消息發(fā)送至一個不同的通道,如@"coffeShop". 消息會被接收端打印出來嗎? 還是東西留在了元組空間里么轻局?
Let's hit up the coffee shop.
練習(xí)
記住洪鸭,在rholang中,任何事情都是并行地而非按順序地執(zhí)行仑扑。如果我們把接收信息的代碼放在前面览爵,那么披薩店的代碼仍可執(zhí)行。嘗試一下吧镇饮。
元組空間污染
如果你遇到了舊數(shù)據(jù)滯留在元組空間并會對后面的代碼執(zhí)行有影響蜓竹,你需要清空你的元組空間。最簡單的方式是刪除你的數(shù)據(jù)目錄.rnode
使用上述方法清空元組空間已經(jīng)過時了储藐。一個更好的方法是防止它一開始被舊數(shù)據(jù)污染俱济。我們可以通過修改最上面的new代碼段來實(shí)現(xiàn)。
舊的方案
new stdout(rho:io:stdout) in { @"world"!("Welcome to RChain") }
嘗試下面新的方案
new world, stdout(rho:io:stdout) in { world!("Welcome to RChain") // No more @ or " " }
我們將在“不可偽造的names”的課程中講解它的原理「撇現(xiàn)在你不需要每次都重置通道蛛碌。
發(fā)送前接收
Rather than the message appearing first, then someone receiving it, Greg is trying to receive first. Hopefully someone will send him a message so he can have a comm event.
當(dāng)發(fā)送和接收同時存在于通道時,這被稱為通信事件辖源,或稱為"comm event"左医。
不像普通郵件那樣必須被發(fā)送,對方才能被接收同木,在rholang中,上述兩個事件可以以任何順序發(fā)生或者同時發(fā)生跛十。這類似于可以先接收消息彤路,再發(fā)送它。每當(dāng)發(fā)送和接收共存時芥映,就會觸發(fā)通信事件洲尊。
合約
The poor chef is too busy making sure he can receive orders to take care of his pizza.
我們的披薩店例子很好地說明了通信事件,但期望每次有新的訂單時奈偏,披薩店都能自動發(fā)出一個新的接收來處理它們坞嘀,這并不現(xiàn)實(shí)。
幸運(yùn)地是惊来,我們可以只部署一次代碼丽涩,然后每次接收到它的消息時都執(zhí)行一次。這類代碼稱為“智能合約”。讓我們看一個比披薩店更高級但相似的例子--咖啡店矢渊。
coffeeShop.rho
練習(xí)
在咖啡店點(diǎn)第二杯飲料
練習(xí)
更改上面例子的確認(rèn)消息
一般來說继准,下列哪一個會第一個發(fā)生?
發(fā)送,因為它與普通郵件的工作原理一樣矮男。 接收移必,因為以該方式運(yùn)行的代碼更快。 發(fā)送或接收都可以最先發(fā)生毡鉴,或者同時崔泵。 接收,因為rohlang是并行的猪瞬。 都不憎瘸。直接觸發(fā)通信事件(comm event)。
練習(xí)
通道被命名為 @"coffeeShop"撑螺。將它更名為你所選擇的特定咖啡店的名稱含思。然后使用我們最近學(xué)到的new來修改代碼
Persistent For
實(shí)際上,在rholang中有兩種不同的語法來表示持續(xù)從通道取出信息甘晤。我們剛剛學(xué)習(xí)contract語法含潘。下面的用for語法的代碼是等價的。
contract @"coffeeShop"(order) = { for(order <= @"coffeeShop") { 注意线婚,上述代碼與正常的 for 不同遏弱,因為它使用了雙劃線 <= 而不是單劃線 <-. for和contract是有不同的地方的恩沛,我們會在討論區(qū)塊鏈的時候討論到他們的區(qū)別±酵裕現(xiàn)在你可以將它們當(dāng)做同一功能。
練習(xí)
用持久的for語法而不是"contract"語法來寫一個想咖啡店這樣的披薩店合約凸椿。嘗試自己從頭寫一次整個代碼游沿,這樣會讓你更容易記清語法饰抒。
下面哪一項是與其他兩項不同的?
for (a <- b){}
contract b(a) = {}
for (a <= b){}
哪一個發(fā)送語句會與for (message <- @"grandmasSnapChat"){Nil}對應(yīng)產(chǎn)生一個通信事件 ?
grandmasSnapChat!("Hi Grandma")
@"grandmasSnapChat"!("Glad you're snapping Grandma")
for("Here's a snap for you g'ma" <- @"grandmasSnapChat")
課程3 -- 傳音筒诀黍、"name"和“process”
消息傳遞
The game of telephone is perfect to simulate message forwarding in rholang.
在前面的章節(jié)袋坑,我們學(xué)習(xí)了如何向祖母或披薩店發(fā)送消息。但是至今所有的接收方都通過將消息打印至標(biāo)準(zhǔn)輸出眯勾,來告知已經(jīng)接收到了枣宫。
現(xiàn)在讓我們做一些更有意思的事情--類似孩子們的傳話游戲那樣傳遞消息。
telephone3.rho
你可以通過運(yùn)行上面的代碼來做實(shí)驗吃环。你可以修改你覺得合適的地方多運(yùn)行幾次也颤。
練習(xí)
傳話游戲很有趣,但有更多玩家參與會更好郁轻。請?zhí)砑拥谌幻鹘藽harlie的玩家翅娶。bob接收消息后將發(fā)送消息給Charlie,而不是簡單打印至stdout。然后Charlie將它打印至屏幕上故觅。多多益善!
The message never seems to get there correctly. I blame Bob.
練習(xí)
如果你曾經(jīng)玩過電話游戲厂庇,你應(yīng)該知道,消息極少能被正確地傳遞输吏。Bob現(xiàn)在決定通過發(fā)送一條錯誤的消息权旷。改寫程序,使得Bob無論收到什么贯溅,都能傳遞不同的消息拄氯。
*這到底是啥?
Opposites attract
你注意到 @"Bob"!(message)中的? 在rholang中有兩種類型它浅, "names" 和 "processes"译柏。同樣也有可以在兩者之間互相轉(zhuǎn)化的方法。
"processes"可以是rholang中任何一個代碼片段姐霍,例如我們的傳話筒游戲鄙麦,或者是披薩店訂單程序∧髡郏“process”可以是上百行的大程序胯府,也可以只有幾行。它們甚至可以是用于表示值的代碼恨胚。下面是一些“process”的例子骂因。
stdout!("Sup Rholang?") 一個常見的發(fā)送操作。
Nil 最小的“process”赃泡。如字面意思寒波,它不做任何事。
for(msg <- @"phone"){Nil} 一個常見的接收操作升熊,在消息到達(dá)時它不會做任何事俄烁。
"Hello World" 另一個不做任何事請的小“process”。被稱為"基礎(chǔ)術(shù)語"级野。 "names"可以被用于賦名通道以發(fā)送消息页屠。在大多數(shù)編程語言中,"name"是完全獨(dú)立的一樣?xùn)|西勺阐,它們本身就存在。但是在rholang中矛双,"name"來自"引用process"渊抽,即將@標(biāo)簽放在“process”之前,即可得到一個"name"议忽。下面是"name"的一些例子懒闷。
@"Hello World" 通過引用基礎(chǔ)術(shù)語"Hello World"來創(chuàng)建。
@Nil 最小的“name”。通過引用最小的“process”來創(chuàng)建愤估。
@(@"Alice"!("I like rholang, pass it on."))
通過引用來自傳話筒游戲的"process"來創(chuàng)建帮辟。
關(guān)于*的一切
What kind of name is that!? Did your parents just name you after some computer code?
通過用@符號來標(biāo)記“process”,我們可以將“process”打包以創(chuàng)建一些“name”玩焰。我們也可以通過使用*標(biāo)記“name”由驹,從而將“name”轉(zhuǎn)變?yōu)椤皃rocess”。
在rholang中昔园,我們需要記住的是發(fā)送“process”和接收“name”蔓榄。這很重要,因此我再次強(qiáng)調(diào)默刚。你總是發(fā)送一個“process”甥郑,在另一端接收一個“name”。
Aice通過for(message <- @"Alice")接收我們的消息荤西,所以澜搅, message 變成了一個“name”。當(dāng)她之后發(fā)送給Bob時邪锌,她不得不發(fā)送“process”勉躺,所以她要用@"Bob"!(message)使用將message轉(zhuǎn)變回一個“process”。
小測驗
我們發(fā)送什么秃流?
processes
names
我們接收什么赂蕴?
processes
names
@"registration"是什么?
process
name
非法語法
Nil是什么?
process
name
非法語法
@Nil是什么?
process
name
非法語法
@@Nil是什么?
process
name
非法語法
*importantData 是一個“process”, 那么importantData是什么?
process
name
非法語法
下面哪一個與"BobsPhone"等價?
*@"BobsPhone"
@"BobsPhone"
*"BobsPhone"
@*BobsPhone
stdout!("BobsPhone")
練習(xí)
This telephone game has a fork
不像之前的線性傳話游戲那樣舶胀,每個玩家將信息傳遞給下一位概说,我么來為游戲添加一個分支。現(xiàn)在嚣伐,Bob與先前一樣將發(fā)送消息給Charlie糖赔,但同時也會發(fā)送給Elise。
每個分支的長度由你定轩端,但在每個分支的最后都得將消息打印至標(biāo)準(zhǔn)輸出放典。
課程4 -- 持續(xù)發(fā)送與窺探
為什么要重復(fù)發(fā)送?
This radio navigation aid helps airplanes navigate by broadcasting the same message over and over
我們的披薩和咖啡店都可以在同一個復(fù)用通道中接收消息基茵。我們使用一個持續(xù)的for (msg <= chan){...}或者一個合約contract chan(msg){...}來達(dá)成這一目的奋构。
空中交通管制塔樓可能會樂于做剛好相反的事——不停地發(fā)送相同的消息。塔樓中的控制者希望記錄同時包含天氣和跑道信息的消息拱层,并且提供給所有需要的飛行員弥臼。類似披薩店, 他們很繁忙根灯,不會費(fèi)力地在每次飛行員需要時都不停地發(fā)送信息径缅。
持續(xù)發(fā)送的語法
控制塔需要在代碼上做較小的調(diào)整掺栅,以使得發(fā)送操作能夠持續(xù)。他們會使用!!而非單個!纳猪。
persistentSend.rho
請自行確認(rèn)一下氧卧,原先發(fā)送的消息是否仍然在元組空間內(nèi)。
練習(xí)
注意上述代碼氏堤,第二名飛行員同樣能夠接收到信息沙绝。發(fā)送仍在持續(xù)。
對了丽猬,你注意到了嗎宿饱?當(dāng)我們實(shí)際上并不使用stdout時,我們不需要new stdout(...) in {}
for (x <- y) {Nil} | y!!(Nil)中有多少次通信事件發(fā)生脚祟?
1
很多次
0
二次檢查消息
正如我們剛才展示的谬以,持續(xù)性發(fā)送和接收非常有用。但是由桌,普通的發(fā)送和接收也同樣足夠好了为黎。設(shè)想這樣的場景:我將一個字母發(fā)送給祖母,她接收到了這個消息行您。
grandma.rho
現(xiàn)在我們設(shè)想:我想要二次檢查我是否給她發(fā)送了正確的時間铭乾。我可以簡單地取出這條消息,但這樣一來她就沒法讀取這個消息了娃循。
練習(xí)
依據(jù)你所知道的炕檩,你可以通過獲取這個消息,自行檢查它捌斧,再將它發(fā)送回舊的通道笛质,以達(dá)到我們的目的。
請自行嘗試上面的方案捞蚂。答案已列在下面妇押。
for (x <= y) {Nil} | y!!(Nil)會產(chǎn)生多少個通信事件?
1
很多個
0
答案
grandmaCheck.rho
窺探語法
Maybe I'll just peak at Grandma's letter through the envelope.
rholang以后會為觀察通道內(nèi)變量提供一個特殊的語法姓迅。目前我們還不能使用它敲霍,但是下面會展示給你看這個語法的用法。我們將使用<!操作符來"窺探"一個通道內(nèi)的消息丁存。
peek.rho
如果你使用過excel的宏肩杈,或者excel,你應(yīng)該對如何在不取出數(shù)據(jù)的情況下訪問它感到非常熟悉解寝。把它當(dāng)做for (value <! A1) { ... }扩然。
下列哪一個語法是用于窺探一個消息的?
for (x <! y){...}
for (x <= y){...}
x!!(y)
for (x <! y) {Nil} | y!!(Nil)會產(chǎn)生多少個通信事件编丘?
1
許多
0
課程5 -- Join操作
多數(shù)據(jù)源
In general, the winner of this pushup competition can't be determined until both participants are finished.
有時候僅當(dāng)從兩個以上不同的數(shù)據(jù)源獲取數(shù)據(jù)后与学,才會開始計算。例如嘉抓,在你得知了你的彩票號碼和中獎號碼之前索守,你無法知道你是否贏得大獎。在你知道購買物品價格和購買總額之前抑片,你無法進(jìn)行購買卵佛。在你知道每個參賽者做了多少個俯臥撐前,你無法知道誰贏得俯臥撐比賽敞斋。
rholang提供了Join操作截汪,來應(yīng)對這種情況。使用;符號來執(zhí)行一次Join操作植捎。
for (p1Pushups <- @"player1"; p2Pushups <- @"player2") { @"stdout"!("The winner is...") }
火箭發(fā)射
一家太空探索公司想要確保衙解,僅當(dāng)兩個航空工程師,Alice和Bob焰枢,都下達(dá)了發(fā)射命令后蚓峦,他們的火箭才會發(fā)射。例如济锄,Bob將通過發(fā)送BobLaunch!("launch")來下達(dá)命令暑椰。當(dāng)兩位工程師都下達(dá)了命令,那么火箭便可以發(fā)射荐绝。
練習(xí)
思考一下一汽,使用我們剛提到的Join操作符,應(yīng)該怎么寫這個代碼呢低滩?
錯誤的方式
下面的例子中召夹,其中一人先收到發(fā)射指令,并嘗試處理火箭發(fā)射問題委造,然后再輪到另一個人戳鹅。
launchBad.rho
問題在于,當(dāng)Alice批準(zhǔn)發(fā)射昏兆,而Bob還沒有枫虏,Alice應(yīng)該能夠更改她的指令,但在此例中她不行爬虱。設(shè)想一下隶债,如果她突然發(fā)覺火箭有一個問題,或者收到了一些不好的消息跑筝,想要停止發(fā)射死讹。
No use in grabbing just one set of mail. Might as well wait until the second set
當(dāng)使用Join時,她依然可以更改她的決定曲梗,因為for只會在雙方的消息都進(jìn)入通道并準(zhǔn)備好后赞警,才會開始取出雙方的消息妓忍。
發(fā)射的解決方案
launch.rho
下列哪一段代碼是Alice所需,用以撤銷發(fā)射命令的愧旦?
@"AliceCancel"!("cancelZ")
@"AliceLaunch"!("cancel")
for (x <- @"AliceLaunch"){Nil}
Join的概念起初是在哲學(xué)家進(jìn)餐問題中被提出世剖,并且在這篇簡短的rholang教程中(更詳細(xì)的解釋)[developer.rchain.coop/tutorial/#d…"]。
在for (x <- y; a <- b){ Nil }中, 應(yīng)該優(yōu)先向哪一個通道發(fā)送消息笤虫?
y
b
無所謂
同時被發(fā)送
在for (x <- y; a <- b){ Nil }中, 哪一條消息被優(yōu)先取出旁瘫?
x
a
無所謂
會被同時取出
練習(xí)
有一個比賽,兩名選手將各自在各自的通道發(fā)送消息琼蚯。誰第一個發(fā)送了消息酬凳,誰就輸?shù)舯?賽,第二個發(fā)送消息的人獲勝遭庶。你的任務(wù)是寫一段代碼告訴我們誰贏了宁仔。參賽選手應(yīng)按如下方式發(fā)送消息。
P1!("Send any message") P2!("Hope I win")
在這場需要靠耐心獲勝競賽這一例子中峦睡,我們不使用求并運(yùn)算台诗,因為我們在意哪個選手先行動。希望你沒有陷入我的陷阱中;)
patienceSolution.rho
正如注釋所說赐俗,你應(yīng)該使用REPL模式運(yùn)行上面的代碼拉队,然后用兩種不同的順序來發(fā)送的消息確保兩個選手都獲勝一次。另一個方案如下所示阻逮,讓一個玩家去通知另一個玩家何時執(zhí)行粱快。我們將在下一節(jié)繼續(xù)研究這種方法。
P1First.rho
在上面我們寫的代碼中叔扼,為什么可能出現(xiàn)沒有人贏得這場耐心比賽事哭?
因為兩名選手可以同時發(fā)送消息
選手們在錯誤的通道發(fā)送消息
第一個塊接收P2,而第二個塊接收P1瓜富,所以代碼并不能保證游戲完成
課程6 -- 不可偽造的Names和Acks
使通道"私有"
A competing pizza shop steals orders because the channel isn't secure.
到目前為止鳍咱,每一個我們發(fā)送信息的通道都是公共的"name",如@"pizzaShop"与柑。 任何一個人都可以往這個通道發(fā)送信息(可能對于某些商用行為是好的)谤辜,但是任何一個人也可以從這個通道中獲取信息(這對于一些商業(yè)就很糟糕了)。想象一下如果競爭者可以從披薩店中獲取他們的披薩訂單讓披薩店無法獲取他們的訂單价捧,那肯定十分糟糕丑念。
披薩店的競爭者需要什么樣的代碼來竊取披薩點(diǎn)的訂單?
contract evilPizzaShop(interceptedMessage) = {Nil}
@"evilPizzaShop"!("pizzaShop")
@"pizzaShop"!("intercept")
for (interceptedMessage <- @"pizzaShop"){...}
綁定和自由的Names
上面我們學(xué)習(xí)到如何通過for和contract獲取信息。這兩種方式都構(gòu)造出"綁定的"“names”结蟋。舉個下面例子脯倚,order就是在咖啡店代碼里一個綁定的"name"。
bound1.rho
當(dāng)我們使用contract語法的時候也是一樣的嵌屎。
bound2.rho
如果一個"name"存在在一個特定的"process"中并且不能被"process"外部訪問推正,我們就認(rèn)為一個"name"是綁定的恍涂。所以"name" order是綁定在咖啡代碼中。另一方面植榕,在上面的例子中乳丰,任何一個能從別的地方訪問的"name"都是"自由的"“name”。在上面的例子中内贮,@"coffeeShop" 是一個自由的"name"。
指出下面每段代碼中 x 是綁定的還是自由的汞斧。
for (x <- y){Nil}
綁定的
自由的
都不是
for (y <- x){Nil}
綁定的
自由的
都不是
new x in { x!(true) }
綁定的
自由的
都不是
contract x(y) = { Nil }
綁定的
自由的
都不是
contract y(x) = { Nil }
綁定的
自由的
都不是
for (y <- @"x"){Nil}
綁定的
自由的
都不是
new操作符
for 和 contract都是在連續(xù)計算中綁定"name"的完美方法夜郁。但是如果我們想要創(chuàng)建一個綁定的"name"用于發(fā)送? 舉個例子,我們的披薩店不想讓自己的訂單被人截取粘勒。我們通過new操作符解決這個問題竞端。
newPizzaShop.rho
首先要注意到 pizzaShop 是一個"name"即使它不是以 @開始。
那是因為new操作符直接把它創(chuàng)造為一個"name"而不是一個引號括起的"process"庙睡。無論你如何使用new創(chuàng)造一個"name", 它總是一個綁定的"name"事富。
然后,注意這種方法不僅可以阻止其它披薩店獲取訂單乘陪,還阻止新的客戶下訂單统台。我們將會在bundles教程中解決這個問題。
當(dāng)你在new 限制范圍外嘗試下訂單會發(fā)生什么事情啡邑。
訂單正常發(fā)送
訂單正常發(fā)送但是需要更長時間
出現(xiàn)關(guān)于頂層自由變量的錯誤
代碼可以運(yùn)行贱勃,但是沒有訂單成功被接受不了
我們學(xué)習(xí)到所有的"name"可以通過用@標(biāo)記轉(zhuǎn)化為"process"。所以 pizzaShop這個"name"通過@轉(zhuǎn)化后是一個什么樣的"process"? 嘗試將那個"process"打印到stdout 看看谤逼。
@標(biāo)記的"pizzaShop"
并沒有任何標(biāo)記
"一些不可以偽造的16進(jìn)制代碼"
私有 vs 不可偽造
Although the messages can no longer be stolen, they can still be eavesdropped on. You've been warned.
new 是一個限制操作符因為它把自己創(chuàng)建的綁定的"names"限制在它的花擴(kuò)話中或者說"詞法范圍"內(nèi). 在rholang的世界里贵扰,這些新建的"names"就只能在確定的范圍內(nèi)可見,但是記住流部,程序員可以從外部世界中查找到這些"names"戚绕。當(dāng)你在區(qū)塊鏈環(huán)境工作中尤其要注意。
所以說枝冀,雖然競爭的披薩店不再可能竊取 本來給我們店的披薩訂單舞丛,但是他們?nèi)匀豢梢栽趨^(qū)塊鏈瀏覽器中知道我們這些訂單的信息。有些情況下果漾,一些程序員會把new 創(chuàng)建的"names"稱為 "私有的", 但是一個更恰當(dāng)?shù)脑~應(yīng)該是 "不可偽造的(unforgeable)", 這就能解釋前面的問題了瓷马。
我們前面用到了 new 來阻止元組空間被污染. 為什么使用不可偽造的"names"可以讓我們避免每個合約跑之前都清理一次元組空間?
因為 new 創(chuàng)建自由的"names"
因為 new 創(chuàng)建出不可偽造的"names",它們不能被外部代碼使用
因為 new 自動清理元組空間
確認(rèn)通道
We acknowledge communications all the time in conversations
不可偽造"names"一個通用的用法就是 "確認(rèn)通道", 簡稱為"ack" 通道. 披薩店可以僅僅讓顧客知道訂單已經(jīng)被下達(dá)跨晴,而不是通過打印到屏幕讓每一個人都知道來確認(rèn)訂單欧聘。
為了能實(shí)現(xiàn)這個方法,披薩點(diǎn)需要知道如何聯(lián)系客戶端盆。所以客戶需要提供一個確認(rèn)通道來回調(diào)怀骤。通常這樣的通道被稱為ack.
pizzaAck.rho
為什么前面例子的確認(rèn)信息并沒有顯示在屏幕上费封?
代碼中有錯誤
訂單沒有正確被接收
確認(rèn)信息沒有發(fā)送到stdout
練習(xí)
之前的例子會導(dǎo)致元組空間中的@"Alice" 和 @"Bob"通道被污染.修改它,讓Alice 和 Bob 各自有自己的不可偽造的"name".
給發(fā)送的"names"權(quán)限
我們剛剛看到顧客如何給出一個ack通道來獲取訂單確定信息. 其實(shí)我們可以做得更好. 在我們之前的代碼,任何一個人都可以在ack通道中聯(lián)系客戶. 那意味著任何一個人都可以發(fā)送一個偽造的ack通道給客戶讓客戶認(rèn)為訂單已經(jīng)下發(fā)成功蒋伦,但是實(shí)際上并沒有弓摘。所以Alice 和 Bob 真的需要嚴(yán)格保管他們的不可偽造的"names". 因為給別人那個"name"就意味著別人可以聯(lián)系你。
privateAck.rho
解決方案是創(chuàng)建一個新的不可偽造的"name",然后發(fā)送它到披薩店以至于只有他們可以回復(fù)你痕届。即使披薩店是在new alice的外面, 它仍然可以在那個通道上發(fā)送信息因為Alice給了通道名字韧献。這是一個很好的方法來委派權(quán)限。
在這個例子中研叫,我們相信披薩店只會在ack通道中 發(fā)送 锤窑,但是要注意它也又可能是在通道中接收信息,如果它想要的話嚷炉。我們將在下一節(jié)bundles中學(xué)習(xí)如何只給出一部分的權(quán)限出來渊啰。
Bob也想要訂一份披薩,給出一個不可偽造的ack通道申屹。我們應(yīng)該在哪里創(chuàng)建他自己的不可偽造的通道绘证?
在他自己的那行,alice代碼后面
在Alice同一行
在程序代碼的第一行
stdoutAck 和 stderrAck
現(xiàn)在既然你知道了ack通道, 那么你應(yīng)該要知道其它兩種打印到屏幕的方法.它們是叫做stdoutAck 和 stderrAck的通道. 他們就像第一課說的stdout一樣工作,但是他們需要一個ack通道哗讥。
stdoutAck.rho
順便說一句嚷那,你注意到每次啟動一個新的元組空間都有一堆東西么?這些東西其中4個東西是內(nèi)置的用于接受屏幕打印的通道杆煞。另外一些是用于加密的车酣。我們將在以后討論到。
練習(xí)
stdout!("1")|stdout!("2")|stdout!("3")
注意這段程序不會按照一定的順序打印出數(shù)字索绪。他們是同時發(fā)生的湖员。想象我們現(xiàn)在真的要按照順序打印幾行。修改代碼瑞驱,使用ack通道來保證數(shù)字按順序打印出來娘摔。
練習(xí)
預(yù)測這個程序怎么運(yùn)行(它會輸出什么,它在元組空間怎么簡化計算唤反。)然后運(yùn)行它來檢測你的預(yù)測凳寺。
new myChan in { myChan!("Hi There") } | for (msg <- myChan) {stdout!(*msg)}
如果你對上面的程序預(yù)測失敗,修改程序彤侍,讓程序按照你的想法運(yùn)行肠缨。
提問
在 for(x <- y){Nil}中哪個name是綁定的
x
y
Nil
在 new x in {Nil}哪個"name"是綁定的
x
y
Nil
如果 pizzzaShop 是一個"name", 那么 @pizzaShop是什么?
一個name
一個process
無效的語法
為什么pizzaShopAck 代碼發(fā)送 "bob" 作為一個ack通道而不是@"bob"?
沒有原因; 就是一種風(fēng)格。
因為 @"bob" 是一個name, 但是我們必須發(fā)送processed盏阶。
那是給ack通道用的特別語法晒奕。