< < <
Sending, Receiving, and other Fundamentals
Name Privacy
Contents
Tutorials / Learn Rholang

Learn Rholang

Learn rholang by example and experimentation. The examples and exercises are meant to be run, read, modified and completed. For best results, change anything and everything you are curious about. This tutorial is a tour of the most common and important features of rholang and a fast starting point for motivated learners.

Sending and Standard Out

Say Hello

"Person waiving hello"

There is a long-standing tradition in programming that your first program should say "Hello World". Here's the simplest rholang code to put that text on the screen.

new stdout('rho:io:stdout') in {
                stdout!("Hello World!")}

Exercise

Make the program print "Rholang rocks!" instead of "Hello World".

Exercise

Try to change "stdout" to something else. Any results?.

  • Try this funny channel name
    @"someChannel"
    .
  • We're casual here. Make the program say "Sup World" on the screen.

WTH is stdout?

Channels are like mailboxes for sending messages

The heart of rholang is communicating on channels. Channels are communication lines that you use to send and receive messages. To send a message on a channel, you use the ! character.

Redo this diagram!

stdout is a special channel that is used to send text to "standard out" which usually just means your computer screen. Because it's special, we had to have that first line of code.

Using other channels

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

You can actually send messages on lots of channels, not just stdout. But unlike stdout they won't display on the screen.

@"RandoChannel"!("This won't be on the screen")

So where do the other channels go then? Nowhere! Not yet anyway. The messages just sit there waiting for someone to receive or "consume" them. We'll learn how to consume messages in the next lesson. The place where messages sit in the meantime is called the "tuplespace".

Remember when you tried it a few slides ago? Try that again and make sure your message is sitting in the tuplespace.

You should find something like this highlighted text in your tuplespace.

Doing two things at once

Rather than following an ordered list, all ingredients are added concurrently.  Looks delicions

In rholang we don't tell the computer to do one thing, then another, then a third. Rather we tell it all the things to do, and it does them "concurrently," or all at once.

new stdout(`rho:io:stdout`) in {
  stdout!("I'm on the screen")
  |
  @"chan1"!("I'm in the tupplespace")
}

The | is pronounced "parallel", or "par" for short.

Exercise

Send the message "1 large pepperoni please" on a channel called "pizza shop".

Exercise

Send "Hi Mom" on the channel "Mom's Phone".

Exercise

Print two messages, "Rick" and "Morty", on the screen in one program.

Quiz

What will stdout!("Programming!") print to the screen?

  • Programming!
  • stdout!
  • Nothing

Correct!

Incorrect. Please try again.

What channel does @"what"!("Up") send a message on?

  • @"Up"
  • @"what"
  • what

Correct!

Incorrect. Please try again.

Which does rholang do first in

@"stdout"!("Dogs")
|
@"stdout"!("Cats")
  • prints "Dogs"
  • prints "Cats"
  • Neither. They are concurrent

Correct!

Incorrect. Please try again.

PS. There is also a special channel called stderr. Check out what happens when you send to it.

what's the difference?

Receiving

Check for Messages

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

We learned last time how to send a message. Now it's time to learn how to receive a message. The general syntax is:

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

BTW, lines that start with // are called comments. They're just there for human coders and don't affect the way the program runs at all. They're a good idea, and you should use them! Anyone who reads your code (including your future self) will appreciate them.

Comm Events

Pizza shop can receive messages on its channel.

The following code sends a message on a channel for a pizza shop and the pizza shop receives it. The pizza shop acknowledges receiving the message by printing to stdout.

new stdout(`rho:io:stdout`) in {
  @"pizzaShop"!("2 medium pies")
  |
  for(order 	<- @"pizzaShop"){
    stdout!("Order Received.")
  }
}

Exercise

Send that message to a different channel like @"coffeShop". Did the acknowledgement print? Is anything left in the tuplespace?

Let's hit up the coffee shop.

Exercise

Remember, in rholang things don't happen in order, they happen concurrently. The pizza shop code will work just as well if we put the receive first. Give it a try!

Tuplespace Pollution

If you're having trouble with old data sticking around and showing up later, you just need to clear your tuplespace. The easiest way to do that is to delete you data-directory which is usually called .rnode

Clearing out your tuplespace that way can get old quickly. A better idea would be to keep it from getting polluted in the first place. We can do that by modifying the top line that says new.

Rather than the old way

new stdout(`rho:io:stdout`) in {
  @"world"!("Welcome to RChain")
}

Try this

new world, stdout(`rho:io:stdout`) in {
  world!("Welcome to RChain") // No more @ or " "
}

We'll talk about how this works in our lesson on Unforgeable names. for now just enjoy not having to reset every time.

Receiving Before Sending

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.

When a send and a receive come together on a channel, it is called a communication event, or "comm event" for short.

Unlike normal mail where a message must be sent then received, the two can happen in either order or at the same time in rholang. It is just as acceptable to receive a message, then send it. Whenever a send and receive come together, a comm event takes place.

Contracts

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

Our pizza shop example illustrates comm events nicely, but it isn't very realistic to expect the pizza shop to manually issue a new receive every time an incoming order consumes theirs from the tuplespace.

Luckily it's possible to deploy code once, and have it run every time it receives a message. This kind of thing is called a "smart contract". Let's look at some code for a coffee shop that is much superior to the pizza shop.

new stdout(`rho:io:stdout`) in {
  contract @"coffeeShop"(order) = {
    stdout!("Coffee Order Received")
  }
  |
  @"coffeeShop"!("one hot chocolate")
  |
  @"coffeeShop"!("two large cappuccinos please")
}

Exercise

Order a second drink from the coffee shop

Exercise

Change the acknowledgement message

Which should generally come first?

  • A send because that's how normal mail works.
  • A receive because it's faster to run the code that way.
  • Either a send or a receive can come first, or they can come together.
  • A receive because rholang is concurrent.
  • Neither. Just make a comm event directly.

Correct!

Incorrect. Please try again.

Exercise

The channel is just named @"coffeeShop". Change it to be named after a specific coffee shop of your choosing. While you're at it, modify the code to use new like we recently learned.

Persistent For

There are actually two different styles of syntax in rholang to achieve this persistent behavior. We just learned about contract. The following snippets are equivalent.

contract @"coffeeShop"(order) = {

for(order <= @"coffeeShop") {

Notice this is different from a normal for because it has a double arrow <= rather than a single arrow <-. The only difference between the persistent for and a contract comes when we start talking about blockchains. For now you can think of them as the same thing.

Exercise

The pizza shop could use a contract like the one the coffee shop had. Let's write it one but use a persistent for instead of a contract. Try to write the entire thing from scratch so you remember the syntax better.

Which of these things is not like the other?

  • for (a <- b){}
  • contract b(a) = {}
  • for (a <= b){}

Correct!

Incorrect. Please try again.

Which send will produce a comm event with for (message <- @"grandmasSnapChat"){0}?

  • grandmasSnapChat!("Hi Grandma")
  • @"grandmasSnapChat"!("Glad you're snapping Grandma")
  • for("Here's a snap for you g'ma" <- @"grandmasSnapChat")

Correct!

Incorrect. Please try again.

Telephone, Names, and Processes

Message Relaying

The game of telephone is perfect to simulate message forwarding in rholang.

We've previously learned how to send a message to grandma or a pizza shop. But so far all those recipients have done is acknowledge the message by printing to standard output.

Now let's make them do something more interesting by passing the message along like in a child's telephone game

new stdout(`rho:io:stdout`) in {
  // Start the game by sending a message to Alice
  @"Alice"!("How to program: Change stuff and see what happens.")
  |

  // Concurrently, Alice listens for the message
  for (message <- @"Alice") {

    // When she receives the message she'll pass it on to Bob
    @"Bob"!(*message)
  }
  |

  // Concurrently, Bob will listens for the message
  for (message <- @"Bob") {
    // Bob is the last player, so he'll announce the message
    stdout!(*message)
  }
}

As the message says, you learn most when you experiment. So be sure to change things as you see fit.

Exercise

That telephone game was fun, but it's always better the have more players. Go ahead and add a third player called Charlie. Instead of printing to stdout, bob will send the message along to Charlie. Then Charlie will print it to the screen. The More the Merrier!

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

Exercise

If you've ever actually played telephone, you know that the message rarely arrives in tact. Bob decided to mix it up by passing along a false message. Change the program so Bob passes along a different message regardless of what he receives.

WTH is That *?

Opposites attract

Did you notice the * in @"Bob"!(*message)? In rholang there are two kinds of things, "names" and "processes". There are also ways to convert between the two.

A "process" is any piece of rholang code such as our telephone game, or our pizza shop order program. Processes can be big hundred-line programs or small on-liners. They can even be tiny pieces of code that are just values. Here are some example processes.

  • stdout!("Sup Rholang?") A common send
  • 0 The smallest possible process. It literally means "do nothing".
  • for(msg <- @"phone"){0} A common receive that does nothing when a message arrives.
  • "Hello World" Another small process that also does nothing. These are called "Ground Terms".

A name is something that can be used as a channel to send messages over. In most programming languages, names are totally separate things that exist on their own. But in rholang, names come from "quoting processes" by putting the @ sign before a process. Here are some example names.

  • @"Hello World" Made by quoting the ground term "Hello World".
  • @0 The smallest possible name. Made by quoting the smallest possible process.
  • @(@"Alice"!("I like rholang, pass it on.")) Made by quoting a process from our telephone game

So What is * all about?

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

We can package processes up to make names by quoting them with the @ symbol. We can also turn names back into processes by "unquoting" them with the * symbol.

The big thing to remember is in rholang we send processes and receive names. That's important so I'll say it again. You always send a process and on the other end, you receive a name.

When Alice receives our message with for(message <- @"Alice") she is receiving, so message becomes a name. When she later sends to Bob she has to send a process, so she uses * to turn message back into a process with @"Bob"!(*message)

Quiz

What do we send?

  • processes
  • names

Correct!

Incorrect. Please try again.

What do we receive?

  • processes
  • names

Correct!

Incorrect. Please try again.

What is @"registration"

  • process
  • name
  • invalid syntax

Correct!

Incorrect. Please try again.

What is 0?

  • process
  • name
  • invalid syntax

Correct!

Incorrect. Please try again.

What is @0?

  • process
  • name
  • invalid syntax

Correct!

Incorrect. Please try again.

What is @@0?

  • process
  • name
  • invalid syntax

Correct!

Incorrect. Please try again.

*importantData is a process, so what is importantData?

  • process
  • name
  • invalid syntax

Correct!

Incorrect. Please try again.

Which of these is equivalent to "BobsPhone"?

  • *@"BobsPhone"
  • @"BobsPhone"
  • *"BobsPhone"
  • @*BobsPhone
  • stdout!("BobsPhone")

Correct!

Incorrect. Please try again.

Exercise

This telephone game has a fork

Instead of a linear telephone game where each player passes the message to the next, let's add a branch in the game. So now Bob will send to Charlie like before, but Bob will also send to Elise.

Each branch can be as long as you want, but at the end of each branch, print the message to stdout.

Persistent Sends and Peek

Why send repeatedly?

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

Our pizza and coffee shops were both interested in receiving many messages on the same reusable channel. We accomplished that with a persistent for for (msg <= chan){...} or a contract contract chan(msg){...}.

An air traffic control tower may be interested in doing just the opposite -- sending the same message over and over. The controllers in the tower want to record a message containing weather and active runway information once, and make it available for every pilot who needs it. Like the pizza shop, they are busy and can't be bothered to continually re-send the message every time a pilot consumes it.

Persistent send syntax

The control tower just needs a minor adjustment in their code to make the send persistent. Rather than sending with a single !, they will use a double !!.

// ATC sends the info
@"airportInfo"!!("No wind; Runway 11")
|
// Pilot receives the info
for (info <- @"airportInfo") {
  0 // Pilot just listens, doesn't do anything
}

Confirm for yourself that the original send is still in the tuplespace.

Exercise

Modify the above code so that a second pilot also receives the information. Still, the send persists.

By the way, did you notice that we don't need new stdout(...) in {} when we don't actually use stdout.

How many comms happen in for (x <- y) {0} | y!!(0)

  • 1
  • many
  • 0

Correct!

Incorrect. Please try again.

Double Checking a Message

Persistent sends and receives are very useful as we just showed. But often normal sends and receives are perfectly good too. Imagine that I send my grandmother a letter, and she receives it.

new grandma, stdout(`rho:io:stdout`) in {
  // I send the message
  grandma!("Meet at the mall at 4:00")
  |
  // Grandma receives it
  for (msg <- grandma){
    stdout!("I got your message")
  }
}

Now Imagine that I want to double check that I sent her the correct time. I could simply consume the message, but then it wouldn't be around for her to read anymore.

Exercise

Using what you already know, you can achieve this by consuming the message, checking it yourself, and then sending the same message back to the old channel.

Give that a try on your own first. The solution is listed below.

How many comms happen in for (x <= y) {0} | y!!(0)

  • 1
  • many
  • 0

Correct!

Incorrect. Please try again.

Answer to Exercise

new grandma, stdout(`rho:io:stdout`) in {
  // I send the message
  grandma!("Meet at the mall at 4:00")
  |
  // I double check the message
  for (msg <- grandma){
    stdout!("Double checking the message")
    |
    // Put a copy back on the channel for grandma
    grandma!(*msg)
  }
  |
  // Grandma receives it
  for (msg <- grandma){
    stdout!("I got your message")
  }
}

Peek Syntax

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

Rholang will have a special syntax for this sort of thing eventually. It isn't available right now, but I'll show you the syntax just so you're ready. To "peek" at what's on a channel without consuming it, use the <! operator.

// REMINDER: Peek is not yet implemented

// I send the message
@"grandma"!("Meet at the mall at 4:00")
|
// I double check the message
for (msg <! @"grandma"){
  0 // Do whatever you do to double check the time
    // Message is still safely on the channel
}
|
// Grandma receives it
for (msg <- @"grandma"){
  @"stdout"!{"I got your message"}
}

If you've ever written spreadsheet macros, or even used a spreadsheet. Accessing data without consuming it should be familiar. Think of it as

Which syntax is used to peek at a message?

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

Correct!

Incorrect. Please try again.

How many comms happen in for (x <! y) {0} | y!!(0)

  • 1
  • many
  • 0

Correct!

Incorrect. Please try again.

Join Operator

Multiple data sources

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

Occasionally a computation can only be made once data is retrieved (messages are received) from two or more different data sources. For example, you can't tell whether you won the lottery until you've got your ticket number and the winning number. You can't make change for a purchase until you know the price and the amount tendered. You can't tell who wins a pushup contest until you know mow many pushups each competitor completed.

Rholang has the join operator for exactly this situation. To perform a join, just use the ; character.

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

Rocket Launch

A space exploration company wants to make sure their rocket only launches when both of two flight engineers, Alice and Bob, give the launch command. As an example, Bob would give the go ahead by sending BobLaunch!("launch"). When both engineers give the command, the rocket can be launched.

Exercise

Consider how this code might be written using the join operator that we just learned discussed.

The wrong way

One may have been tempted to solve the rocket problem by first receiving one launch command then the other.

new stdout(`rho:io:stdout`) in {
  // Listen for Alice's then Bob's launch commands
  for (x <- @"AliceLaunch"){
    for (y <- @"BobLaunch"){
      stdout!("Launching the rocket")
    }
  }
  |
  // When ready, Engineers send their commands
  @"AliceLaunch"!("launch")
  |
  @"BobLaunch"!("launch")
}

The problem here is when Alice okay's the launch, but Bob hasn't yet. Alice should be able to change her mind, but she cannot. Imagine if she suddenly noticed a problem with the rocket, or received some bad news and wanted to abort the launch.

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

When using a join, she can still change her mind because the for won't consume her launch message until Bob's message also appears and is ready to be consumed.

Launch Solution

new stdout(`rho:io:stdout`) in {
  // Listen for both launch commands
  for (x <- @"AliceLaunch"; y <- @"BobLaunch"){
    stdout!("Launching the rocket")
  }
  |
  // When ready, Engineers send their commands
  @"AliceLaunch"!("launch")
  |
  @"BobLaunch"!("launch")
}

What code would Alice need to "par in" to retract her launch command.

  • @"AliceCancel"!("cancelZ")
  • @"AliceLaunch"!("cancel")
  • for (x <- @"AliceLaunch"){0}

Correct!

Incorrect. Please try again.

The concept of joins is framed as the dining philosophers problem, and (explored further) in the concise rholang tutorial.

In for (x <- y; a <- b){ 0 }, which channel must be sent on first?

  • y
  • b
  • doesn't matter
  • they must be sent simultaneously

Correct!

Incorrect. Please try again.

In for (x <- y; a <- b){ 0 }, which message will be consumed first?

  • x
  • a
  • doesn't matter
  • they will be consumed simultaneously

Correct!

Incorrect. Please try again.

Exercise

There is a game where two players will each send a message on separate channels. Whoever sends the first message, loses, and whoever sends the second message wins. Your task here is to write the game code that will tell which player won. To play the game, players should send messages like shown.

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

Solution to the patience game In this case we actually don't want to use join because we care which player went first. Hope I didn't trick you ;)

new stdout(`rho:io:stdout`) in {
  // Use the rnode repl to test your code
  // Send messages like in both orders
  // @"P1"!("Send any message")
  // @"P2"!("Hope I win")

  // When Player one wins
  for (m2 <- @"P2"){
    for (m1 <- @"P1"){
      stdout!("Player one wins!")
    }
  }
  |
  // When player two wins
  for (m1 <- @"P1"){
    for (m2 <- @"P2"){
      stdout!("Player two wins!")
    }
  }
}

Like the comment says, you should send the messages in both orders to make sure of who wins. The easiest way to do that right now is probably in the REPL. Another option is to have one player signal the other for when to go as shown below. We'll explore this concept further in the next unit.

new P1, P2, signal in {
  // P1 sends their message then signals P2 who is waiting
  P1!("Send any message")
  |
  signal!("Go ahead, I'm done.")
  |
  // When P2 receives the signal, they send their message
  for (_ <- signal){
    P2!("Hope I win")
  }
}

Why is it possible for nobody to win the patience game as we wrote it above?

  • Because both players could send messages at the same time
  • The players are sending on the wrong channels
  • The first block receives P2 while the second receives P1, so neither ever finishes

Correct!

Incorrect. Please try again.

Unforgeable Names and Acks

Making Channels "Private"

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

So far every channel we've sent on has been a public name like @"pizzaShop". Anyone can send a message to this channel (which is good for business) but anyone can receive a message on it too (which is terrible for business). Imagine if competitors could consume pizza orders so that the pizza shop never received them.

What code would a bad pizza shop have to write to intercept orders?`

  • contract evilPizzaShop(interceptedMessage) = {0}
  • @"evilPizzaShop"!("pizzaShop")
  • @"pizzaShop"!("intercept")
  • for (interceptedMessage &lt;- @"pizzaShop"){...}

Correct!

Incorrect. Please try again.

Bound and Free Names

We learned how to receive messages with for and contract last time. Both of these constructs "bind" names. For example, order is a bound name within the coffee shop code.

for (order <= @"coffeeShop") {
  @"stdout"!("Coffee Order Received")
}

The same is true when we use contracts.

contract @"coffeeShop"(order) = {
  @"stdout"!("Coffee Order Received")
}

A name is considered bound if it only exists inside a specific process and can't be accessed from outside. So the name order is bound in the coffee code. On the other hand, any name that can be accessed from elsewhere is a "free" name. @"coffeeShop" is a free name in the above examples.

State whether x is bound or free in each of the following code snippets.

for (x &lt;- y){0}

  • Bound
  • Free
  • Neither

Correct!

Incorrect. Please try again.

for (y &lt;- x){0}

  • Bound
  • Free
  • Neither

Correct!

Incorrect. Please try again.

new x in { x!(true) }

  • Bound
  • Free
  • Neither

Correct!

Incorrect. Please try again.

contract x(y) = { 0 }

  • Bound
  • Free
  • Neither

Correct!

Incorrect. Please try again.

contract y(x) = { 0 }

  • Bound
  • Free
  • Neither

Correct!

Incorrect. Please try again.

for (y &lt;- @"x"){0}

  • Bound
  • Free
  • Neither

Correct!

Incorrect. Please try again.

The new Operator

for and contract are perfect for binding names inside of continuations. But what if we want to create a bound name to send messages on? For example, our pizza shop would prefer not to have its orders intercepted. We solve this with the new operator.

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

  // Same contract as before
  contract pizzaShop(order) = {
    stdout!("Order Received.")
  }
  |
  // Known customers can order because pizzaShop is bound here.
  pizzaShop!("Extra bacon please")
  |
  pizzaShop!("Hawaiian Pizza to go")
}

// But we can't order from here, because pizzaShop doesn't exist

First notice that pizzaShop is a name even though it doesn't start with @. That's because the new operator created it as a name directly rather than by quoting some written-down process. Whenever you create a new name, it is always a bound name.

Second, notice that while this technique does prevent other pizza shops from consuming orders, it also prevents new customers from ordering. We'll solve this problem in the lesson on bundles.

What happens when you try to order a pizza fron outside of the new restriction.

  • The order works fine
  • The order works but takes much longer
  • Error about top-level free variables
  • The code runs, but no order is received

Correct!

Incorrect. Please try again.

We learned that all names quote processes. So what process does the pizzaShop name quote? Try printing the process to stdout to see

  • It quotes "pizzaShop"
  • It doesn't quote anything
  • "Some Unforgeable hex code"

Correct!

Incorrect. Please try again.

Private vs Unforgeable

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

new is known as the restriction operator because it restricts use of the bound names that it creates to within it curly braces or "lexical scope". Within the world of the rholang these new names really are only visible within the correct scope, but remember that human programmers can look in to that world from the outside. That is especially true when working in a blockchain context.

So while a competing pizza shop can no longer consume pizza orders intended for our shop, they can still read the orders with a block explorer. Occasionally programmers call new names "private", but a better term is "unforgeable", which explains the answer to the previous question.

We've previously used new to prevent tuplespace pollution. Why did using unforgeable names prevent us from having to clear the tuplespace after each contract run?

  • Because new makes free names
  • Because new creates unforgeable names that can't be accessed by outside code
  • Because new automatically clears the tuplespace

Correct!

Incorrect. Please try again.

Acknowledgement Channels

We acknowledge communications all the time in conversations

One common use of unforgeable names is "acknowledgement channels", usually called "ack" channels for short. Instead of confirming orders by printing to the screen and disturbing everyone, the pizza shop should really just let the customer know that the order has been placed.

To do that the pizza shop needs to know how to contact the customer. So the customer should supply an acknowledgement channel to be called back on. Traditionally such a channel is named ack.

new pizzaShop in {

  // Now we take an order and an ack channel
  contract pizzaShop(order, ack) = {
    // Instead of acknowledging via stdout, we use ack
    ack!("Order Received.")
  }
  |
  // Known customers can order because pizzaShop is bound here.
  pizzaShop!("Extra bacon please", "alice")
  |
  pizzaShop!("Hawaiian Pizza to go", "bob")
}

Why don't the acknowledgements in the previous example show up on the screen?

  • There is a bug in the code
  • The orders were not received correctly
  • The confirmation was not sent to stdout

Correct!

Incorrect. Please try again.

Exercise

The previous example causes tuplespace pollution on the channels @"Alice" and @"Bob". Fix it so that Alice and Bob both have unforgeable names.

Sending Names Gives Permission

We just saw how the customer can give the shop an ack channel to receive order confirmation. It turns out we can do even better. With our previous code, anyone could contact the customer on the ack channel. That means anyone could send a forged ack making the customer think the order was placed when really it wasn't. Really Alice and Bob should keep their unforgeable names under tight control. Because giving someone that name gives them the capability to contact you.

new pizzaShop in {

  // Take orders and acknowledge them
  contract pizzaShop(order, ack) = {
    ack!("Order Received.")
  }
  |
  // Order a pizza and send a private ack channel.
  new alice in {
    pizzaShop!("One medium veggie pizza", *alice)
  }
}

The solution is to create a new unforgeable name, and give it to the pizza shop so that only they can call you back. Even though the pizza shop is outside of the new alice, it can still send on the channel because alice gave it the channels name. This is a wonderful way to delegate priveledges.

In this example we trust the shop to only send on the ack channel, but notice that it could also receive if it wanted to. We'll learn how to give out only some of those permissions in the next lesson on bundles.

Bob also wants to order a pizza and give a unforgeable ack channel. Where should he create his unforgeable channel?

  • On his own line, after the alice code
  • On the same line the Alice did
  • On the very first l ine of the program

Correct!

Incorrect. Please try again.

stdoutAck and stderrAck

Now that you understand ack channels, you should know about two other ways to print to the screen. They are channels called stdoutAck and stderrAck. They work just like their cousins from lesson 1, but they take an ack channel.

new myAckChannel,
    stdout(`rho:io:stdout`),
    stdoutAck(`rho:io:stdoutAck`) in {
    
  stdoutAck!("Print some words.", *myAckChannel)
  |
  for (acknowledgement <- myAckChannel) {
    stdout!("Received an acknowledgement.")
  }
}

By the way, did you ever notice the handful of stuff that always starts in a fresh tuplespace? Four of those things are the built-in receives for the screen-printing channels. The others are for cryptography. We'll discuss them later.

Exercise

stdout!("1")|stdout!("2")|stdout!("3") Notice that this program does not print the numbers in any particular order. The calls happen concurrently. Imagine we really need these lines to print in order. Modify the code to use ack channels and ensure that the numbers get printed in order.

Exercise

Predict how this program will run (what it outputs and how it reduces in the tuplespace). Then run it to test your prediction. new myChan in { myChan!("Hi There") } | for (msg &lt;- myChan) {stdout!(*msg)}

If your prediction for the previous exercise was wrong, modify the program so it actually does what you predicted it would.

Quiz

Which name is bound in for(x &lt;- y){0}

  • x
  • y
  • 0

Correct!

Incorrect. Please try again.

Which name is bound in new x in {0}

  • x
  • y
  • 0

Correct!

Incorrect. Please try again.

If pizzzaShop is a name, then what is @pizzaShop?

  • A name
  • A process
  • Invalid syntax

Correct!

Incorrect. Please try again.

Why did the pizzaShopAck code send "bob" as an ack channel instead of @"bob"?

  • No reason; it's just a style choice.
  • Because @"bob" is a name, but we have to send processes.
  • That's special syntax for ack channels.

Correct!

Incorrect. Please try again.

Bundles

Stolen Messages

Alice's fans love her and want to send her lots of mail .

Alice is a rising celebrity who receives mail from her fans. They used to send mail directly to her on a public name.

new stdout(`rho:io:stdout`) in {
  // Alice reads fan mail
  for (mail <- @"alice") {
    stdout!("Alice received a fanmail")
  }
  |

  // Bob sends fan mail
  @"Alice"!("Dear Alice, you're #TheBest")
}

But as she became more popular, her jealous competitor Eve began stealing her mail.

Exercise

Write the code for a competitor to steal the mail

The problem is that the competitors can listen on the same channel Alice can. So what she really needs is for her fans to have a "write-only bundle"

// These channels are basically public names within
// the file like rho:pubkey:whatever

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

  // Alice gets a lot of fan mail, so she
  // creates a new write only bundle and publishes it.
  new aliceFanMail in {

    // Alice gives fanmail channel publically
    alice!!(bundle+ {*aliceFanMail})
    |

    // Alice also reads fan mail
    for (mail <- aliceFanMail) {
      stdout!("Alice received a fanmail")
    }
  }
  |

  // When Bob wants to send fanmail he asks for the channel
  // and then sends
  for (aliceFanMail <- alice) {
    aliceFanMail!("Dear Alice, you're #TheBest")
  }
  |

  // Eve tries to intercept a message, but cannot
  // because Alice's fanmail channel is write-only
  for (aliceFanMail <- alice) {
    for (@stolenMail <= aliceFanMail) {
      stdout!(["Eve stole a message: ", stolenMail])
    }
  }
}

The bundle+ {*aliceFanMail} is a channel just like aliceFanMail except it can only be snet on, not received.

Subscriptions

The bundle solution above does prevent Ewritten onve from stealing mail, which is good. But in the blockchain context it also has the unfortunate side effect that Alice has to pay to send her fanmail address. Blockchain fees work a little like postage.

The sender includes a return envelope already posted to himself

Exercise

Alice can save postage by making fans request the fanmail address from her. Then they will have to pay the transaction costs. A bit like sending a return envelope with a stamp already on it.

Complete Alice's code so that she can get Bob the address he needs.

Here's the answer:

// These channels are basically public names within
// the file like rho:pubkey:whatever

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

  // Alice get a lot of fan mail, so she
  // creates a new write only bundle and publishes it.
  new aliceFanMail in {

    // Alice returns fanmail channel to any fan that asks
    for (return <= alice) {
      return!(bundle+ {*aliceFanMail})
    }
    |

    // Alice also reads fan mail
    for (mail <- aliceFanMail) {
      stdout!("Alice received a fanmail")
    }
  }
  |

  // When Bob wants to send fanmail he asks for the channel
  // and then sends
  new return in {
    alice!(*return) |
    for (aliceFanMail <- return) {
      aliceFanMail!("Dear Alice, you're #TheBest")
    }
  }
  |

  // Eve tries to intercept a message, but cannot
  // because Alice's channel is write-only
  new return in {
    alice!(*return) |
    for (aliceFanMail <- return) {
      for (@stolenMail <= aliceFanMail) {
        stdout!(["Eve stole a message: ", stolenMail])
      }
    }
  }
}

Astute readers will notice that Eve can now just intercept messages asking for the fanmail address. Good observation. As a bonus exercise, you could write Eve's new code. (hint: it's the same as the old code). The solution to this problem involves public key cryptography and the registry. We'll learn about that in unit 4.

Exercise

Our pizza shop back in lesson 2 had a similar problem to Alice. Rework that code so they can easily take on new customers.

Jackpot

A single "thrower" throws a ball for one of many catchers to catch

I used to play a game called jackpot as a kid. One player would throw the ball and yell a number. The other players would all try to catch the ball and whoever caught it would receive that number of points.

Playing jackpot is just the opposite of sending fanmail. Before there were many fans all sending to one celebrity. Now there is one thrower, sending to one of many recipients

new throw, stdout(`rho:io:stdout`) in {
  // Throw the ball worth five points
  throw!(5)
  |

  // Throw the ball several more times
  throw!(4) |
  throw!(2) |
  throw!(6) |

  // Bill and Paige both try to catch
  for (points <= throw){
    stdout!("Bill caught it")
  }
  |
  for (points <= throw){
    stdout!("Paige caught it")
  }
}

Who will catch the ball in the jackpot code?

  • Bill because his catch code is first.
  • Bill because his catch code is closest to the throw code.
  • Paige because her catch code is last.
  • We don't know; it is nondeterminate.

Correct!

Incorrect. Please try again.

Exercise

Exercise: Use stdoutAck to display how many points each person actually gets when they catch the ball.

Most programming languages will allow you to join or "concatenate" two strings together, and rholang is no exception. We can stdout!("Hello " ++ "world"), but we can't concatenate a string with an int. That's why we had to use that stdoutAck trick. Another option is to print a list stdout!(["Bill caught it. Points earned: ", *points]). We'll go into more detail about both techniques in future lessons.

How is this game in rholang different than the real game where one ball is throw repeatedly?

  • It is a very accurate simulation
  • In rholang all balls are thrown at once and caught in any order
  • In rholang the balls are caught in the reverse order from what they are thrown.
  • In rholang Bill makes all his catches, then Paige makes all her catches.

Correct!

Incorrect. Please try again.

Imposter throws

Notice that anyone can come along and mess up this game by throwing fake balls. This is just the opposite of Eve coming along and stealing Alice's fanmail.

What code would Eve have to par in to throw an imposter ball worth 100 points?

  • for (imposter <- throw){imposter!(100)}
  • throw!(100)
  • @"throw"!("100")

Correct!

Incorrect. Please try again.

We solve this problem by making sure that the public can only read from the throw channel, but not write to it.

new gameCh, stdout(`rho:io:stdout`) in {
  new throw in {

    //Give out read-only access
    gameCh!!(bundle- {*throw})
    |
    // Now actually make all the throws
    throw!(4) |
    throw!(2) |
    throw!(6)
  }
  |
  // Bill and Paige join the game
  for (throw <- gameCh){
    for (points <= throw){
      stdout!(["Bill caught it. Points: ", *points])
    }
  }
  |
  // Eve tries to throw a fake, but can't
  for (throw <- gameCh){
    throw!(100)
  }
}

Like before, this code requires the game host to pay for everyone who get's the bundle from him. It could be refactored so players have to subscribe to the game like we did with Alice and her fan mail.

Public Key Crypto

This bundle is sealed within the blockchain world, but totaly openable to anyone outside. Remember that just because data is off limits within the blockchain, doesn't mean it's truly private.

In some ways, read-only bundles duplicate the signing features of public-key cryptography. The jackpot catchers here are sure that the balls came from the thrower because only he can send on the throw channel, which is a lot like cryptographic signing.

In some ways write-only bundles duplicate the encryption features of public-key cryptography. Only Alice can receive messages sent on her fan mail channel. One very important difference is that the messages sent here are 100% visible from outside the blockchain! So while write-only bundles are an effective way to communicate unforgeable names, they are not a good way to plot a heist, or evade government surveillance. Be Careful!

More Bundles

In addition to read- and write-only bundles, there are two other types. The other bundle types are less common, but still useful. We'll explore them as the need arises, but I'll summarize them briefly here.

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

State Channels

Holding on to data

Variables are so 2015. It's all about state channels now.

By now you're good at sending data to the tuplespace, and receiving data from the tuplespace. But whenever you're computing, you occasionally need to set data aside to be used later. Almost every programming language has a concept of variables.

Another way in which rholang is unique is that it doesn't have traditional variables. Instead, we can just use the tuplespace to store our data. Whenever you want to set something aside for later, just send it on some channel and receive it back later. Channels that are used in this way are called "state channels", and often have Ch at the end of their name

new boxCh in {
  // To save data we just put it in the box
  boxCh!(42)
  |

  // Then to get data back out
  for (data <- boxCh) {
    // Do whatever you want with the data here.
    @"somePublicChannel"!(*data)
  }
}

What data gets sent to @somePublicChannel?

  • 0
  • box
  • 42
  • It is empty

Correct!

Incorrect. Please try again.

What data is left in the boxCh state channel?

  • 0
  • @"somePublicBox"
  • 42
  • It is empty

Correct!

Incorrect. Please try again.

Persisting Data

If we were to check the box again, we would not get a result. Because once we receive the message, it gets consumed from the tuplespace. We briefly discussed the solution back in our lesson on persistent sends and peeks.

new boxCh in {
  // To save data we just put it in the box
  boxCh!(42)
  |

  // To check the value without consuming it
  for (data <- boxCh) {
    // We send a copy back to the box
    boxCh!(*data)
    |
    // The do whatever with it
    @"somePublicChannel"!(*data)
  }
}

What data gets sent to @somePublicChannel?

  • 0
  • box
  • 42
  • It is empty

Correct!

Incorrect. Please try again.

What data is left in the boxCh state channel?

  • 0
  • @"somePublicBox"
  • 42
  • It is empty

Correct!

Incorrect. Please try again.

Patience Game Revisited

A few lessons back we discussed the patience game, where each player hopes to be the last one to send a message to the contract. We had some problems back then because we couldn't guarantee that a result of the game would ever be output.

Take a minute to remind yourself of the problem we had. With a state channel, we can solve this problem properly.

new P1, P2, stdout(`rho:io:stdout`) in {

  // active gets its own scope so players can't change its value.
  new active in {
    active!(true)
    |
    for(_ <- active; _ <- P1) {
      for( _ <- P2) {
        stdout!("P2 Wins")
      }
    }
    |
    for(_ <- active; _ <- P2) {
      for (_ <- P1) {
        stdout!("P1 Wins")
      }
    }
  }
  |
  P1!(0)
  |
  P2!(0)
}

How does the above code avoid the problem of each block getting called once?

  • By forcing player one to use the top block
  • Because after the first call activeCh is empty
  • Because state channel make things sequential

Correct!

Incorrect. Please try again.

Objects and Methods

This click-counter can be incremented and reset

In "Object Oriented Programming" languages such as java, we can model real-world objects by encapsulating some data along with methods that use or change the data. The same thing is possible in rholang.

In this example, we'll create an object that represents a basic click counter. The ingredients are:

  • State Channels: currentCount
  • Methods: increase, reset
new currentCount, increase, reset, stdout(`rho:io:stdout`) in {

  // Start the counter at zero
  currentCount!(0) |

  // Method to increase counter (returns the old value)
  contract increase(ack) = {
    for (old <- currentCount) {
      currentCount!(*old + 1) |
      ack!(*old)
    }
  }
  |

  // Method to reset the counter (returns the old value)
  contract reset(ack) = {
    for(old <- currentCount){
      currentCount!(0) |
      ack!(*old)
    }
  }
  |

  // Demo using the counter
  // This part gets deeply nested because it is sequential
  new ack in {

    // Increase the counter three times
    increase!(*ack) |
    increase!(*ack) |
    increase!(*ack) |

    // And check it's value afterward
    for(_ <- ack; count <- currentCount) {
      stdout!(*count)
    }
  }
}

Exercises

It is inconvenient to manually replace the counter value each time I check it. Instead we should have a method for that.

Exercise

Now that it's easier and safer to check the counter, let's write some better tests that actually check all the methods.

Factories

If you've programmed in other languages like java you may be familiar with constructors. If you haven't programmed in in java, that's great, because Rholang uses factories to make new objects rather than constructors.

The counter is a useful construct in rholang, and you'll likely find that you use it in your projects. The problem is that many projects may want to use counters, and having just one is insufficient. So the solution is to make a factory contract that makes counters. When the factory contract is called, it sends back a brand new counter.

new counterFactory, stdout(`rho:io:stdout`) in {
  contract counterFactory(increase, reset) = {
    new currentCount in {
      // Start the counter at zero
      currentCount!(0) |

      // Method to increase counter (returns the old value)
      contract increase(ack) = {
        for (old <- currentCount) {
          currentCount!(*old + 1) |
          ack!(*old)
        }
      }
      |

      // Method to reset the counter (returns the old value)
      contract reset(ack) = {
        for(old <- currentCount){
          currentCount!(0) |
          ack!(*old)
        }
      }
    }
  }
  |

  new ack, myIncrease, myReset in {
    // Demo using the counter here
    0
  }
}

How would a user call the factory to get a new counter?

  • counterFactory!(*ack)
  • counterFactory!(0, *ack)
  • @"counterFactory"()
  • counterFactory!(myIncrease, myReset)

Correct!

Incorrect. Please try again.

How would a user reset their counter after creating as indicated above.

  • myIncrease!(*ack)
  • myReset!(*ack)
  • counterFactory!(myReset, *ack)
  • counterFactory!(myReset)

Correct!

Incorrect. Please try again.

Method Dispatching

There are two primary techniques for making methods available. The first option I call "separation of powers" because each method listens on its own dedicated channel.

Another option is the "control panel" technique where there is a single unforgeable name called the control panel and all the methods are built on it.

// Separation of Powers
contract factory(method1, method2) = {
  contract method1(ack) = { ... }
  contract method2(ack) = { ... }
}

// Control Panel
contract factory(cPanel) = {
  contract @[cPanel, "method1"](ack) = { ... }
  contract @[cPanel, "method2"](ack) = { ... }
}

Which technique did the counter example use to dispatch methods?

  • Separation of Powers
  • Control Panel

Correct!

Incorrect. Please try again.

Exercise

Convert the counter example to use the other method dispatching technique. You should convert the tests too.