通过例子和实践来学习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通道用的特别语法。
作者:RChain中国
链接:https://juejin.im/post/5e3177ca6fb9a02fee1ede93
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。