1. 程式人生 > >通過示例學習rholang(上部:課程0-6)

通過示例學習rholang(上部:課程0-6)

通過例子和實踐來學習rho語言。下面的例子和練習都很值得去執行、閱讀、修改和完善。修改練習和教程中任何你感到有意思的程式碼,這樣能夠獲得最好的學習效果。該教程包含了rho語言最常見以及最重要的特性,足以讓開發者快速入門。

課程0 -- 開發環境

配置你的開發環境

為了可以執行這個教程裡面的rholang程式碼,你需要一些開發環境。 這不是一個會讓你感到疲憊的rholang開發工具或者技術棧。 然而它展示了一些基本的開發環境給你開始。

網上編譯器

RChain社群的成員提供了一個基於公共網站的線上rholang編譯器。 這個工具非常有前途,也是一種入門的簡單方式。 但是它還是開發節點,有時候會不穩定。

本地節點

真正正確執行rholang程式碼的方法是在通過啟動你自己本地機子的RNode然後使用它的rholang編譯器。 首先你要為你自己的平臺安裝 RNode

對於初學者,這裡有詳細的一步一步指導你怎麼使用AWS 或者Docker啟動你的節點.

一旦你的RNode安裝好了,你可以執行基本的獨立節點。

$ rnode run -s -n

在單獨的終端裡,你可以在REPL模式下一次執行一行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 }

當你執行更多行數的rholang程式碼時候,你可以使用RNode的eval模式來執行程式碼。

$ 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的輸出會出現在你執行程式碼的同一個終端。但是其它一些程式碼輸出會直接出現在第一個終端。 所以在你熟悉什麼輸出出現在哪裡前請確定好檢查兩邊的終端。

Cryptofex IDE

一個叫做cryptofex 的開發環境已經進入了alpha版本。 Cryptofex可能最後最好的開發rholang的地方,但是現在還是很早期的軟體。 Cryptofex提供rholang語法高亮特性並且可以在RChain整合節點上檢測dApps。 IDE同時也提供環境建立和測試在乙太網上,私人測試網上和單獨模式的EVM上的智慧合約。

課程1 -- 傳送與標準輸出(stdout)

傳送與標準輸出(stdout)

說聲Hello

 

"Person waiving hello"

 

程式設計界有一個存在已久的傳統——輸出"Hello World"應該是你學習的第一個程式。下面是一個在螢幕上輸出"Hello World"的最簡單例子。

hello.rho

練習

請讓程式輸出"Rholang rocks!" 而不是 "Hello World"。

練習

嘗試將"stdout"替換為別的語句。會得到什麼結果?

嘗試一下這個有趣的通道名稱@"someChannel". 這裡可以比較隨意。請讓程式在螢幕上輸出 "Sup World"。

標準輸出(stdout)到底是什麼東西

 

 

Channels are like mailboxes for sending messages

rho語言的核心是通道(channel,下面都稱為通道)通訊. 通道是你可以用來發送和接收訊息的通訊線路。你可以使用!字元來在通道中傳送訊息。

 

 

Redo this diagram!

stdout 是一個特殊的通道,用於將文字傳送至"標準輸出",通常指你的電腦螢幕。正因為它的特殊,我們不得不將它寫在第一段學習的程式碼裡面。

使用其他通道

 

 

Sent messages wait to be received here in "message purgatory"... JK, it's called the "tuplespace"

實際上你可以在很多通道中傳送訊息,而非只有stdout。 但其它通道不像 stdout 他們不會在螢幕上顯示。

tupleSpace.rho

那麼,在其他通道中的訊息將被髮送至哪裡?哪裡都不會去!這些訊息暫時哪兒都不去,這些訊息會繼續待在通道內,等待其他人去取出它們。我們將在下一課程中學習如何獲取這些訊息。同時,訊息滯留所在的地方,我們稱為 "元組空間"。

請確保你的資訊保留在元組空間裡。你應該會看到像下面的資訊。

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中,我們不會告訴計算機做完一件事,再到另一件。相反,我們會告訴它需要做的所有事情,然後"並行地"執行它們,或者一次性全部執行。

parallel.rho

| 的發音是 "parallel", 可簡稱為 "par"。

練習

向"pizza shop"通道傳送訊息"1 large pepperoni please"。

練習

向"Mom's Phone"通道傳送"Hi Mom"。

練習

用一個程式在螢幕上輸出兩個訊息,"Rick"和 "Morty"。

小測試

stdout!("Programming!") 將在螢幕上輸出什麼?

Programming!

stdout!

Nothing

@"what"!("Up") 在什麼通道上傳送訊息?

@"Up"

@"what"

what

rholang會先執行哪一條語句?

@"stdout"!("Dogs")

|

@"stdout"!("Cats")

輸出 "Dogs"

輸出 "Cats"

都不。 它們是並行的 PS. 有一個特殊的通道 stderr. 請嘗試一下看看往這個通道傳送訊息,會發生什麼? 有什麼區別?

課程2 -- 接收

訊息檢查

 

// Dear future self, keys in freezer because...

在上一章我們學習瞭如何傳送訊息。現在是時候學習如何接收訊息了。常規語法如下:

for(message <- channel){ // Do something here}

順便提一下, // 用於標示註釋。 //後面的內容程式並不會執行。寫好註釋可以有利於其他開發者(包括你自己)閱讀程式碼,並瞭解程式碼的意圖,其他讀你程式碼的開發者會感激你寫註釋的。

通訊事件

 

 

Pizza shop can receive messages on its channel.

下面的程式碼使用披薩店的通道傳送了一個訊息,披薩店收到了它。pizza店通過將訊息列印至標準輸出來表明其已收到。

pizzaOrder

練習

將上述訊息傳送至一個不同的通道,如@"coffeShop". 訊息會被接收端打印出來嗎? 還是東西留在了元組空間裡麼?

 

Let's hit up the coffee shop.

練習

記住,在rholang中,任何事情都是並行地而非按順序地執行。如果我們把接收資訊的程式碼放在前面,那麼披薩店的程式碼仍可執行。嘗試一下吧。

元組空間汙染

如果你遇到了舊資料滯留在元組空間並會對後面的程式碼執行有影響,你需要清空你的元組空間。最簡單的方式是刪除你的資料目錄.rnode

使用上述方法清空元組空間已經過時了。一個更好的方法是防止它一開始被舊資料汙染。我們可以通過修改最上面的new程式碼段來實現。

舊的方案

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”的課程中講解它的原理。現在你不需要每次都重置通道。

傳送前接收

 

 

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.

當傳送和接收同時存在於通道時,這被稱為通訊事件,或稱為"comm event"。

不像普通郵件那樣必須被髮送,對方才能被接收,在rholang中,上述兩個事件可以以任何順序發生或者同時發生。這類似於可以先接收訊息,再發送它。每當傳送和接收共存時,就會觸發通訊事件。

合約

 

 

The poor chef is too busy making sure he can receive orders to take care of his pizza.

我們的披薩店例子很好地說明了通訊事件,但期望每次有新的訂單時,披薩店都能自動發出一個新的接收來處理它們,這並不現實。

幸運地是,我們可以只部署一次程式碼,然後每次接收到它的訊息時都執行一次。這類程式碼稱為“智慧合約”。讓我們看一個比披薩店更高階但相似的例子--咖啡店。

coffeeShop.rho

練習

在咖啡店點第二杯飲料

練習

更改上面例子的確認訊息

一般來說,下列哪一個會第一個發生?

傳送,因為它與普通郵件的工作原理一樣。 接收,因為以該方式執行的程式碼更快。 傳送或接收都可以最先發生,或者同時。 接收,因為rohlang是並行的。 都不。直接觸發通訊事件(comm event)。

練習

通道被命名為 @"coffeeShop"。將它更名為你所選擇的特定咖啡店的名稱。然後使用我們最近學到的new來修改程式碼

Persistent For

實際上,在rholang中有兩種不同的語法來表示持續從通道取出資訊。我們剛剛學習contract語法。下面的用for語法的程式碼是等價的。

contract @"coffeeShop"(order) = { for(order <= @"coffeeShop") { 注意,上述程式碼與正常的 for 不同,因為它使用了雙劃線 <= 而不是單劃線 <-. for和contract是有不同的地方的,我們會在討論區塊鏈的時候討論到他們的區別。現在你可以將它們當做同一功能。

練習

用持久的for語法而不是"contract"語法來寫一個想咖啡店這樣的披薩店合約。嘗試自己從頭寫一次整個程式碼,這樣會讓你更容易記清語法。

下面哪一項是與其他兩項不同的?

for (a <- b){}

contract b(a) = {}

for (a <= b){}

哪一個傳送語句會與for (message <- @"grandmasSnapChat"){Nil}對應產生一個通訊事件 ?

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.

在前面的章節,我們學習瞭如何向祖母或披薩店傳送訊息。但是至今所有的接收方都通過將訊息列印至標準輸出,來告知已經接收到了。

現在讓我們做一些更有意思的事情--類似孩子們的傳話遊戲那樣傳遞訊息。

telephone3.rho

你可以通過執行上面的程式碼來做實驗。你可以修改你覺得合適的地方多執行幾次。

練習

傳話遊戲很有趣,但有更多玩家參與會更好。請新增第三位明教Charlie的玩家。bob接收訊息後將傳送訊息給Charlie,而不是簡單列印至stdout。然後Charlie將它列印至螢幕上。多多益善!

 

 

The message never seems to get there correctly. I blame Bob.

練習

如果你曾經玩過電話遊戲,你應該知道,訊息極少能被正確地傳遞。Bob現在決定通過傳送一條錯誤的訊息。改寫程式,使得Bob無論收到什麼,都能傳遞不同的訊息。

*這到底是啥?

 


Opposites attract

你注意到 @"Bob"!(message)中的? 在rholang中有兩種型別, "names" 和 "processes"。同樣也有可以在兩者之間互相轉化的方法。

"processes"可以是rholang中任何一個程式碼片段,例如我們的傳話筒遊戲,或者是披薩店訂單程式。“process”可以是上百行的大程式,也可以只有幾行。它們甚至可以是用於表示值的程式碼。下面是一些“process”的例子。

stdout!("Sup Rholang?") 一個常見的傳送操作。

Nil 最小的“process”。如字面意思,它不做任何事。

for(msg <- @"phone"){Nil} 一個常見的接收操作,在訊息到達時它不會做任何事。

"Hello World" 另一個不做任何事請的小“process”。被稱為"基礎術語"。 "names"可以被用於賦名通道以傳送訊息。在大多數程式語言中,"name"是完全獨立的一樣東西,它們本身就存在。但是在rholang中,"name"來自"引用process",即將@標籤放在“process”之前,即可得到一個"name"。下面是"name"的一些例子。

@"Hello World" 通過引用基礎術語"Hello World"來建立。

@Nil 最小的“name”。通過引用最小的“process”來建立。

@(@"Alice"!("I like rholang, pass it on."))

通過引用來自傳話筒遊戲的"process"來建立。

關於*的一切

 


What kind of name is that!? Did your parents just name you after some computer code?

通過用@符號來標記“process”,我們可以將“process”打包以建立一些“name”。我們也可以通過使用*標記“name”,從而將“name”轉變為“process”。

在rholang中,我們需要記住的是傳送“process”和接收“name”。這很重要,因此我再次強調。你總是傳送一個“process”,在另一端接收一個“name”。

Aice通過for(message <- @"Alice")接收我們的訊息,所以, message 變成了一個“name”。當她之後傳送給Bob時,她不得不傳送“process”,所以她要用@"Bob"!(message)使用將message轉變回一個“process”。

小測驗

我們傳送什麼?

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")

練習

 


This telephone game has a fork

不像之前的線性傳話遊戲那樣,每個玩家將資訊傳遞給下一位,我麼來為遊戲新增一個分支。現在,Bob與先前一樣將傳送訊息給Charlie,但同時也會發送給Elise。

每個分支的長度由你定,但在每個分支的最後都得將訊息列印至標準輸出。

課程4 -- 持續傳送與窺探

 


 

為什麼要重複傳送?

This radio navigation aid helps airplanes navigate by broadcasting the same message over and over

我們的披薩和咖啡店都可以在同一個複用通道中接收訊息。我們使用一個持續的for (msg <= chan){...}或者一個合約contract chan(msg){...}來達成這一目的。

空中交通管制塔樓可能會樂於做剛好相反的事——不停地傳送相同的訊息。塔樓中的控制者希望記錄同時包含天氣和跑道資訊的訊息,並且提供給所有需要的飛行員。類似披薩店, 他們很繁忙,不會費力地在每次飛行員需要時都不停地傳送資訊。

持續傳送的語法

控制塔需要在程式碼上做較小的調整,以使得傳送操作能夠持續。他們會使用!!而非單個!。

persistentSend.rho

請自行確認一下,原先發送的訊息是否仍然在元組空間內。

練習

注意上述程式碼,第二名飛行員同樣能夠接收到資訊。傳送仍在持續。

對了,你注意到了嗎?當我們實際上並不使用stdout時,我們不需要new stdout(...) in {}

for (x <- y) {Nil} | y!!(Nil)中有多少次通訊事件發生?

1

很多次

0

二次檢查訊息

正如我們剛才展示的,持續性發送和接收非常有用。但是,普通的傳送和接收也同樣足夠好了。設想這樣的場景:我將一個字母傳送給祖母,她接收到了這個訊息。

grandma.rho

現在我們設想:我想要二次檢查我是否給她傳送了正確的時間。我可以簡單地取出這條訊息,但這樣一來她就沒法讀取這個訊息了。

練習

依據你所知道的,你可以通過獲取這個訊息,自行檢查它,再將它傳送回舊的通道,以達到我們的目的。

請自行嘗試上面的方案。答案已列在下面。

for (x <= y) {Nil} | y!!(Nil)會產生多少個通訊事件?

1

很多個

0

答案

grandmaCheck.rho

窺探語法

Maybe I'll just peak at Grandma's letter through the envelope.

rholang以後會為觀察通道內變數提供一個特殊的語法。目前我們還不能使用它,但是下面會展示給你看這個語法的用法。我們將使用<!操作符來"窺探"一個通道內的訊息。

peek.rho

如果你使用過excel的巨集,或者excel,你應該對如何在不取出資料的情況下訪問它感到非常熟悉。把它當做for (value <! A1) { ... }。

下列哪一個語法是用於窺探一個訊息的?

for (x <! y){...}

for (x <= y){...}

x!!(y)

for (x <! y) {Nil} | y!!(Nil)會產生多少個通訊事件?

1

許多

0

課程5 -- Join操作

多資料來源

 


In general, the winner of this pushup competition can't be determined until both participants are finished.

有時候僅當從兩個以上不同的資料來源獲取資料後,才會開始計算。例如,在你得知了你的彩票號碼和中獎號碼之前,你無法知道你是否贏得大獎。在你知道購買物品價格和購買總額之前,你無法進行購買。在你知道每個參賽者做了多少個俯臥撐前,你無法知道誰贏得俯臥撐比賽。

rholang提供了Join操作,來應對這種情況。使用;符號來執行一次Join操作。

for (p1Pushups <- @"player1"; p2Pushups <- @"player2") { @"stdout"!("The winner is...") }

火箭發射

一家太空探索公司想要確保,僅當兩個航空工程師,Alice和Bob,都下達了發射命令後,他們的火箭才會發射。例如,Bob將通過傳送BobLaunch!("launch")來下達命令。當兩位工程師都下達了命令,那麼火箭便可以發射。

練習

思考一下,使用我們剛提到的Join操作符,應該怎麼寫這個程式碼呢?

錯誤的方式

下面的例子中,其中一人先收到發射指令,並嘗試處理火箭發射問題,然後再輪到另一個人。

launchBad.rho

問題在於,當Alice批准發射,而Bob還沒有,Alice應該能夠更改她的指令,但在此例中她不行。設想一下,如果她突然發覺火箭有一個問題,或者收到了一些不好的訊息,想要停止發射。

 

 

No use in grabbing just one set of mail. Might as well wait until the second set

當使用Join時,她依然可以更改她的決定,因為for只會在雙方的訊息都進入通道並準備好後,才會開始取出雙方的訊息。

發射的解決方案

launch.rho

下列哪一段程式碼是Alice所需,用以撤銷發射命令的?

@"AliceCancel"!("cancelZ")

@"AliceLaunch"!("cancel")

for (x <- @"AliceLaunch"){Nil}

Join的概念起初是在哲學家進餐問題中被提出,並且在這篇簡短的rholang教程中(更詳細的解釋)[developer.rchain.coop/tutorial/#d…"]。

在for (x <- y; a <- b){ Nil }中, 應該優先向哪一個通道傳送訊息?

y

b

無所謂

同時被髮送

在for (x <- y; a <- b){ Nil }中, 哪一條訊息被優先取出?

x

a

無所謂

會被同時取出

練習

有一個比賽,兩名選手將各自在各自的通道傳送訊息。誰第一個傳送了訊息,誰就輸掉比 賽,第二個傳送訊息的人獲勝。你的任務是寫一段程式碼告訴我們誰贏了。參賽選手應按如下方式傳送訊息。

P1!("Send any message") P2!("Hope I win")

在這場需要靠耐心獲勝競賽這一例子中,我們不使用求並運算,因為我們在意哪個選手先行動。希望你沒有陷入我的陷阱中;)

patienceSolution.rho

正如註釋所說,你應該使用REPL模式執行上面的程式碼,然後用兩種不同的順序來發送的訊息確保兩個選手都獲勝一次。另一個方案如下所示,讓一個玩家去通知另一個玩家何時執行。我們將在下一節繼續研究這種方法。

P1First.rho

在上面我們寫的程式碼中,為什麼可能出現沒有人贏得這場耐心比賽?

因為兩名選手可以同時傳送訊息

選手們在錯誤的通道傳送訊息

第一個塊接收P2,而第二個塊接收P1,所以程式碼並不能保證遊戲完成

課程6 -- 不可偽造的Names和Acks

使通道"私有"

 

 

A competing pizza shop steals orders because the channel isn't secure.

到目前為止,每一個我們傳送資訊的通道都是公共的"name",如@"pizzaShop"。 任何一個人都可以往這個通道傳送資訊(可能對於某些商用行為是好的),但是任何一個人也可以從這個通道中獲取資訊(這對於一些商業就很糟糕了)。想象一下如果競爭者可以從披薩店中獲取他們的披薩訂單讓披薩店無法獲取他們的訂單,那肯定十分糟糕。

披薩店的競爭者需要什麼樣的程式碼來竊取披薩點的訂單?

contract evilPizzaShop(interceptedMessage) = {Nil}

@"evilPizzaShop"!("pizzaShop")

@"pizzaShop"!("intercept")

for (interceptedMessage <- @"pizzaShop"){...}

繫結和自由的Names

上面我們學習到如何通過for和contract獲取資訊。這兩種方式都構造出"繫結的"“names”。舉個下面例子,order就是在咖啡店程式碼裡一個繫結的"name"。

bound1.rho

當我們使用contract語法的時候也是一樣的。

bound2.rho

如果一個"name"存在在一個特定的"process"中並且不能被"process"外部訪問,我們就認為一個"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都是在連續計算中繫結"name"的完美方法。但是如果我們想要建立一個繫結的"name"用於傳送? 舉個例子,我們的披薩店不想讓自己的訂單被人擷取。我們通過new操作符解決這個問題。

newPizzaShop.rho

首先要注意到 pizzaShop 是一個"name"即使它不是以 @開始。

那是因為new操作符直接把它創造為一個"name"而不是一個引號括起的"process"。無論你如何使用new創造一個"name", 它總是一個繫結的"name"。

然後,注意這種方法不僅可以阻止其它披薩店獲取訂單,還阻止新的客戶下訂單。我們將會在bundles教程中解決這個問題。

當你在new 限制範圍外嘗試下訂單會發生什麼事情。

訂單正常傳送

訂單正常傳送但是需要更長時間

出現關於頂層自由變數的錯誤

程式碼可以執行,但是沒有訂單成功被接受不了

我們學習到所有的"name"可以通過用@標記轉化為"process"。所以 pizzaShop這個"name"通過@轉化後是一個什麼樣的"process"? 嘗試將那個"process"列印到stdout 看看。

@標記的"pizzaShop"

並沒有任何標記

"一些不可以偽造的16進位制程式碼"

私有 vs 不可偽造

 

 

Although the messages can no longer be stolen, they can still be eavesdropped on. You've been warned.

new 是一個限制操作符因為它把自己建立的繫結的"names"限制在它的花擴話中或者說"詞法範圍"內. 在rholang的世界裡,這些新建的"names"就只能在確定的範圍內可見,但是記住,程式設計師可以從外部世界中查詢到這些"names"。當你在區塊鏈環境工作中尤其要注意。

所以說,雖然競爭的披薩店不再可能竊取 本來給我們店的披薩訂單,但是他們仍然可以在區塊鏈瀏覽器中知道我們這些訂單的資訊。有些情況下,一些程式設計師會把new 建立的"names"稱為 "私有的", 但是一個更恰當的詞應該是 "不可偽造的(unforgeable)", 這就能解釋前面的問題了。

我們前面用到了 new 來阻止元組空間被汙染. 為什麼使用不可偽造的"names"可以讓我們避免每個合約跑之前都清理一次元組空間?

因為 new 建立自由的"names"

因為 new 創建出不可偽造的"names",它們不能被外部程式碼使用

因為 new 自動清理元組空間

確認通道

 

 

We acknowledge communications all the time in conversations

不可偽造"names"一個通用的用法就是 "確認通道", 簡稱為"ack" 通道. 披薩店可以僅僅讓顧客知道訂單已經被下達,而不是通過列印到螢幕讓每一個人都知道來確認訂單。

為了能實現這個方法,披薩點需要知道如何聯絡客戶。所以客戶需要提供一個確認通道來回調。通常這樣的通道被稱為ack.

pizzaAck.rho

為什麼前面例子的確認資訊並沒有顯示在螢幕上?

程式碼中有錯誤

訂單沒有正確被接收

確認資訊沒有傳送到stdout

練習

之前的例子會導致元組空間中的@"Alice" 和 @"Bob"通道被汙染.修改它,讓Alice 和 Bob 各自有自己的不可偽造的"name".

給傳送的"names"許可權

我們剛剛看到顧客如何給出一個ack通道來獲取訂單確定資訊. 其實我們可以做得更好. 在我們之前的程式碼,任何一個人都可以在ack通道中聯絡客戶. 那意味著任何一個人都可以傳送一個偽造的ack通道給客戶讓客戶認為訂單已經下發成功,但是實際上並沒有。所以Alice 和 Bob 真的需要嚴格保管他們的不可偽造的"names". 因為給別人那個"name"就意味著別人可以聯絡你。

privateAck.rho

解決方案是建立一個新的不可偽造的"name",然後傳送它到披薩店以至於只有他們可以回覆你。即使披薩店是在new alice的外面, 它仍然可以在那個通道上傳送資訊因為Alice給了通道名字。這是一個很好的方法來委派許可權。

在這個例子中,我們相信披薩店只會在ack通道中 傳送 ,但是要注意它也又可能是在通道中接收資訊,如果它想要的話。我們將在下一節bundles中學習如何只給出一部分的許可權出來。

Bob也想要訂一份披薩,給出一個不可偽造的ack通道。我們應該在哪裡建立他自己的不可偽造的通道?

在他自己的那行,alice程式碼後面

在Alice同一行

在程式程式碼的第一行

stdoutAck 和 stderrAck

現在既然你知道了ack通道, 那麼你應該要知道其它兩種列印到螢幕的方法.它們是叫做stdoutAck 和 stderrAck的通道. 他們就像第一課說的stdout一樣工作,但是他們需要一個ack通道。

stdoutAck.rho

順便說一句,你注意到每次啟動一個新的元組空間都有一堆東西麼?這些東西其中4個東西是內建的用於接受螢幕列印的通道。另外一些是用於加密的。我們將在以後討論到。

練習

stdout!("1")|stdout!("2")|stdout!("3")

注意這段程式不會按照一定的順序打印出數字。他們是同時發生的。想象我們現在真的要按照順序列印幾行。修改程式碼,使用ack通道來保證數字按順序打印出來。

練習

預測這個程式怎麼執行(它會輸出什麼,它在元組空間怎麼簡化計算。)然後執行它來檢測你的預測。

new myChan in { myChan!("Hi There") } | for (msg <- myChan) {stdout!(*msg)}

如果你對上面的程式預測失敗,修改程式,讓程式按照你的想法執行。

提問

在 for(x <- y){Nil}中哪個name是繫結的

x

y

Nil

在 new x in {Nil}哪個"name"是繫結的

x

y

Nil

如果 pizzzaShop 是一個"name", 那麼 @pizzaShop是什麼?

一個name

一個process

無效的語法

為什麼pizzaShopAck 程式碼傳送 "bob" 作為一個ack通道而不是@"bob"?

沒有原因; 就是一種風格。

因為 @"bob" 是一個name, 但是我們必須傳送processed。

那是給ack通道用的特別語法。