HI

Rholang

为你打开Rholang世界的大门

V1.0

中文版

Rholang 简介

 

Rholang是一门基于Rho calculus (Rho 演算) 的并发语言,专为分布式系统设计。

 

Rholang程序是完全异步的,由消息传输机制驱动执行。

 

Rholang不只是一门智能合约语言,可用于其它计算环境中。

 

Rholang 源于 Rho Calculus

 

传统的计算机语言基于图灵机和自动机的概念,适于描述单个自动机的行为。

 

而Pi Calculus(Pi演算)则为开放而自由的网络而生,描述分布式的网络环境。

 

Rho calculus是在Pi Calculus的基础上,增加了Name(名字)和Process(过程)的转换方法,称之为Reflection(反射性)。

 

Rho calculus

P,Q,R ::=0                                      //nil or stopped process
  | for ( ptrn1 <- x1; ... ; ptrnN <- xN ).P    //input guards
  | x!( Q )                                     //output
  | \*x\                                        //dereferenced or unquoted name
  | P|Q                                         //parallel composition
x,ptrn ::= @P                                   //name or quoted process 

目录

Rholang环境准备

方法1:在本地运行Rnode节点 (推荐)

1.本地运行Rnode节点(以Mac为例),下载最新版的Rnode客户端,下载地址:https://developer.rchain.coop/。(注意请下载文件名为 .TGZ的客户端,本教程中使用的均为该客户端)

2.打开“终端”,通过命令进入解压缩后的文件夹,找到bin文件夹并进入。

3.在bin文件夹下通过命令:./rnode run -s -n 来运行一个单独的本地节点(Stand Alone)。

4.将写好的智能合约存入文件夹,通过命令:./rnode eval /路径/xxx.rho运行智能合约。

Cryptofex可以在Windows、Mac和Linux上本地运行,下载地址:https://cryptofex.io/

方法2:Cryptofex IDE

方法3:使用Rholang在线Interpreter 

在线Interpreter不需要配置任何环境就可以直接在网页端写出智能合约并运行,网页链接:https://rchain.cloud

Send & Receive

01

Send

1. a!(P)指Send一次,a!!(P) 指持续Send

 

2. Send 可以理解为:一个Process被发送/持续发送到目标Name中,换个说法是:目标Name被放入/持续放入了一个Process

 

3. 一个最常见的Send:stdout!(P),作用是:打印到屏幕

 

4. stdout(`rho:io:stdout`)是Rholang的Power Box,相当于标准库

new HelloWorld, stdout(`rho:io:stdout`) in {
    HelloWorld!("Hello, world!")|
    for (@text <- HelloWorld) {
        stdout!(text)
    }
}

//运行结果:屏幕上出现“Hello,world!”

Send

Receive

1. for (a <- b) {Q} 指Receive一次,即:目标Name里的Process被取走后,触发process Q

 

2. for (a <= b) {Q} 指持续Receive,即:只要目标Name里有Process,就被取走,取走后触发Process Q

 

3. contract b(a) = {P}是for (a <= b) {P}的语法糖

//用for接收消息
new pizzaShop, stdout(`rho:io:stdout`) in {
    pizzaShop!("2 medium pies")|
    pizzaShop!("5 medium pies")|
    for (order <- pizzaShop) {
        stdout!("Order Received.")
    }
}

//用for持续接收消息
new pizzaShop, stdout(`rho:io:stdout`) in {
    for (order <= pizzaShop) {
        stdout!("Pizza Order Received")
    }|
    pizzaShop!("one hot chocolate")|
    pizzaShop!("two large latte's please")
}

//用contract持续接收消息
new coffeeShop, stdout(`rho:io:stdout`) in {
    contract coffeeShop(order) = {
        stdout!("Coffee Order Received")
    }|
    coffeeShop!("one hot chocolate")|
    coffeeShop!("two large cappuccinos please")
}

#5:即使pizzaShop被放入了两个Process,for也只能取走一个

 

#12:只要 pizzaShop被放入了Process,就会被取走

 

#21:只要coffeeShop 被放入了Process,contract coffeeShop就被触发,Process就会被取走

Receive

如何更深入地理解Send 和 Receive?(难点)

1. a!(Q)

Process Q 向 Name a的传递

 

2. for (@c <- a ) {P}

a里面的Process被传递到了@c这个Name里     ✗

a里的Process被消耗,同时c被赋值(bind)为a里的Process,可以直接使用,@c 成为一个有作用域的Name    ✓

new a, stdout(`rho:io:stdout`) in {
    a!(1)|
    for (@b <- a) {
        for (@c <- @b) {
            stdout!([c, "index1"])
        }|
        stdout!([b,"index2"])
    }
}

3. a!(Q)|for (@c < -a) {P}

包含同一个Name的一次Send 和 一次Receive 组成了一个Comm Event

//运行结果是:[1, "index2"]
//@b里没有Process,所以@c取不到值

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

很多时候,要得到两个或更多数据源的数据才能够执行一个计算。比如,两个运动员赛跑,要得到他们两个的成绩之后,才能判断输赢。

 

在Rholang里,这种情况要使用join运算符,也就是#1中两个receive中间的 ;

Join Operator

new aliceLaunch, bobLaunch, stdout(`rho:io:stdout`) in {
    for (x <- aliceLaunch) {
        for (y <- bobLaunch) {
            stdout!("Launching the rocket")
        }
    }|
    aliceLaunch!("launch")|
    bobLaunch!("launch")
}

错误实现   ✗

一旦Alice发现问题,想要取消发送指令,她无法做到终止发射。

例子1: Rocket Launch

一架火箭准备被发射,程序设定,只有它在收到Alice 和 Bob 两个飞行员发出的发射指令后,才能被触发发射

Join Operator

new aliceLaunch, bobLaunch, stdout(`rho:io:stdout`) in {
    for (x <- aliceLaunch; y <- bobLaunch) {
        stdout!("Launching the rocket")
    }|
    aliceLaunch!("launch")|
    bobLaunch!("launch")
}

正确实现   ✓

#2中,x、y都Receive到指令后才能触发#3,在那之前,aliceLaunch 和 bobLaunch里的消息均不会被取走。

例子1: Rocket Launch

Join Operator

两名玩家独立发送消息,谁的消息先被收到,谁就输了

例子2:Patience Game

错误实现   ✗

new p1, p2, stdout(`rho:io:stdout`) in {
    p1!("Send any message")|
    p2!("Hope I win")|
    for (m2 <- p2) {
        for (m1 <- p1) {
            stdout!("Player one wins!")
        }
    }|
    for (m1 <- p1) {
        for (m2 <- p2) {
            stdout!("Player two wins!")
        }
    }
}

#4和#9并行,此时,p1、p2里的消息都已经被取走,#5和#10没有一个能被触发。

Join Operator

例子2:Patience Game

正确实现   ✓

new p1, p2, a, stdout(`rho:io:stdout`) in {
    p1!("Send any message")|
    p2!("Hope I win")|
    a!(1)|
    for (m2 <- p2; _ <-a) {
        for (m1 <- p1) {
            stdout!("Player one wins!")
        }
    }|
    for (m1 <- p1; _ <-a) {
        for (m2 <- p2) {
            stdout!("Player two wins!")
        }
    }
}

a的引入起到了“开关”的作用,配合Join运算符的使用,实现程序目的

Join Operator

例子3: Dining Philosophers and Deadlock

什么是Deadlock?

 

多个进程或线程相互等待对方的资源,在等到对方资源之前,也不会释放自己的资源,这样造成循环等待的现象,若无外力作用,他们都无法推进下去,这样就产生了deadlock。

 

Dining Philosophers 就是非常经典的Deadlock问题。

 

在下面的案例中,把Dining Philosophers简单化:两名哲学家,分别坐在桌子的东边和西边,共享一副刀叉,而同时拥有刀叉的一方方能就餐。

 

Join Operator

new philosopher1, philosopher2, north, south, knife, spoon in {
    north!(*knife)|
    south!(*spoon)|
    for (@knf <- north) {
        for (@spn <- south) {
            philosopher1!("Complete!")|
            north!(knf)|
            south!(spn)
        }
    }|
    for (@spn <- south) { 
        for (@knf <- north) {
            philosopher2!("Complete!")|
            north!(knf)|
            south!(spn)
        } 
    }
}

错误实现   ✗

例子3: Dining Philosophers and Deadlock

Join Operator

new philosopher1, philosopher2, north, south, knife, spoon in {
    north!(*knife)|
    south!(*spoon)|
    for (@knf <- north; @spn <- south) {
        philosopher1!("Complete!")|
        north!(knf)|
        south!(spn)
    }|
    for (@spn <- south; @knf <- north) {
        philosopher2!("Complete!")|
        north!(knf)|
        south!(spn)
    }
}

正确实现   ✓

例子3: Dining Philosophers and Deadlock

Join Operator

并行 和 串行

02

什么是并行

 

在Rholang中,所有的Process都是并行的,Process之间用|来连接。

new a in {
    a!(1)|
    a!(2)|
    for(@x<-a){Nil}|
    if(P){Q}
    else{R}|
    contract a(b)={P}
}

并行的特点

new a, stdout (`rho:io:stdout`) in{
    a!(1)|
    a!(2)|
    a!(3)|
    a!(4)|
    a!(5)|
    for(@x<=a){
        stdout!(x)
    }
}

 

并行有两个特点:

1.所有Process都会执行。

2.并行执行没有先后顺序,是随机的。

 

#2 #3 #4 #5 #6 #7是并行的,所以输出顺序是随机的。

例子1: 随机输出

new a,b,stdout (`rho:io:stdout`) in{
    a!(1)|
    for(@x<-a){
        b!(x+1)|
        stdout!(x)
    }|
    for(@y<-b){
        stdout!(y)
    }
}

Rholang中如何做到顺序执行

通过参数的传递来做到顺序执行。

例子2: 顺序输出

 

  • a里有Process, #3 才会执行
  • b里有Process, #6才会执行。

Process & Name

03

Process和Name

 

Process是一段Rholang代码,比如实现某种功能的完整代码,或是一段很短的代码片段。

举几个例子:

  • Send:stdout!(" Sup Rholang? ")
  • 最小的Process,表示什么都不做:Nil
  • 接到消息后什么都不做:for(msg <- phone){ Nil }
  • Ground Terms:“Hello World”、“Hi, Rholang”、1、 ......

 

All computation done by processes communicating over names

所以在Rholang中除了Process就是Name,且两者可以相互转换。

Name are quoted processes,Name又被称为Channel,用来传输和存储Process。

在Rholang中,有两种形式的name:

 

一种是使用new和for语法定义的Name,如

  • new a in { for ( b <- a) { Nil } } 中的a和b都是新定义的Name。

 

另一种是被引用的Process,用@标记,如

  • 引用了Ground Term的name:@“Hello World”
  • 最小的name:@Nil
  • 引用了具体的process的name:@P、@{a! (P)}、@{for (b <- a) {P}}

Process和Name

new alice, bob, stdout(`rho:io:stdout`) in {
    alice!("How to program: Change stuff and see what happens.")|
    for (message <- alice) {
        bob!(*message)
    }|
    for (message <- bob) {
        stdout!(*message)
    }
}

例子1:Telephone Game

Alice向Bob传递消息,Bob收到后将它打印出来。

alice、bob、message是Name;

“How to program: Change...... ”、*message以及整段代码是Process。

Process和Name

In rholang we send processes and receive names.

在 a! ( P ) 中,a是Name,P是Process;

在for(b <- a) { P }中,a和b是Name,P是Process。

例子中,哪些Name,哪些是Process?

 

用@符号来标记Process,就转换成了Name,如@P ;

用*符号标记来Name,就转换成了Process,如*x ;

Process通过Name传递。

Process与Name的转换

Process与Name的转换

process name
Nil @Nil
*importantData importantData
"BobsPhone" ackChannel
*@"BobsPhone" @*ackChannel
**name @@Nil
new a, b, stdout(`rho:io:stdout`) in {
    a!(*b)|
    b!(1)|
    for (@c <- a) {
        stdout!(c)
    }
}

//结果: Unforgeable(0x0ef84f0816679bafc4365da3e28d2ea9f674823f0dc7709dd3c74d2b8ac0a038)

a,b是name,c是process

 

#2 当使用Send语句时,b需要转化为process,被*标记

*b并不是对应b中存放的process,而是将b由name转换成process

 

#4 在for语法中,箭头左侧是name,c用@标记

Process与Name的转换

for (@n <- m) {stdout!(n)}

Process与Name的转换

通常,在for语句中,出现在箭头左侧的Name,如果更多的以Process的形式被传递和存储,则使用被引用的Process,如左侧例子中的@n。

 

同样的,在contract语句中,括号里面的Name,如果更多的以Process的形式被传递和存储,则使用被引用的Process,如右侧例子中的@a。

 

但当Name用来作为确认程序执行结束或返回结果用时(如ack、return),不需要进行Process与Name的转换,如右侧例子中的ack。

contract example(@a, ack) = {
    stdout!(a)|
    ack!(Nil)
}
new get, initial, stdout(`rho:io:stdout`) in {
    initial!(0)|
    contract get(@"new", @arg, ack) = {
        for (@old <- initial) {   
            initial!(arg)|
            ack!(old)  
        }
    }|
    new ack in {
        get!("new", 1, *ack) |
        for (_ <- ack) {
            stdout!("The process had finished.")
        } 
    }   
}

注意程序中get、initial、“new”、@arg、 ack、 @old的用法

Process与Name的转换

如果Process是等价的,那被引用后形成的Name就是等价的

 

1. @{P|Q}、@{Q|P}、@{P|Q|Nil}等价

​|是并行,没有先后顺序,所以P|Q、Q|P、P|Q|Nil 是等价的Process

 

2.@{ for (x <- chan) {P} } 与 @{ for (z <- chan) {P} } 等价

x、z是for 语句中的Bind Variable,所以for (x <- chan) {P}、for (z <- chan) {P}是等价的Process

Name等价

3. @{8+2} 与 @{5+5}等价

Name等价

new stdout(`rho:io:stdout`) in {
    for (a <- @{8+2}) {
        stdout!(*a)
    }|
    @{5+5}!("Hello")
}
Evaluating:
new x0 in {
  @{(5 + 5)}!("Hello") |
  for( @{x1} <- @{(8 + 2)} ) {
    x0!(x1)
  }
}
"Hello"

在名字被使用之前,先进行运算,运算结果(也是Process)等价,则被引用的形成的Name等价。运算包括:

 

算术运算:+, -, *, /

关系运算:>, >=, <, <=, ==, !=

逻辑运算符:and, or, not

匹配运算符:P match Q

Name 的类型

04

new pizzaShop, stdout(`rho:io:stdout`) in {
    contract pizzaShop(order) = {
        stdout!("Order Received.")
    }
}|
pizzaShop!("Extra bacon please")|
pizzaShop!("Hawaiian Pizza to go")

#1 pizzaShop只在new后面的{...}内起作用,在外部无法对pizzaShop进行操作

约束变量:指在某Process中定义,只在该Process之内起作用的Name。

约束变量和自由变量

Bind Variable & Free Variable

new a {P}
for(a<-b){P}
contract b(a)={P}

对于P来说,a均为约束变量,只在P内起作用

错误实现

✗

#6 #7 pizzaShop在这里未被定义,会报错

提交订单

new pizzaShop, stdout(`rho:io:stdout`) in {
    contract pizzaShop(order) = {
        stdout!("Order Received.")
    }|
    pizzaShop!("Extra bacon please")|
    pizzaShop!("Hawaiian Pizza to go")
}

// 运行结果
// "Order Received."
// "Order Received."

正确实现

✓

提交订单

约束变量和自由变量

Bind Variable & Free Variable

自由变量:指在某Process之外被定义,那么对该Process而言,这个Name是自由变量。

new b in {Q|for (a <- b) {P}}
new b in {Q|contract b(a) = {P}}

对于P来说,b均为自由变量。

在包含P的更大作用域内被定义,不仅在过程P内起作用,在Q内也起作用。

约束变量和自由变量

Bind Variable & Free Variable

所有Name在被定义时,被约束在其后的整个Process,但对于其内部的Process是自由变量

new a,stdout(`rho:io:stdout`) in {
    a!(Nil)|
    a!(Nil)|
    for (x <- a) {P}|
    contract a(y) = {Q}
}

a对于整个Process而言是约束变量,对于P、Q而言是自由变量

约束变量和自由变量

Bind Variable & Free Variable

for (x <- y){Nil}

约束

自由

都不是

 

for (y <- x){Nil}

 约束

 自由

都不是

 

new x in { x!(true) }

约束

自由

都不是

contract x(y) = { Nil }

约束

自由

都不是

 

contract y(x) = { Nil }

约束

自由

都不是

 

 

在以下Process中,x是什么变量

✓   

✓   

✓   

✓   

✓   

约束变量和自由变量

Bind Variable & Free Variable

  1. Unforgeable Name 是Object Capability Security Model  (OCAP) 里的一个核心概念

  2. 出于安全的考虑,在一个Process内部定义的Name不应该被外部访问到,除非有意通过消息机制传递出去。

Unforgeable Name

Unforgeable Name的定义

用new定义的Name是Unforgeable Name,由一串独一无二的字符表示。可以理解为身份证系统中,同名的人身份ID互不相同。

new a,stdout(`rho:io:stdout`) in{
    stdout!(*a)|
    new a in{
        stdout!(*a)
    }
}|
new a,stdout(`rho:io:stdout`) in{
    stdout!(*a)
}

// 运行结果
// Unforgeable(0x729d3a68aaf4b4b4985b168f95626d2842cc4e3a972e265bc2538fbf1c83db58)
// Unforgeable(0x46404982fa2da2ba74ac7b6816eca1af46a8ab5880bfdf73cacf4f794262a775)
// Unforgeable(0x5ab9526e8bcc709e85e914ba96ad09a7e90e92258b9ca087f4ca754eb0341df4)

注:每次运行,Unforgeable Name都不一样

Unforgeable Name

用new定义的Name

用for和contract定义的Name,不是Unforgeable Name,可以理解为昵称或代号。

new a,stdout(`rho:io:stdout`) in{
    stdout!(*a)|
    a!(100)|
    for(x<-a){
        stdout!(*x)
    }
}

// 运行结果
// Unforgeable(0x6f2df38e2d7fa3cc4f7f40cbede8834a069ac6d52a4b8f9825fd5ed5fa043ce6)
// 100

Unforgeable Name

用for和contract定义的Name

for 定义的Name

Unforgeable Name

Unforgeable Name 的传递

  • Name可以反射为Process (a是Name,那么*a是Process)
  • 通过Unforgeable Name 的传递,可以实现Process的传递。传递Unforgeable Name就相当于把该Name相关的Process都共享给了另一个Name。
new a, b in {
    b!(*a)|
    for (x <- b) {
        x!(P)
    }|
    for (y <- a) {Q}
}
new a in {
    a!(P)|
    for (x <- a) {Q}
}

=

#3 先被触发,x被赋值为a的Unforgeable Name

#4 *x与*a等价,因此{x!(P)}与{a!(P)}等价,从而#6 被触发

new alice, bob, stdout(`rho:io:stdout`) in {
    alice!(*bob)|
    bob!("Bob,You are the best. From Charlie")|
    stdout!(["un1 ", *bob])|
    for (bobCh <- alice) {
        for (@letter <- bobCh){
            stdout!(letter)
        }|
        stdout!(["un2",*bobCh])
    }
}
  • alice, bob, bobCh, @letter中哪些Name有Unforgeable Name?
  • 以上两个智能合约输出结果一样吗?

Unforgeable Name

Unforgeable Name 的传递

new alice, bob, stdout(`rho:io:stdout`) in {
    alice!(*bob)|
    stdout!(["un1",*bob])|
    for (@letter <- bob){
        stdout!(letter)
    }|
    for (bobCh <- alice) {
        bobCh!("Bob,You are the best. From Charlie")|
        stdout!(["un2",*bobCh])
    }
}

Charlie给Bob写了一封信,Alice帮Bob读信

Charlie写了一封信,Alice帮他寄给Bob

#2 Bob的Unforgeable Name发送给了Alice,从而Alice可以对Bob读和写

  1. 每一个智能合约部署到区块链后,对所有人都是可见的。从区块链上读到的合约内容中,用new定义的Name以一串字符(Unforgeable Name)表示。
  2. Unforgeable Name的生成不可预测,互不相同。
  3. It is impossible to guess the name of a process, and thereby interact with that process. 链上智能合约中包含很多可见的Unforgeable Name,仅通过Unforgeable Name不可调用和触发该Name。(必须通过该Unforgeable Name在区块链上的Uri才能调用和触发它,每一个Unforgeable Name 的Uri在区块链上不可查,也不可预测。详见智能合约与区块链的交互。)

Unforgeable Name

Unforgeable Name在区块链上的应用

new oldHen, round, counter, stdout(`rho:io:stdout`) in {
    contract oldHen ( ack ) = {
        round!(1)|
        for (@round_ <= round) {
            counter!(1)|
            if (round_ < 4) {
                for (@counter_ <= counter){
                    if(counter_ < 4 ){
                        counter!( counter_ + 1 )|
                        stdout!(["counter",counter_ ])                    
                    }    
                    else {
                        round!( round_ + 1)|
                        stdout!(["round", round_ ])                
                    }
                }
            }
        }
    }|
    oldHen!(1)
}

计数器

每轮都是一个从1到3的计数器,一共3轮

错误实现

✗

More Cases

#4 "<="相当于有多个Process (#4-#18,称为P1)在等待round里的值。

#7 "<="相当于有多个Process (#7-#16,称为P2)在等待counter里的值。

一旦某P1被触发,counter就被放入1。由于counter的作用域是所有P2,因此其他P1里的P2就有可能被触发,从而counter的结果变成乱序的死循环。

……

P1

……

P2

P2

P2

P1

……

P2

P2

P2

P1

……

P2

P2

P2

counter!(1)

counter!(1)

counter!(1)

More Cases

计数器

每轮都是一个从1到3的计数器,一共3轮

错误实现示意图

可能触发任意等待中的P2

new oldHen, round, stdout(`rho:io:stdout`) in {
    contract oldHen ( ack ) = {
        round!(1)|
        for (@round_ <= round) {
            if (round_ < 4) {
                new counter in{
                    counter!(1)|
                    for (@counter_ <= counter){
                        if(counter_ < 4 ){
                            counter!( counter_ + 1 )|
                            stdout!(["counter",counter_])                    
                        }    
                        else {
                            round!( round_ + 1)|
                            stdout!(["round", round_])                
                        }
                    }
                } 
            }
        }
    }|
    oldHen!(1)
}

每一个P1(#4 -20)里定义的counter都不同,作用域为该P1里的Q(#6-18)。

 

每一个P1里有多个P2(#8-17)在等待counter里的值。

 

一个P1里的counter只能触发该P1里的P2,不能触发其他P1里的P2

计数器

每轮都是一个从1到3的计数器,一共3轮

正确实现

✓

More Cases

计数器

每轮都是一个从1到3的计数器,一共3轮

More Cases

……

P1

P1

P1

……

P2

P2

P2

counter1!(1)

……

P2

P2

P2

counter2!(1)

……

P2

P2

P2

counter3!(1)

Q

Q

Q

正确实现示意图

✗

在这个例子中,因为"<="的特殊性,由变量类型导致的错误非常隐蔽,这就提醒我们在定义name时应该注意其作用范围。

这个计数器有没有更好的实现方法?

More Cases

用 Join Opertator的方法

计数器

每轮都是一个从1到3的计数器,一共3轮

Contract Factory

05

关于Contract Factory

1. Contract Factory是一组定义好的合约

 

2. 在面向对象编程的语言里,经常使用封装(数据+方法)的技术,来模拟现实世界的业务逻辑,

在Rholang里,此项功能是用Contract Factory来实现的

 

3. 通常,Contract Factory有两种写法,示例如下:

// Separation of Powers
// line4、line5被触发时,method1 与 method2 会被分别传入unforgeable name
contract factory(method1, method2) = {
    contract method1(ack) = { ... }
    |
    contract method2(ack) = { ... }
}

// Control Panel
// 只有factory拥有unforgeable name
contract factory(cPanel) = {
    contract @[cPanel, "method1"](ack) = { ... }
    |
    contract @[cPanel, "method2"](ack) = { ... }
}

例子1:计数器

从0开始、可被重置的计数器

new currentCount, increase, reset, stdout(`rho:io:stdout`) in {

    currentCount!(0)|

    contract increase(ack) = {
        for (old <- currentCount) {
            currentCount!(*old + 1)|
            ack!(*old)
        }
    }|

    contract reset(ack) = {
        for (old <- currentCount) {
            currentCount!(0)|
            ack!(*old)
        }
    }|

    new ack in {
        increase!(*ack)|
        for (_ <- ack) {
            increase!(*ack)|
            for (_ <- ack) {
                increase!(*ack)|
                for (_ <- ack) {
                    increase!(*ack)|
                    for (_ <- ack; count <- currentCount) {
                        stdout!(*count)
                    }
                } 
            }
        }
    }
}

#3:设置初始值

 

#5-10:触发后,计数器+1

 

#12-17:触发后,计数器重置

 

#19-26:对合约的调用

用简单的Contract方法实现   

关于Contract Factory

new counterFactory, stdout(`rho:io:stdout`) in {

    contract counterFactory(increase, reset) = {
        new currentCount in {
            currentCount!(0)|

            contract increase(ack) = {
                for (old <- currentCount) {
                    currentCount!(*old + 1)|
                    ack!(*old)|
                    stdout!(*old)
                }
            }|

            contract reset(ack) = {
                for(old <- currentCount){
                currentCount!(0)|
                ack!(*old)
                }
            }
        }
    }|

    new ack, AliceIncrease, AliceReset in {
        counterFactory!(*AliceIncrease,*AliceReset)|
        AliceIncrease!(*ack)|
        // AliceIncrease!(*ack)|
        // AliceIncrease!(*ack)|
        AliceIncrease!(*ack)
    }|

    new ack, BobIncrease, BobReset in {
        counterFactory!(*BobIncrease,*BobReset)|
        BobIncrease!(*ack)|
        // BobIncrease!(*ack)|
        // BobIncrease!(*ack)|
        BobIncrease!(*ack)
    }
}

#3:counterfactory包含的两个功能:增加、重置

 

#7-13:触发后,计数器+1

 

#15-22:触发后,计数器重置

 

#25-27:Alice对counterfactory的调用,必须调用过#3后,才能调用#7/#15

 

#32-38:Bob对counterfactory的调用,必须调用过#3后,才能调用#7/#15

用Contract Factory的方法实现   

例子1:计数器

关于Contract Factory

两种实现方法有什么区别?

通过使用Contract Factory,把计数器增加和重置的功能定义好、封装,不同的合约调用者(Alice与Bob)可以各自独立地调用任意功能。

例子1:计数器

关于Contract Factory

传入通道和传出通道

传入通道和传出通道的本质其实是通过Unforgeable Name的传递来完成对相应合约的触发。

“

”

new factory in {

    contract factory(method1, method2) = {
        contract method1(ack) = {
            Nil
        }|
        contract method2(ack) = {
            Nil
        }
    }|

    new a, b in {
        factory!(*a, *b)|
        a!(1)|
        b!("Hello")
    }
}

传入通道中Unforgeable Name的传递

#3:method1和method2被bound在contract factory里面,没有Unforgeable Name

 

#12/13:method1被赋值为a的Unforgeable Name,method2被赋值为b的Unforgeable Name

 

#14/15:把某个Process发送给a,就相当于把

它发送给method1,于是contract method1 被触发,contact method2的触发同理。

传入通道和传出通道

new factory in {
    contract factory(x, y) = {
        new method1, method2 in {
            x!(*method1)|
            y!(*method2)|
            contract method1(ack) = {
            Nil
            }|
            contract method2(ack) = {
            Nil
            }
        }        
    }|
    new a, b in {
        factory!(*a, *b)|
        for (ack1 <- a) {
            ack1!(1)
        }|
        for (ack2 <- b) {
            ack2!("Hello")
        }
    }
}

#2/14/15:x被赋值为a的Unforgeable Name,y被赋值为b的Unforgeable Name(传入通道)

 

#4/5:将method1、method2的Unforgeable Name 发送给x、y

 

#16-20:从a和b中取出method1和method2的Unforgeable Name,然后把某个Process发送给这两个Unforgeable Name,即可触发相应的contract

传出通道中Unforgeable Name的传递

传入通道和传出通道

在上一段代码中,method1的Unforgeable Name 是怎么传递的?

unforgeable name

x

a

ack1

传入通道和传出通道

机场控制台把现有的天气和跑道信息存在一个Channel里,然后设置一个控制器,它能够在必要的时候对原始信息进行修改,而飞行员可以查询到相关信息。 

例子2:机场控制台

传入通道和传出通道

传入通道

new stationFactory, stdout(`rho:io:stdout`) in {
    contract stationFactory(initialMessage, getInfo, setInfo) = {
        new currentMessage in {
            currentMessage!(*initialMessage)|
            contract setInfo(newMessage) = {
                for (msg <- currentMessage) {
                    currentMessage!(*newMessage)
                }
            }|
            contract getInfo(return) = {
                for (msg <- currentMessage) {
                    return!(*msg)|
                    currentMessage!(*msg)
                }
            }
        }
    }|
    new airportInfo, set in {
        stationFactory!("Weather is nice", *airportInfo, *set)|
        set!("Strong crosswinds, be advised")|
        airportInfo!(*stdout)
    }   
}

例子2:机场控制台

传入通道和传出通道

设置一个银行账户,可存、可取、可查询。需要注意的是,账户必须保证安全,除了账户所有人之外,其他人不能知道里面的余额,更不能有权限把钱拿出来。

例子3:银行账户

传入通道和传出通道

new openAccount, stdout(`rho:io:stdout`) in {
    // This contract registers a new account and creates its methods.
    contract openAccount(initialDeposit, deposit, withdraw, check) = {
        new balanceCh in {
            balanceCh!(*initialDeposit)|
            // Deposit Contract
            contract deposit(amount, ack1) = {
                for (old <- balanceCh) {
                    balanceCh!(*old + *amount)|
                    ack1!(Nil)
                }
            }|
            // Withdraw Contract
            contract withdraw(amount, ack2) = {
                for (old <- balanceCh) {
                    balanceCh!(*old - *amount)|
                    ack2!(Nil)
                }
            }|
            // Check contract
            contract check(ack3) = {
                for (balance <- balanceCh) {
                    ack3!(*balance)
                }
            }
        }
    }|
    // Customer Sarah creates an uses an account
    new sarahDeposit, sarahWithdraw, sarahCheck, sarahack in {
        openAccount!(10, *sarahDeposit, *sarahWithdraw, *sarahCheck)|
        sarahWithdraw!(3, *sarahack)|
        for (_ <- sarahack) {
            sarahCheck!(*stdout)
        }
    }
}

#3/#29-30:不同的人在调用主智能合约时,都可以通过新定义属于自己的传入通道来实现账户的安全性

 

#14/#31:触发withdraw合约,实现账户更新

 

#10/#17/#32:ack是一个确认channel,作用是:保证在调用check合约时,查询到的是最新的balance。

例子3:银行账户

传入通道和传出通道

 stephanie!(*sarahDeposit)

如果 Sarah想要授权给她的朋友Stephanie向她自己的账户存款的权限(不开放取款和查询余额的功能),她应该执行什么代码?

把Sarah的存款通道传出给Stephanie

例子3:银行账户

传入通道和传出通道

补充知识点——关于ack

 

1. ack,acknowledgement 的缩写,是Rholang中常用的、用于确认的Channel

 

2. 当ack 里面被放入Process时,用来证明:一次相对应的Send 和 Receive(即一次COMM event )已经完成。

 

3. ack 也可以当做普通的Name来使用

传入通道和传出通道

new pizzaShop, stdout(`rho:io:stdout`) in {
    contract pizzaShop(order, ack) = {
        ack!("Order Received.")
    }|
    new alice in {
        pizzaShop!("One medium veggie pizza", *alice)|
        for (@info<-alice) {
            stdout!(info)
        }
    }
}

例子4:披萨店订单确认

Alice订购披萨,披萨店在收到订单后向Alice专属的确认Channel内返回“订单已收到”的信息

#2/#6:定义Alice专属的确认Channel,把它的Unforgeable Name 和订单一起传入 contract

pizzashop

 

#3:订单收到,向确认Channel放入消息

确认Channel——ack

传入通道和传出通道

确认channel——ack

用作确认channel时,ack 还有一个功能:在Rholang这个以并行为基本逻辑的语言中,实现顺序执行。

传入通道和传出通道

确认channel——ack

例子5:有顺序的发送

new chan, ack, stdoutAck(`rho:io:stdoutAck`) in {
    chan!(0)|
    for (_ <- ack) {
        chan!(1)
    }|
    for (@num <= chan) {
        stdoutAck!(num, *ack)
    }
}

#3-#4:只有把值从ack里取出来,才能触发向chan发送1

 

#7:stdoutAck的功能是把num收到的值输出到屏幕上,同时在ack里面放入Nil,相当于:stdout!(num) | ack!(Nil)

传入通道和传出通道

在第二章里,通过Join Operator实现了两个飞行员共同控制火箭的发射。现在,为该情景添加如下功能:

两个人都有权利撤回发射,将命令从“发射”更改到“取消发射”。

例子6:Rocket Launch 升级版

传入通道和传出通道

new rocketFactory, stdout(`rho:io:stdout`) in {

    contract rocketFactory(aliceLaunch, bobLaunch, ackCh) = {
        new aliceReadyCh, bobReadyCh in {

            contract aliceLaunch(ackAlice) = {
                aliceReadyCh!(true)|
                new aliceAbort in {
                    contract aliceAbort(_) = {
                        for (_ <- aliceReadyCh) {
                                stdout!("Abort")
                        }
                    }|
                    ackAlice!(*aliceAbort)
                }
            }|

            contract bobLaunch(ackBob) = {
                bobReadyCh!(true)|
                new bobAbort in {
                    contract bobAbort(_) = {
                        for (_ <- bobReadyCh) {
                            stdout!("Abort")
                        }
                    }|
                    ackBob!(*bobAbort)
                }
            }|

            for (a <- aliceReadyCh; b <- bobReadyCh) {
                stdout!("Launch!")|
                ackCh!("Launch!")    
            }
        }
    }|
    // 发射
    new aliceLaunch, bobLaunch, ackCh, ackAlice, ackBob in {
        rocketFactory!(*aliceLaunch, *bobLaunch, *ackCh)|
        aliceLaunch!(*ackAlice)|
        bobLaunch!(*ackBob)
    }|
    // Alice取消发射
    new aliceLaunch, bobLaunch, ackCh, ackAlice, ackBob in {
        rocketFactory!(*aliceLaunch, *bobLaunch, *ackCh)|
        aliceLaunch!(*ackAlice)|
        for (x <- ackAlice) {
            x!(Nil)
        }
    }
}

传出通道的使用:#6第一次被触发后,aliceAbort的Unforgeable Name被传出到了ackAlice中,所以,再次触发#6时,相当于向aliceAbort里发送Process,触发contract aliceAbort ,完成“取消发射”的功能

 

例子6:Rocket Launch 升级版

传入通道和传出通道

More Cases

例子7:取消/停止触发

new MakeRevokableForwarder in {
    contract MakeRevokableForwarder(target, ret) = {
        new port, kill, forwardFlag in {
            ret!(*port, *kill)|
            forwardFlag!(true)|
            contract port(msg) = {
                for (@status <- forwardFlag) {
                    forwardFlag!(status)|
                    match status { true => target!(*msg) }
                }
            }|
            for (_ <- kill; _ <- forwardFlag) {
                forwardFlag!(false)
            }
        }
    }|
    new target, ret in {
        MakeRevokableForwarder!(*target,*ret)|
        for (port, kill<- ret) {
            port!(111)
        // |kill!(000)
        }
    }
}

传入通道的使用:#17中target和ret的Unforgeable Name 被传入#2

 

传出通道的使用:#3中port和kill的Unforgeable Name通过ret被传出,所以#20中向port发送Process即可触发#6的contract

 

触发port的结果:被放入port的msg最后被放入到了target;触发kill的结果,forwardFlag里面被放入false,游戏结束。

例子8:存衣处

new MakeCoatCheck in {
    contract MakeCoatCheck(ret) = {
        new port, table in {
            ret!(*port)|
            for (@"new", @arg, ack <= port) {  // 传出通道的使用:port的Unforgeable Name被传出到了ret,#28中又把它取出,之后向cc发送Process即触发相应的合约(#5/11/17)
                new ticket in {   // 定义一个ticket,代表存衣凭证。它的Unforgeable Name被传出到ack中
                    ack!(*ticket)|
                    @{*ticket | *table}!(arg)
                }
            }|
            for (@"get", @arg, ack <= port) {   // 查看功能。@"get"用于匹配,@arg代表凭证,ack是返回通道
                for (@value <- @{arg | *table}) {
                    @{arg | *table}!(value)|
                    ack!(value)
                }
            }|
            for (@"set", @arg1, @arg2, ack <= port) {   // 更换存衣功能。@"set"用于匹配,arg1代表凭证,arg2代表新的衣服,ack是返回通道
                for (_ <- @{arg1 | *table}) {
                    @{arg1 | *table}!(arg2)|
                    ack!(true)
                }
            }
        }
    }|
    // Usage
    new ret, get, set in {
        MakeCoatCheck!(*ret)|  // 传入通道的使用:ret的Unforgeable Name被传入#2,两个ret等价
        for (cc <- ret) {
            // Creates new cell with initial value 0
            cc!("new", 0, *ret)|
            for (ticket <- ret) {
                contract get(return) = { 
                    cc!("get", *ticket, *return) 
                }|
                contract set(@value, return) = { 
                    cc!("set", *ticket, value, *return) 
                }|        
                get!(*ret)|
                for (@r <- ret) {
                    //r is equal to 0
                    set!(1, *ret)|
                    for (_ <- ret) {
                        get!(*ret)|
                        for (@r <- ret) {
                            //r is equal to 1
                            Nil
                        }
                    }
                }
            }
        }
    }
}

More Cases

数据结构

06

String

 

1. String是一种组织字符的数据结构,如"hello"

2. String之间用++ 连接

new string, stdout(`rho:io:stdout`) in{
    contract string (@word) = {
        stdout!("Hello" ++ word)
    }|
    string!("World")
}

Tuple

 

1. Tuple是有顺序的元素集。

2. Tuple! (3, 4, 5),Tuple里面有三个元素;Tuple! ((3, 4, 5)) ,Tuple里面只有一个元素。

new tCh, stdout (`rho:io:stdout`) in {
    tCh!!((3, 4, 5))|
    for (@t <- tCh){
        stdout!(["Test nth. Expected: 5. Got ", t.nth(2)])
    }|
    for(@t <-  tCh){
        stdout!(["toByteArray test.Got:",t.toByteArray()])
  }
}

(Tuple name).nth(位置)取出tuple中的元素。

例子1: 对Tuple的调用

Tuple的基础知识

new a,stdout (`rho:io:stdout`) in{
    contract a (x,y,z) ={
        stdout!("HelloWorld")
    }|
    a!(1,2,3)|
    a!(1)
}

在调用时必须向a send由三个元素组成的Tuple。

例子1: contract定义的name是Tuple

Tuple在contract语句中的应用

Tuple

 

建立在Tuple结构上的name,称为compound name。

new alice, bob, key1, key2, stdout(`rho:io:stdout`) in {

    alice!(*key1)|
    bob!(*key2)|

    contract @(*key1, *key2)(_) = {
        stdout!("Congratulations, Alice and Bob, you've cooperated.")
    }|
    for(@a<-alice;@b<-bob){
        @(a,b)!(1)
    }
}

当alice和bob同时用自己的key调用contract的时候,contract才能被执行。

Tuple在contract语句中的应用

例子2:联合

Tuple

P Output
stdout!(t.nth(2)) Nil
stdout!(t.toByteArray()) 2a19aa01160a042a0210020a042a0210040a000a062a041a024869

Tuple调用方法一览

Tuple ! ((1,2,Nil,"Hi"))|for(@t<-Tuple){P}

Tuple

List

 

List是一个有顺序的数组。

new lCh, stdout(`rho:io:stdout`) in {

    lCh!!([3, 4, 5])|

    for (@l <- lCh){
        stdout!({l.nth(2)})
    }|
    for (@l <- lCh){
        stdout!(["Test slice. Expected: [4, 5]. Got: ", l.slice(1, 3)])
    }|

    for (@l <- lCh){
        stdout!({l.length()})
    }
}

例子:对List的调用

List的基础知识

P Output
stdout!({l.nth(2)}) Nil
stdout!({l.length()}) 4
stdout!({l.slice(1,3)}) [2,Nil]
stdout!({l.toByteArray()}) 2a19a201160a042a0210020a042a0210040a000a062a041a024869

List调用方法一览

List ! ([1,2,Nil,"Hi"])|for(@l<-List){P}

List

Set

Set是一个无序的process的合集,其中每一个元素都是不可重复的。

new sCh, stdout(`rho:io:stdout`) in {
    sCh!!(Set(3, 4, 5))|
    // 在Set里面加一个process
    for (@s <- sCh){
        stdout!(["Test add. Expected Set(3, 4, 5, 6), Got: ", s.add(6)])
    }|
    // 删除Set里面的一个process
    for (@s <- sCh){
        stdout!(["Test delete. Expected: Set(3, 4). Got: ", s.delete(5)])
    }|
    for (@s <- sCh){
        stdout!(["Test contains. Expected: true. Got:", s.contains(5)])|
        stdout!(["Test contains. Expected: false. Got: ", s.contains(6)])
    }|
    for (@s <- sCh){
        stdout!(["Test union. Expected: Set(1, 2, 3, 4, 5). Got: ", s.union(Set(1, 2))])
    }|
    for (@s <- sCh){
        stdout!(["Test diff. Expected: Set(5) Got: ", s.diff(Set(3, 4))])
    }|
    for (@s <- sCh){
        stdout!(["Test toByteArray. Got: ", s.toByteArray()])
    }      
}

例子:对Set的调用

Set的基础知识

P Output
stdout!({s.union(Set(1,4))}) Set(1,2,4,Nil,"Hi")
stdout!({s.delete(2)}) Set(1,Nil,"Hi')
stdout!({s.contains(5)}) false
stdout!({s.size()}) 4
stdout!({s.toByteArray()}) 2a19b201160a000a042a0210020a042a0210040a062a041a024869

Set调用方法一览

Sets! (Set(1,2,Nil,"Hi"))|for(@s<-Sets){P}

Set

Map

Map的每一个元素都由key和value组成,每个key都不能重复。表现形式为{"a":1,"b":2}。

例子:对Map的调用

new mCh, print(`rho:io:stdout`) in {
    mCh!!({"a": 3, "b": 4, "c": 5})|
    // .union 用来将两个map联合起来
    for (@m <- mCh){
        print!(["Test union. Expected: {a: 3, b: 4, c: 5, d: 6}. Got: ", m.union({"d": 6})])
    }|
    // .diff用来调出map中除了.diff()里面的东西。
    for (@m <- mCh){
        print!(["Test diff. Expected: {b: 4, c: 5}. Got: ", m.diff({"a": 3})])
    }|
    for (@m <- mCh){
        print!(["Test toByteArray. Got: ", m.toByteArray()])
    }|
    for (@m <- mCh){
        print!(["Test delete. Expected: {a: 3, b: 4}. Got: ", m.delete("c")])
    }|
    for (@m <- mCh){
        print!(["Test contains. Expected: true. Got: ", m.contains("c")])|
        print!(["Test contains. Expected: false. Got: ", m.contains("d")])
    }|
    for (@m <- mCh){
        print!(["Test get. Expected: 4. Got: ", m.get("b")])
    }|
    for (@m <- mCh){ 
        //getOrElse的第二个参数是Default Value
        print!(["getOrElseSuccessful. Expected: 4. Got: ", m.getOrElse("b", "?")])|
        print!(["getOrElseFailed. Expected: ?. Got: ", m.getOrElse("d", "?")])
    }|
    for (@m <- mCh){
        print!(["Test set. Expected {a: 3, b: 2, c: 5}. Got: ", m.set("b", 2)])
    }|
    for (@m <- mCh){
        print!(["Test keys. Expected Set(a, b, c)). Got: ", m.keys()])
    }|
    for (@m <- mCh){
        print!(["Test size. Expected 3. Got: ", m.size()])
  }
}

Map的基础知识

P Output
​stdout!({m.union{{"c":3}}}) ​{"a":1,"b":2,"c":3}
stdout!({m.delete{"b"}}) {"a":1}
stdout({m.contains{"c"}}) false
stdout!({m.get{"b"}}) 2
stdout!({m.getOrElse{"d","fail"}}) fail
stdout!({m.set{"b",4}}) {"a":1,"b":4}
stdout!({m.keys()}) Set("a","b")
stdout!({m.size()}) 2
stdout!({m.toByteArray()}) 2a21ba011e0a0d0a052a031a016112042a0210020a0d0a052a031a016212042a021004

Map调用方法一览

Map ! ({"a":1,"b":2})|for(@m<-Map){P}

Map

调用方法 调用目的 Tuple List Map Set
nth 选取Tuple或list中顺序的某一位 ✓ ✓
toByteArray 将数据结构以16进制的方式输出 ✓ ✓ ✓ ✓
union 将两个数据结构合在一起 ✓ ✓
diff 调出数据结构中除diff()括号内以外的数据 ✓ ✓
add 在Set中添加一个process ✓
delete 删除key ✓ ✓
contains 检验map中是否包含某个key ✓ ✓
get 得到key的value ✓
getOrElse 如果map中没有这个key则输出Default Value ✓
set 更改key的value ✓
keys 调出map中所有key ✓
size map中key的数量 ✓ ✓
length 输出list中的元素位数 ✓
slice 截取list ✓

数据结构调用方法一览表

.toByteArray()

数据转换

Rholang里所有数据结构(比如String,Int,List,Map,Set……)都可以转换成字节数组

new stdout(`rho:io:stdout`),sCh,lCh, mCh in {
    stdout!(["String","a".toByteArray()])|
    stdout!(["Int",16.toByteArray()])|
    stdout!(["list",["abc", 100].toByteArray()])|
    stdout!(["Set",Set("Hello",1).toByteArray()])|
    stdout!(["Map",{"b":99,"bb":999,}.toByteArray()])
}

//运行结果:
//["Int", 2a021020]
//["list", 2a13a201100a072a051a036162630a052a0310c801]
//["Map", 2a24ba01210a0e0a052a031a016212052a0310c6010a0f0a062a041a02626212052a0310ce0f]
//["Set", 2a14b201110a042a0210020a092a071a0548656c6c6f]
//["String", 2a031a0161]

三种加密方法:

keccak256Hash, sha256Hash 和 blake2b256Hash

转换为字节数组后才能加密

数据加密

new stdout(`rho:io:stdout`), hashValueCh in {
    @"keccak256Hash"!("1111F8dwxuPdE95j4XVqusbo2dDvkiwgi7JCodF7UQXVFDoWGTrA9".toByteArray(), *hashValueCh)|
    for(@hashValue<-hashValueCh){
    	stdout!(hashValue)
    }
}
// 运行结果:7b892e7366cd0f57e846bba7d1f70d6a62acff466e57834b9f70bc08182834e4
new stdout(`rho:io:stdout`), hashValueCh in {
    @"sha256Hash"!("1111F8dwxuPdE95j4XVqusbo2dDvkiwgi7JCodF7UQXVFDoWGTrA9".toByteArray(), *hashValueCh)|
    for(@hashValue<-hashValueCh){
    	stdout!(hashValue)
    }
}
// 运行结果:ae3d1d0f4cb47251155e707270aa4958dde64278e2cc353937f222fd7345fb80
new stdout(`rho:io:stdout`), hashValueCh in {
    @"sha256Hash"!("1111F8dwxuPdE95j4XVqusbo2dDvkiwgi7JCodF7UQXVFDoWGTrA9".toByteArray(), *hashValueCh)|
    for(@hashValue<-hashValueCh){
    	stdout!(hashValue)
    }
}
// 运行结果:2f7a32d03369be70d0d1b8f5f42cbb3e241c037fd5b147a9e6f65bca37660ace

匹配

07

写在前面

在Rholang官方教程中,匹配都叫做"Pattern Matching"。

为了便于理解,在本章节中将"Pattern Matching"分为结构样式匹配和逻辑命题匹配。

结构样式匹配

Rholang里的结构样式

数据类型
Int : -1,12,345
Strings : "Hello World"
Lists : [1,2,"Hi"]
Booleans : true, false
Tuples : (1,2,3)
Sets : Set(a, 5, b)
Maps : {"a":1, "b":10, "c":100}
_
语句结构
send : a!(b)
receive : for(x<-a){P}
Par: P|Q
Nil

结构样式匹配

用match进行结构样式匹配

for (@x <- <channel>) {
    match x{
        pattern1 =>{P}
        pattern2 =>{Q}
    }
}
  1. P和X均为Process。
  2. X是match新定义的Process时,@X 被赋值为P。
  3. 按顺序从上到下依次匹配,匹配成功则停止。
  4. _可以匹配一切。通常我们它放在最后,用来表示以上都没有匹配。
match P{
  X =>{...}
  _ =>{...}
}

当向channel Send Process 符合pattern1时,执行P;符合pattern2时,执行Q。

new patternMatcher, stdout(`rho:io:stdout`) in {
    for (x <= patternMatcher) {
        match *x {
            Nil     => stdout!("Got the stopped process")
            "hello" => stdout!("Got the string hello")
            [x, y]  => stdout!("Got a two-element list")
            Int     => stdout!("Got an integer")
            _       => stdout!("Got something else")
        }
    }|
    patternMatcher!({for(@0 <- @0){0}})|
    patternMatcher!({@"world"!("hello")})|
    patternMatcher!({Nil|"hello"})|
    patternMatcher!({0|"hello"})|
    patternMatcher!(Nil)
}

match数据类型匹配

# 13   {Νil|"hello"}={"hello"}

结构样式匹配

用match进行结构样式匹配

new patternMatcher, stdout(`rho:io:stdout`) in {
    for (x <= patternMatcher) {
        match *x {
            Nil               => stdout!("Got the stopped process")
            {_!(_)}           => stdout!("Got a send")
            {for(@0 <- _){_}} => stdout!("Got a receive on @0")
            _                 => stdout!("Got something else")
        }
    }|
    patternMatcher!({for(@0 <- @0){0}}) |
    patternMatcher!({@"world"!("hello")}) |
    patternMatcher!({0|"hello"}) |
    patternMatcher!(Nil)
}

结构样式匹配

用match进行结构样式匹配

match语句结构匹配

结构样式匹配

用for进行结构样式匹配

new chan, stdout(`rho:io:stdout`) in {
    chan!(1,2,3) |
    for (@x, @y, @z <= chan) {
        stdout!(["three", x, y, z])
    } |

    chan!(7,8) |
    chan!([9, 10], 11) |
    for (@a, @b <= chan) {
        stdout!(["two", a, b])
    } |

    chan!((4,5,6)) |
    chan!(12 | 13) |
    for (@a <= chan) {
        stdout!(["one", a])
    } 
}

for里的数据类型匹配

  • 元素个数和数据类型必须同时匹配才能匹配成功
  • {P|Q}既是两个并行的Process,也是一个Process
for (name pattern <- channel) { process }

当向channel send的Process符合name pattern时,上述语句才会被触发

new chan,stdout(`rho:io:stdout`)in{
    for (_ <- chan) {stdout!("Hello")}|
    chan!(1|2)|

    for (x <- chan) {stdout!("World")}|
    chan!(1|2|3)|

    for (@{y|x} <- chan) {stdout!("Hi")}|
    chan!(333)|

    for (@{y|_} <- chan) {stdout!("Hi~")}|
    chan!(22|44|66)|

    for (@x,@y <- chan) { stdout!(x)}|
    chan!(5,10)|

    for (@x,_ <- chan) { stdout!(x)}|
    chan!(5,10)|

    for (@{x!(Q)} <- @"chan") { stdout!(Q)}|
    @"chan"!({@"a"!(999)})|

    for (@{for (@P <- z) { R }} <- @"chan"){stdout!(R)}|
    @"chan"!(for (x <- @"a") {666})

}

for语句结构匹配

结构样式匹配

用for进行结构样式匹配

# 2 # 5  匹配1个Process

# 8 # 11 匹配1个或1个以上并行的Process

# 14 #17 匹配2个Process

# 20 # 23 先进行语句结构匹配,然后赋值

new chan,stdout(`rho:io:stdout`)in{

    for (@{y|Nil} <- chan) {stdout!(y)}|
    chan!(Nil|333)|

    for (@{for (@5 <- x) {Nil}} <- chan) { stdout!("Hi")}|
    chan!(for (@5 <- chan) {Nil})|

    for(@{x!("Hello")} <- chan){ stdout!("World")}|
    chan!({@"a"!("Hello")})

}

name pattern含有String,Int,触发语句需要与其样式完全一致

for语句结构及数据类型匹配

结构样式匹配

用for进行结构样式匹配

new chan,stdout(`rho:io:stdout`) in{

    contract @(*chan,"method1")(@ack)={stdout!(["method1",ack])}|
    @(*chan,"method1")!(1)|

    contract @[*chan,"method2"](@ack)={stdout!(["method2",ack])}|
    @[*chan,"method2"]!(2)|

    contract chan(@"method3",@ack)={stdout!(["method3",ack])}|
    chan!("method3",3)|

    contract @(*chan,"method4")(@"method4",@ack)={stdout!(["method4",ack])}|
    @(*chan,"method4")!("method4",4)

}

结构样式匹配

用contract进行结构样式匹配

contract里的结构样式匹配

  • # 3 channel pattern是一个Tuple,Tuple第一个元素为chan的Unforgeable name,第二个元素为"method1"
  • # 6 channel pattern是一个List,List第一个元素为chan的Unforgeable name,第二个元素为"method2"
  • # 9 name pattern含有两个变量,第一个是@"method3" 
contract channel pattern (name pattern) ={ Process }

当channel pattern 和name pattern的都匹配上时,上述语句被触发

new log(`rho:io:stdout`), missile in {
    
    contract @(*missile, "launch")(_) = {
        log!("launching...")
    }|

    contract @(*missile, "inspect")(_) = {
        log!("inspecting...")
    }|

    contract @"getInspectionChannel"(return) = {
        return!((*missile, "inspect"))
    }
    
}
//请补充调用合约,分别实现launch功能,和inspect功能
//|{...}
  • #3 #7 两个contract的channel pattern都是Tuple,Tuple第一个元素为missile的Unforgeable name,第二个元素分别为"launch"和"inspect"
  • #11-#13 @"getInspectionChennel"是一区块链上的公共name,在#1-#13之外,任何人都能对它进行操作

拿到@(*missile,"inspect")的Unforgeable name的人能触发contract @(*missile,"launch")吗?

如何做到?

结构样式匹配

用contract进行结构样式匹配

火箭发射和监控

new log(`rho:io:stdout`), missile in {
    contract @(*missile, "launch")(_) = {
        log!("launching...")
    }|

    contract @(*missile, "inspect")(_) = {
        log!("inspecting...")
    }|

    contract @"getInspectionChannel"(return) = {
        return!((*missile, "inspect"))
    }
}
|
new ack in {
    @"getInspectionChannel"!(*ack)|
    for(@(missile, "inspect") <- ack){
        @(missile, "launch")!(Nil)
    }
}

#17 通过for匹配,拿到missile的Unforgeable name,进而触发contract @(*missile,"launch")

结构样式匹配

用contract进行结构样式匹配

窃取火箭发射权

new getInspectionChannel, log(`rho:io:stdout`), missile in {

  contract @(*missile, "launch")(_) = {
    log!("launching...")
  }
  |
  contract @(*missile, "inspect")(_) = {
    log!("inspecting...")
  }
  |
  contract getInspectionChannel(return) = {
    return!(bundle+{(*missile, "inspect")})
  }
}

#12 bundle+后的(*missile,“inspect”),只能写不能读。

(可以向@(*missile,“inspect”) send process,不能从@(*missile,“inspect”) receive process。详见第九章第一节)

结构样式匹配

用contract进行结构样式匹配

更安全的火箭发射

逻辑命题 判断
a>b a大于b时该命题为真
a < b a小于b时该命题为真
a == b  a等于b时该命题为真
a <= b a小于等于b时该命题为真
a != b a不等于b时该命题为真
a and b, a/\b 当a和b都为真时,该命题为真
a or b, a\/b   当a或b为真时,该命题为真
not a 当a为假时,该命题为真

以上a,b均为Process

逻辑命题匹配

Rholang里的逻辑命题

new stdout(`rho:io:stdout`) in {
    for (@balance <= @"signTest") {
        if (balance > 0) {
            stdout!("Account in good standing.")
        } 
        else {
            stdout!("Account overdrawn.")
        }
    }
}
|
@"signTest"!(5)

筛选大于0的balance

逻辑命题匹配

if/else逻辑命题匹配

if (cond) {P}
else {Q}

Process cond为真命题时执行P,为假命题时执行Q

match cond {
  true => P
  false => Q
}
  1. Process cond与booleans进行匹配。cond为真命题执行P,cond为假命题执行Q
  2. cond里只能包含关系运算符,不能包含逻辑运算符

逻辑命题匹配

match逻辑命题匹配

match x {
  cond => P
  _ => Q
}

方法一

方法二

  1. ​x与cond命题等价时,执行P;否则执行Q

if/else是match的语法糖

算术运算:+, -, *, /

关系运算:>, >=, <, <=, ==, !=

逻辑运算符:and, or, not

new agech,stdout(`rho:io:stdout`) in{
    agech!(10)|
    agech!(18)|
    agech!(20)|
    for(@age<=agech){
        match {age>=18} {
            true =>{stdout!("permission")}
            _    =>{stdout!("rejection")}
        }
    }
}

逻辑命题匹配

match逻辑命题匹配

筛选出年龄大于等于18的人

试试用方法二改写上述代码

参考More Cases

new log(`rho:io:stdout`), binderChecker in {
    contract binderChecker(@data, return) = {
        match data {
            "nombre" \/ "name" => return!("name-like")
            _ => return!("not name-like")
        }
    }|
    binderChecker!("name", *log)|
    binderChecker!("nombre", *log)
}

逻辑命题匹配

match逻辑命题匹配

匹配多种字符串

逻辑命题匹配

for逻辑命题匹配

new StudentGradeLevel, stdout(`rho:io:stdout`) in {
    for(@{{@"grade"!(x)}/\{@y!(10)}} <- StudentGradeLevel){stdout!("a")}|
    StudentGradeLevel!(@"grade"!(10))
}

用for模拟数据库处理

for (name pattern <- channel) { Process }
  1. 当name pattern是一个逻辑命题时,触发语句必须向channel Send一个真命题,上述Process才会被触发。
  2. 目前name pattern里的逻辑连接符只能使用and(/\)
  3. name pattern不能包含关系运算符。
new print(`rho:io:stdout`), register in {

    for (@{{@"name"!(_) | _} /\ {@"age"!(_) | _}} <= register){
        print!("Both name and age were in the data")
    }|
    register!(@"name"!(Nil))|
    register!(@"age"!(Nil))|
    register!(@"name"!(Nil) | @"age"!(Nil))|
    register!(@"name"!(Nil) | @"age"!(Nil) | @"height"!(175))
    
}

筛选出数据库中含有@"name" 和@"age"的数据

x被赋值为{@"age"!(_) | y}

逻辑命题匹配

for逻辑命题匹配

{@"name"!(_) | x} /\ {@"age"!(_) | y}

{x/\{@"age"!(_) | y}}/\{y/\{@"name"!(_) | x}}

=

y被赋值为

{@"name"!(_) | x}

{@"name"!(_)|@"age"!(_) |y}/\{@"age"!(_) |@"name"!(_)|x}

代入

{@"name"!(_)|@"age"!(_) |x}

逻辑命题匹配

contract逻辑命题匹配

new StudentGradeLevel, x, stdout(`rho:io:stdout`) in{
    contract StudentGradeLevel (@{{@"grade"!(x)}/\{@y!(10)}}) ={stdout!("a")}|
    StudentGradeLevel!(@"grade"!(10))
}

用contract进行数据库处理

contract channel  (name pattern) ={ Process }
  1. 当name pattern是一个逻辑命题时,触发语句必须向channel send一个真命题,上述Process才会被触发
  2. 目前name pattern里的逻辑命题只能使用and(/\)

​以下样式只能用来做匹配,不能作为Name使用。

  • _  =>  匹配所有Process
  • Int =>  匹配所有整数,包括整数和负数
  • Bool =>  匹配true 和 false
  • String =>  匹配字符串
  • Uri=>  匹配智能合约的ID
  • ByteArray=>  匹配所有数据类型的ByteArray

如果要输出1,怎么办?

More Cases

如何进行匹配并赋值

匹配整形

new ack, stdout(`rho:io:stdout`) in {
    for (@Int <-ack){
        stdout!(Int)
    }|
    ack!(1)
}
//结果为Nil
new ack, stdout(`rho:io:stdout`) in {
    for (@{x /\ Int} <- ack) {
        stdout!(x)
    }|
    ack!(1)
}
//结果是1
  • x/\Int与Int等价,x被赋值为与Int进行匹配的Process
  • 首先1与Int匹配,然后1被赋值给x

More Cases

如何进行匹配并赋值

匹配整形并输出

​以下样式只能用来做匹配,不能作为name使用。

  • _  =>  匹配所有Process
  • Int =>  匹配所有整数,包括整数和负数
  • Bool =>  匹配true 和 false
  • String =>  匹配字符串
  • Uri=>  匹配智能合约的ID
  • ByteArray=>  匹配所有数据类型的ByteArray

如果要输出uriChan,怎么办?

More Cases

如何进行匹配并赋值

匹配智能合约的ID

new chan, uriChan,insertArbitrary(`rho:registry:insertArbitrary`),stdout(`rho:io:stdout`) in{
    insertArbitrary!(bundle+{*chan}, *uriChan)|
    for(@{Uri}<-uriChan){
            stdout!("success")
    }  
}
new chan,stdout(`rho:io:stdout`) in{
    chan!(`rho:id:x93d1g1oaj83pn3rgntehae7b3cnxzpktwojrijbg9uk7z8b3hkbw5`)|
    for(@{Uri}<-chan){
        stdout!("success")
    }
}

​以下样式只能用来做匹配,不能作为name使用。

  • _  =>  匹配所有Process
  • Int =>  匹配所有整数,包括整数和负数
  • Bool =>  匹配true 和 false
  • String =>  匹配字符串
  • Uri=>  匹配智能合约的ID
  • ByteArray=>  匹配所有数据类型的ByteArray

如果要输出ByteArray值,怎么办?

More Cases

如何进行匹配并赋值

匹配所有数据类型的ByteArray

new chan, pm, stdout(`rho:io:stdout`) in{
    chan!(123)|
    for(@chan_<-chan){
        pm!(chan_.toByteArray())|
        for(@{ByteArray}<-pm){
            stdout!("success")
        }
    }  
}
new age, stdout(`rho:io:stdout`) in {
    for(@{x+5}<- age ){ 
        stdout!(x)
    }|
    age!({10+5})
} 

#5 10+5比{ }优先级高,age 里的Process为15,与{x+5}无法匹配

More Cases

无法匹配的样式

for里的算术运算

new age, stdout(`rho:io:stdout`) in {
    for(@{x>5}<- age ){ 
        stdout!(x)
    }|
    age!({10>5})
} 

#5 10>5比{ }优先级高,age 里的Process为true,与{x>5}无法匹配

for里的关系运算

More Cases

无法匹配的样式

new age, stdout(`rho:io:stdout`) in {
    for(@x<- age ){ 
        match x{
            x>5 => stdout!("123")
        }
    }|
    age!(10)
} 

#4 x是新定义的Name,与#3的x不同

match里的关系运算

More Cases

无法匹配的样式

new age, stdout(`rho:io:stdout`) in {
    for (@x <- age) { 
        match x/\10 {
            true => stdout!(x)
        }
    }|
    age!(10)
}

#3 报错:不能使用/\

PS: 也不能使用\/

match里的逻辑运算

More Cases

无法匹配的样式

new helloNameAge, getOlder, stdout(`rho:io:stdout`) in {
    contract helloNameAge(@{@"name"!(name) | @"age"!(age) | _}) = {
        stdout!(["Hello, ", name, " aged ", age])
    } |
    contract getOlder(@{rest /\ {@"name"!(_) | _} | @"age"!(age) }, ret) = {
        ret!(@"age"!(age + 1) | rest)
    } |
    getOlder!(@"name"!("Joe") | @"age"!(39), *helloNameAge)
}

例子1: 将数据记录里的年龄+1

  • 本案例包含结构样式匹配,逻辑命题匹配
  • # 5 是contract的name pattern 匹配,内部包含样式匹配和逻辑命题匹配
  • # 2  是contract的name pattern匹配

More Cases

contract里的结构样式&逻辑命题匹配

例子2: 筛选出age>35岁的人的身份数据

new people, stdout(`rho:io:stdout`) in {
    people!(@"name"!("Joe") | @"age"!(20) | @"eyes"!("blue") | @"seq"!(0)) |
    people!(@"name"!("Julie") | @"age"!(30) | @"eyes"!("brown") | @"seq"!(0)) |
    people!(@"name"!("Jane") | @"age"!(40) | @"eyes"!("green") | @"seq"!(0)) |
    people!(@"name"!("Jack") | @"age"!(50) | @"eyes"!("grey") | @"seq"!(0))|
    for (@{@"seq"!(0) | {row /\ {@"name"!(name) | @"age"!(age) | _}}} <= people) {
        if (age > 35) {
            stdout!([name, age])
        }|
        people!(row | @"seq"!(1))
    }
}

More Cases

for里的结构样式&逻辑命题匹配

  • #10 信息被放回数据库,并且@“seq”里的值变为1,代表该条身份数据已被读取,且不能再次与#6匹配,避免重复筛选。
  • 本案例包含样式匹配,逻辑命题匹配及赋值

迭代

08

迭代指智能合约迭代的过程,即反复地运用同一合约计算,前一次迭代得到的结果作为下一次迭代的输入。

迭代

new myIncrease, stdout(`rho:io:stdout`) in { 
    new current in {
        current!(0)|
        contract myIncrease(ack) = {
            for (@old <- current) {
                current!(old + 1)|
                ack!(old)
            }
        }
    }|
    new ack in {
        myIncrease!(*ack)|
        contract ack(@a) = {
            if (a <= 5) {
                myIncrease!(*ack)|
                stdout!(a)
            }
        } 
    }
}

例子1:计数器

设置一个可以计数到5,并带有重置功能的计数器。

#4-9 是实现计数功能的智能合约。利用迭代,使#6产生的结果在合约再次被触发后,作为输入条件被#5执行。

#12-18 是触发合约,同样利用迭代,不断触发myIncrease执行,直到不再满足条件后程序停止。

More Cases

new sum, add, stdout(`rho:io:stdout`) in {
    contract sum(@arr, ret) = {
        match arr {
            [h, ...t] => {
                new ack in {
                    sum!(t, *ack)|
                    for (@arg <- ack) {
                        add!(h, arg, *ret)
                    }    
                }
            }            
            _ => ret!(0)
        } 
    }|
    contract add(@a, @arg, ret) = {
        ret!(a + arg)
    }|
    sum!([4,5,6], *stdout) 
}

例子2:计算数组[4,5,6]中各数字的加和

#4 [h, ...t]是一个数组结构,h表示数组中第一位元素,t表示除h之外的其他元素。[h, ...t]用来将数组拆散。

 

#5-6 将拆出的 t 再次放入sum合约中迭代。注意,每次迭代产生的ack都是新的,用来保证之后的计算按顺序进行。

 

#7-8 触发#15 add合约,等待计算。

 

#12 数组为空后,ret中放入0。经过传递,最终0被放入arg中。

 

#16 ret被放入新的结果,作为输入,再次触发add合约...直到完成计算。

More Cases

Bundle

09

Bundle的作用是设置合约调用者的读写权限,共有四种语法:

 
Syntax Can Read Can Write
bundle- {proc} YES NO
bundle+ {proc} NO YES
bundle0 {proc} NO NO
bundle {proc} YES YES

Bundle

如何理解bundle

Bundle对合约调用者的限制,其原理类似于非对称密码学中公私钥的签名和加密。

非对称式密码学是密码学的一种算法,它需要两个密钥:

  • 公钥(公开给他人)
  • 私钥(不公开,只能由自己秘密保管)

用其中一个加密,用且只能用另一个解密。

虽然两个密钥在数学上相关,但如果知道了其中一个,并不能凭此计算出另外一个。

 

用法:

  • 用公钥对文件加密,只能由自己秘密保管的私钥进行解密,其他人无法读取,故称用公钥加密。
  • 用私钥对文件加密,由于私钥只自己持有,其他人无法更改经私钥加密的文件,效果等同于在纸本文件上亲笔签署的效果,所以该过程称为签名。

 

 

只写(bundle+)相当于用公钥加密

如何理解Bundle

只读(bundle-)就像公钥加密方法中的签名

如何理解bundle

区块链上的内容是全部可见的,bundle只能解决合约调用时读写权限问题,并不能解决隐私问题。

所以,在使用区块链时需要注意对敏感信息的处理,避免隐私泄露。

如何理解bundle

例子1:AliceFanMail

Eve想要偷取Alice的邮件,如何防止Eve作恶成功?

Bundle的用法

new alice, stdout(`rho:io:stdout`) in {
    new aliceFanMail in {
        alice!!(bundle+ {*aliceFanMail})|
        for (mail <= aliceFanMail) {
            stdout!("Alice received a fanmail")
        }
    }|
    for (aliceFanMail <- alice) {
        aliceFanMail!("Dear Alice, you're #TheBest")
    }|
    for (aliceFanMail <- alice) {
        for (@stolenMail <= aliceFanMail) {
            stdout!(["Eve stole a message: ", stolenMail])
        }
    }
}

#3 aliceFanMail被Bundle后传出

#8-10 粉丝接到aliceFanMail后给Alice发信

#11-15 Eve想要偷取Alice的信件,但没成功

示例2:Jackpot

玩家A扔出一个带有数字的球,其余玩家抢球,抢到球的玩家获得相应分数。如何防止除A以外的人扔球?

new gameCh, stdout(`rho:io:stdout`) in {
    new throw in {
        gameCh!!(bundle- {*throw})|
        throw!(4) |
        throw!(2) |
        throw!(6)
        }|
    for (throw <- gameCh) {
        for (points <= throw) {
            stdout!(["Bill caught it. Points: ", *points])
        }
    }|
    for (throw <- gameCh) {
        throw!(100)
    }
}

Bundle的用法

#2-7 throw被bundle后开始扔球

#8-12 玩家接球,并报出得分

#13-15 玩家想要作弊,扔出一个分数为100的球,但不成功

示例3:高阶版Jackpot

玩家扔出1-10共10个数字的球,由另外3个玩家抢球。抢到球的玩家将本轮得分与之前的得分相加并打印出来。

new gameCh, ack, stdout(`rho:io:stdout`) in {
    new ret in {
        contract gameCh(return) = {
            return!(bundle- {*ret})
        } |
        new throw in {
            throw!(1)|
            contract throw(@num) = {
                if (num <= 10){
                    throw!(num + 1)|
                    ret!(num) 
                }
            }
        }
    }|
    new initial in {
        new return in {
            gameCh!(*return)|
            initial!(0)|
            for (@throw <- return) {
                for (@points <= @throw; @ini <= initial) {
                    initial!(points + ini)|
                    stdout!(["P1 get the ball, the sum number is ", points + ini])          
                }           
            }
        } |
        new return in {
            gameCh!(*return)|
            initial!(0)|
            for (@throw <- return) {
                for (@points <= @throw; @ini <= initial) {
                    initial!(points + ini)|
                    stdout!(["P2 get the ball, the sum number is ", points + ini])          
                }           
            }
        } |
        new return in {
            gameCh!(*return)|
            initial!(0)|
            for (@throw <- return) {
                for (@points <= @throw; @ini <= initial) {
                    initial!(points + ini)|
                    stdout!(["P3 get the ball, the sum number is ", points + ini])      
                }           
            }
        } 
    }
}

#3-4 将ret bundle后传出去。

#6-14 通过迭代的方法实现扔球合约,将结果通过ret传出。

#16-26、#27-36、#37-46 是三个玩家接球并打印结果的合约。

More cases

Powerbox

10

输出

stderr(`rho:io:stderr`) , stdout(`rho:io:stdout`),

输出到屏幕

new stdout(`rho:io:stdout`), stderr(`rho:io:stderr`) in{
    stdout!("Hello")|
    stderr!("World")
}
new ackout, ackerr,stdout(`rho:io:stdout`),stderr(`rho:io:stderr`), 
    stdoutAck(`rho:io:stdoutAck`), stderrAck(`rho:io:stderrAck`) in {
    stdoutAck!("hello", *ackout) |
    for (@ackout_ <- ackout) {
        stdout!([ackout_, "received"])
    }|

    stderrAck!("world", *ackerr) |
    for (@ackerr_ <- ackerr) {
        stderr!([ackerr_, "received"])
    }
}

stdoutAck(`rho:io:stdoutAck`) , stdout(`rho:io:stdout`),

输出到屏幕,并向指定Channel Send Nil

获取Block时间

timestamp(`rho:block:timestamp`)

返回的时间为调用timestamp的智能合约propose成功的block time

new timestamp(`rho:block:timestamp`), stdout(`rho:io:stdout`), tCh in {
  timestamp!(*tCh) |
  for(@t <- tCh) {
    match t {
      Nil => { stdout!("no block time; no blocks yet? Not connected to Casper network?") }
      _ => { stdout!({"block time": t}) }
    }
  }
}
new alice, uriCh, stdout(`rho:io:stdout`), insertArbitrary(`rho:registry:insertArbitrary`) in {
    insertArbitrary!(*alice, *uriCh)|
    //insertArbitrary!(bundle+{*alice}, *uriCh)|
    //insertArbitrary!(bundle-{*alice}, *uriCh)|
    for(uri<-uriCh){
        stdout!(["alice",*uri])}
}
//运行结果:["alice", `rho:id:ohgpocjy48frt5cxtoqr8npp1zzy4odm3px67q9tw81ygc3ujm4kd1`]

获取URI

insertArbitrary(`rho:registry:insertArbitrary`)

给一个Unforgeable Name生成URI,拿到URI的人可以对这个Name进行操作。

#3 调用alice的人,只能写不能读

#4 调用alice的人,只能读不能写

new ack, lookup(`rho:registry:lookup`) in {
  lookup!(`rho:id:c8auch5r99u8ffcd3emzhoa9m99x7umrbf43y1jwc85faw8za9ajs9`, *ack)|
  for(uriUnforgeableName <- ack){
    uriUnforgeableName!(Nil)
  }
}

lookup(`rho:registry:lookup`)

通过链上智能合约里某个Unforgeable Name对应的URI 获得对这个Unforgeable Name的操作权限

向URI对应的Unforgeable Name Send 一个Process

new ack, lookup(`rho:registry:lookup`), stdout(`rho:io:stdout`) in {
  lookup!(`rho:id:c8auch5r99u8ffcd3emzhoa9m99x7umrbf43y1jwc85faw8za9ajs9`, *ack)|
  for(uriUnforgeableName <- ack){
    for(@process<-uriUnforgeableName){
        stdout!(process)
    }
  }
}

从URI对应的Unforgeable Name 中Receive一个Process

如果在生成URI时,对该Unforgeable Name进行的bundle-的权限控制,那么调用时,只能Receive,不能Send

通过URI 调用链上智能合约

如果在生成URI时,对该Unforgeable Name进行的bundle+的权限控制,那么调用时,只能Send,不能Receive

getDeployData(`rho:deploy:params)

new getDeployData(`rho:deploy:params`), ch, stdout(`rho:io:stdout`) in {
    getDeployData!(*ch)|
    for (_, _, @userId, _ <- ch) {
        stdout!(userId)
    }
}
//运行结果:048fb9524f276744aab7c765f2aa0b6e1fefdcaff0729b076459434c7f2fd9aa904b73cc711733780e5445c5748e013fdc99d97398ba14a9fa0dda85d3fb9690c9

功能

得到合约部署者的Public Key

用法

必须使用4个参数去receive  getDeployData里的值,即#3中for语句左边一定要使用4个变量

除了userId以外,从左到右四个变量以此是:computeCodeHash;phloPrice;timestamp

生成和验证RevAddress

RevAddress(`rho:rev:address`)

new RevAddress(`rho:rev:address`),revAddressValid,stdout(`rho:io:stdout`) in {
    RevAddress!("validate", "1111F8dwxuPdE95j4XVqusbo2dDvkiwgi7JCodF7UQXVFDoWGTrA9", *revAddressValid)|
    for (@value <- revAddressValid) {
        stdout!({"validate result":value})
    }
}
//运行结果:{"validate result" : Nil}
new RevAddress(`rho:rev:address`),revAddrCh,stdout(`rho:io:stdout`) in {
    RevAddress!("fromPublicKey",
       "0401d0fd5accd56de6d69c1bd4f56d8460485b19908c789085bb3515eac432f7dabc86ea4d43a166a2c49a4eb5882b4e72b01dad32e493fbe5cfe2150779235758"
        .hexToBytes(), *revAddrCh) |
    for (@revAddress <- revAddrCh) {
        stdout!({"revAddress":revAddress})
    }
}
//运行结果:{"revAddress" : "1111F8dwxuPdE95j4XVqusbo2dDvkiwgi7JCodF7UQXVFDoWGTrA9"}

"fromPublicKey"功能,传入publickey,返回对应的RevAddress

"validate"功能,传入RevAddress,如果RevAddress是存在的,则验证成功返回Nil ,否则不返回Process

About

感谢 Mike Stay 和 Joshy Orndorff的英文Rholang教程

 

本教程编辑团队来自 歪脑袋矿池(WeNode.io):

 

Rholang-China 招募RChain及Rholang的爱好者加入生态建设   https://rholang-china.org/

 

加RChain官方微信群

请加BlockChainForever001

拉你入群

 Dimworm

愁虫

Ziqi

Lango

Iris

今夕何年

 Nancy

柳菇凉

Made with Slides.com