经过例子和实践来学习rho语言。下面的例子和练习都很值得去运行、阅读、修改和完善。修改练习和教程中任何你感到有意思的代码,这样可以得到最好的学习效果。该教程包含了rho语言最多见以及最重要的特性,足以让开发者快速入门。node
课程0 -- 开发环境程序员
配置你的开发环境web
为了能够运行这个教程里面的rholang代码,你须要一些开发环境。 这不是一个会让你感到疲惫的rholang开发工具或者技术栈。 然而它展现了一些基本的开发环境给你开始。编程
网上编译器浏览器
RChain社区的成员提供了一个基于公共网站的在线rholang编译器。 这个工具很是有前途,也是一种入门的简单方式。 可是它仍是开发节点,有时候会不稳定。app
本地节点编程语言
真正正确运行rholang代码的方法是在经过启动你本身本地机子的RNode而后使用它的rholang编译器。 首先你要为你本身的平台安装 RNode工具
对于初学者,这里有详细的一步一步指导你怎么使用AWS 或者Docker启动你的节点.oop
一旦你的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
编程界有一个存在已久的传统——输出"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通道用的特别语法。