A Tutorial Introduction to Michelson & Tezos, Part II: Hello Tezos

Now that we know a little Michelson, let’s take a look at the Tezos blockchain, which is the “environment” in which our Michelson contracts are actually going to run.

This is not going to be an exhaustive overview of Tezos; we’re going to focus on those aspects that are most relevant from the perspective of a Michelson programmer. We’re going to look at a few useful tools, such as the tezos-client program that is in the alphanet docker container we installed last chapter and an online blockchain explorer.

These tools, like Tezos itself, are still immature and undergoing rapid development. For the most part, they employ the “info-dump” style of user interface, which works fine for e.g. a Tezos core developer, but can be very overwhelming to newcomers. For the purposes of this tutorial, we’re specifically interested in how to program in Michelson, so we can mostly abstract over a lot the detail exposed by the tooling that would be more relevant in other circumstances, such as if we wanted to run a baker. Some of these details will be covered again in greater depth in future chapters.

For now, our goal is to gain a basic understanding of how to use the current Tezos tooling, with the goal, by the end of the next chapter, of running and interacting with a simple Michelson contract on the Tezos alphanet.

In your terminal, in the directory where you installed the alphanet container from the last chapter, execute the following command:

$ ./alphanet.sh head

Assuming the node is running (if not, start it with ./alphanet.sh start), this should output something like:

Warning:
This is NOT the Tezos Mainnet.
The node you are connecting to claims to be running on the 
Tezos Alphanet DEVELOPMENT NETWORK.
Do NOT use your fundraiser keys on this network.
Alphanet is a testing network, with free tokens.
{ "protocol": "PsddFKi32cMJ2qPjf43Qv5GDWLDPZb3T3bF6fLKiF5HtvHNU7aP", "chain_id": "NetXgtSLGNJvNye", 
"hash": "BLe9jhCFStAkeYVpp4Czuh1Kp18qKUq1v5XWasbm1MiBMT1LGAs",
"level": 27750, "proto": 1, "predecessor": "BLcZcNKP6ETkC9QRku27VFhSbWwFLqzjpEiMY4U6k5qJnfThVcn",
"timestamp": "2018-12-13T12:53:28Z",
"validation_pass": 4,
"operations_hash": "LLoaaw91Q5k48SfBL96ttQe54WcUdn32BGKyNoDZ8sCA5KAEwJGMb",
"fitness": [ "00", "00000000000bf429" ],
"context": "CoWNoFZFhB2hGWhXv5VqEZgL6mS4UFtdJbYKrM7VY6Yt8MgZXiEp", "priority": 0,
"proof_of_work_nonce": "00000003e24de8c8",
"signature":
"sigk66tvEvH2aYRTb3wurhqrRhw2SnGXuXreZii5Vg55mWgrDL98PJkXeDJ2Q8yZwJPLbTFPCUg9WT6bxYS6x5kBpqqK4Qa2" }

Please ignore everything in this for the moment, except the following line:

"hash": "BLe9jhCFStAkeYVpp4Czuh1Kp18qKUq1v5XWasbm1MiBMT1LGAs",

Please copy the value on the right, as we will use it in a moment. Note that the actual value displayed in your terminal will almost certainly be different for you than what I’ve written down here. Please copy the value from your terminal.

Now open tzcan.io’s alphanet block explorer in your browser, and in the search box paste the hash and hit enter.

You should see something like this:

This is the hash value of the block that your node thinks is currently the head of the Tezos alphanet blockchain.

Look at the sections that say

Predecessor: BLcZcNKP6ETkC9QRku27VFhSbWwFLqzjpEiMY4U6k5qJnfThVcn Successor: BL5CZUh1CePUW4gwjKAJvyUsGrZwyExMwwjUPJGsHcC6vDWmU6j

To briefly review, a blockchain is, as the name suggests, a chain of blocks. Each block includes a hash of its predecessor block, which in turn includes a hash of its predecessor, and so on and so forth all the way back to the first (or “genesis”) block in the chain that has no predecessor. (Note only the predecessor hash is actually in the block, the successor hash is an out-of-band piece of information that tzScan has added for our convenience).

You can take a look at the alphanet Genesis block here: https://alphanet.tzscan.io/0

The reason each block includes the hash of its predecessor is to create a data structure that is prohibitively difficult to surreptitiously modify.

For example, suppose I wanted to edit my copy of block BLe9jh... and add a transaction that credited one of my accounts with 1 billion tez, I could do that. But it would be obvious to everyone that I had tampered with the block, because the hash of the block would no longer match.

All the information in the block (when serialized) is just a number. The hash of the block is just the output of a specific hash function on that number:

Hash(serializedBlock) = BLe9jh...

So if I add a transaction and change the block information, I change the serialized number represents that block, which will change the hash of the block. Since hash functions are designed to take negligible time and cost to run, if I try to send my tampered copy of the block to anyone else, they can easily recompute the hash function and discover that the hashes don’t match.

Furthermore, because each block includes the hash of its predecessor, changing any block changes all the other blocks “downstream” of it. This means that as long as you and I agree on what the latest block in the chain (the head) is, we can agree on the whole history of the chain. (Agreeing on the head is problem solved by the consensus algorithm, which in Tezos is a variant of delegated proof-of-stake).

This type of data structure, in general, is called a hash tree or a Merkle Tree. It’s important to emphasize, given the various abuses of the term, that all “blockchain” refers to is a hash tree whose nodes are blocks of transactions. It is a term in the same category as “key-value map”, or “linked-list”.

Returning to our block explorer:

We can see that block BLe9jh.. does not actually have any transactions in it. (your specific block may). This is because the Tezos Alphanet is designed for testing, so transactions are fairly rare and blocks are frequent (one every 30 seconds). Alphanet tez have negligible value and are in fact given away for free to anyone who wants them by a limitless faucet (which we will use later on). The Alphanet is not decentralized and is periodically reset from genesis every few weeks.

Tezos has three parallel blockchains, called “Mainnet”, “Alphanet” and “Zeronet”

Mainnet is where tez have real dollar value and where the network is decentralized. Zeronet is a bleeding edge test network used mainly by the core developers and is reset usually several times per day.

To see a block with actual transactions go to the Mainnet block list.

For this tutorial we’ll do most of our work on the Alphanet, although in a later chapter we’ll go through bootstrapping our own test network with a new genesis block.

By the way, if you re-run:

$ ./alphanet.sh head

You’ll notice that while you’ve been reading this section the head has updated.

Now that we’ve covered enough context, we’re going to actually do a few things on the Alphanet.

First, we’re going to need an identity, go to your terminal and run:

$ ./alphanet.sh shell

This drops us inside the docker container, so we can directly execute commands on the tezos-node. Your prompt might change and you can run some commands to verify that you’re now inside a docker container:

$ pwd /home/tezos ~ 
$ whoami tezos ~
$ ls -a
. .. .ash_history .tezos-client .tezos-node

You can use <Ctrl-D> or

$ exit

to return to your regular prompt.

$ ./alphanet shell 
$ tezos-node identity generate

This will take a short amount of time, so read on while it works.

A Tezos identity consists of the following things:

  1. A peer_id that uniquely identifies us to the network. You can see a list of alphanet peers on tzScan's network page
  2. A key-pair of public_key, secret_key that lets us securely communicate with other peers. E.g. we can use our secret key to generate transactions that other peers can verify came from us using our public key.
  3. A proof_of_work_stamp that shows that we did some amount of computational work while generating the identity. This is why the identity generate command takes some time, and is to prevent people from spamming the network by generating endless identities.

You can read more about identity generation with

$ tezos-node identity --help

Now that that’s done we can see the identity file generated by running:

$ cat /home/tezos/.tezos-node/identity.json 
{ "peer_id": "idtRhgUQFQcUFJLW5HXQbRcdYkaUr4",
"public_key":"34a911ff9258d7c23b44844372a54c2a2a031d41e275e6a7d1251d1aeb95e207",
"secret_key": xxxx
"proof_of_work_stamp":"70e5ea8e85745a54f1a1d11b7c650e56f57f5c76243c072f"

(Actually this .tezos-node/identity.json file is a dummy file, the docker container stores the real identity in /var/run/tezos/node/data/identity.json, but if you installed tezos from source it would be under .tezos-node)

Remember to never ever ever post your secret key anywhere. If anyone other than you learns your secret key your whole identity is compromised. On the Alphanet it doesn’t really matter, but it’s a good idea to practice good operational-security even when it doesn’t matter so that it becomes a habit for when it does.

The Tezos Alphanet faucet is a website run by the Tezos developers that lets us download a wallet file. Essentially, whenever the devs reset the alphanet, they include a bunch of funded accounts in the genesis block and then give the keys to those accounts away for free to anyone who wants to do some alphanet testing.

Navigate to https://faucet.tzalpha.net/ and download the file.

Now quit out of the alphanet docker container and cat the contents of that file:

$ exit 
$ cat ~/Downloads/tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4.json
{ "mnemonic": [ "xxxx", "xxxx", "xxxx", "xxxx", "xxxxx "xxxx", "xxxx", "xxxx", "xxxx", "xxxx", "xxxx" "xxxx", "xxxx", "xxxx", "xxxx", ],
"secret": "xxxx",
"amount": "7489334243",
"pkh": "tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4",
"password": "xxxx" "email": "qaqjqdvr.xlicmfia@tezos.example.org"
}%

The two lines of relevance for us are:

"amount": "7489334243", 
"pkh": "tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4",

This wallet file, like the identity file, has a cryptographic secret key. After activating the account listed in this file, the way you’re going to tell the network that you want to make a transfer is by broadcasting a transaction message (signed by this secret key) over the peer-to-peer network, using the p2p identity we generated earlier. Other peers can verify the correctness of transactions (whether we have enough tez to make a transfer) by validating our signature with our accounts public-key, which is the “address” of the account, as well as by looking up our balance in the blockchain.

Now let’s activate the account, giving it the local nickname alice:

$ ./alphanet client activate account alice with "container:/home/jcb/Downloads/tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4.json"

This should output:

Node is bootstrapped, ready for injecting operations. 
Operation successfully injected in the node.
Operation hash: onotiUDrveZX1NRt39zQmPxVbVkxS2qfpM5gucFTV8BvvfPZoRB Waiting for the operation to be included...
Operation found in block: BM5RPwciV64Bz5nG3FbYY6xusrFt2j6EHTBrA7Hhye9fzWUhMK1 (pass: 2, offset: 0) This sequence of operations was run:
Genesis account activation: Account: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4
Balance updates: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ... +ꜩ7489.334243
The operation has only been included 0 blocks ago. We recommend to wait more. Use command tezos-client wait for onotiUDrveZX1NRt39zQmPxVbVkxS2qfpM5gucFTV8BvvfPZoRB to be included --confirmations 30 and/or an external block explorer. 
Account alice (tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4) activated with ꜩ7489.334243.

Let’s open up tzScan and take a look at block BM5RPwciV64Bz5nG3FbYY6xusrFt2j6EHTBrA7Hhye9fzWUhMK1 which includes this activation:

And now if we click on the account activation, we should see:

with the balance!

(It can take up to a few minutes for transactions to appear on tzScan, so if you don’t see your transaction just sit tight for a bit while more blocks get confirmed and try again.)

Now lets generate a brand new account called “bob” with no balance of tez:

$ ./alphanet.sh client gen keys bob

This should just output the Alphanet warning, but we can go inside the docker container and see the accounts we’ve generated or activated with

$ ./alphanet.sh shell 
$ cat /var/run/tezos/client/public_key_hashs
[ { "name": "bob", "value": "tz1QCUKL2YZuGLSdTesXtVHJB8zRcG6XTz8f" },
{ "name": "alice", "value": "tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4" } ]

All the internal stored client data is in /var/run/tezos/client in the docker container, and in .tezos-client if you installed from source.

The client provides a better interface to the internal stored data, however:

$ ./alphanet.sh client list known addresses
bob: tz1QCUKL2YZuGLSdTesXtVHJB8zRcG6XTz8f (unencrypted sk known) 
alice: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 (unencrypted sk known)

Lets check the balances for alice and bob:

$ ./alphanet.sh client get balance for alice 
7489.334243 ꜩ
$ ./alphanet.sh client get balance for bob 
0 ꜩ

Let’s transfer 420 tokens to bob from alice:

$ ./alphanet.sh client transfer 420.5 from alice to bob --fee 0.5
Fatal error: The operation will burn ꜩ0.257 which is higher than the configured burn cap (ꜩ0). Use `--burn-cap 0.257` to emit this operation.

There’s burn-cap setting in the client to prevent us from accidentally sending a transaction that would burn more tez than want:

Λ ➜ ./alphanet.sh client transfer 420.5 from alice to bob --fee 0.5 --burn-cap 0.257
Node is bootstrapped, ready for injecting operations. 
Estimated gas: 10100 units (will add 100 for safety)
Estimated storage: 257 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash: opXPyuAXx2yWoReQvWF6tra4aREqS2NgCBQNDtyo4rtZJAuizrM
Waiting for the operation to be included...
Operation found in block: BMRY5jRXjTtU2LeVoWFgSo9D7BU4eFqNB6UCSeZ6neWZhu5AXPt (pass: 3, offset: 0)
This sequence of operations was run:
Manager signed operations: 
From: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4
Fee to the baker: ꜩ0
Expected counter: 5807
Gas limit: 10000
Storage limit: 0 bytes
Revelation of manager public key:
Contract: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4
Key: edpkuS9a1795Cwwrstx6B6Q3Bugo3CKJ2NxtohxnjEaQGiJkWTD5YE
This revelation was successfully applied
Consumed gas: 10000
Manager signed operations: 
From: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4
Fee to the baker: ꜩ0.5
Expected counter: 5808
Gas limit: 10200
Storage limit: 277 bytes
Balance updates: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ... -ꜩ0.5 
fees(tz3NdTPb3Ax2rVW2Kq9QEdzfYFkRwhrQRPhX,14) ........... +ꜩ0.5
Transaction:
Amount: ꜩ420.5
From: tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4
To: tz1QCUKL2YZuGLSdTesXtVHJB8zRcG6XTz8f
This transaction was successfully applied
Consumed gas: 10100
Balance updates: 
tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ... -ꜩ420.5
tz1QCUKL2YZuGLSdTesXtVHJB8zRcG6XTz8f ... +ꜩ420.5
tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ... -ꜩ0.257
The operation has only been included 0 blocks ago. 
We recommend to wait more.
Use command tezos-client wait for opXPyuAXx2yWoReQvWF6tra4aREqS2NgCBQNDtyo4rtZJAuizrM to be included --confirmations 30 and/or an external block explorer.

This prints a nice receipt for us detailing exactly what happened to our tez.

Let’s take a look at bob, or tz1QCUKL2YZuGLSdTesXtVHJB8zRcG6XTz8f on tzScan:

But wait, why is it 420.5 tez? Didn't we pay a fee of 0.5 tez? Take a look at the balance update sections of the receipt again (I've added notes for which address is alice and which is bob)

... Balance updates: 
alice tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ....... -ꜩ0.5
fees(tz3NdTPb3Ax2rVW2Kq9QEdzfYFkRwhrQRPhX,14) .... +ꜩ0.5
...
Balance updates:
alice tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ... -ꜩ420.5
bob tz1QCUKL2YZuGLSdTesXtVHJB8zRcG6XTz8f ......+ꜩ420.5
alice tz1M1tuK4BM53S2niKvQDn6SbACiVk6zbjx4 ... -ꜩ0.257

The fees came out of alices account separately from the transaction. So the total amount alice payed was -ꜩ421.257

This concludes our chapter on basic Tezos operations. Next chapter, we’ll integrate what we’ve learned here with the Michelson we learned in chapter I to actually run a Michelson contract live on the Tezos blockchain!

Exercise 1: The Tezos client comes with a manual, which you can access with

$ ./alphanet.sh client man

This will by default output the manual formatted with colors. Use the manual to figure out what options to pass to the client man command to turn the colors off.

The Unix tools grep and less may be useful here, and there is also a copy on the web: http://tezos.gitlab.io/alphanet/api/cli-commands.html

Exercise 2: Go into the docker container shell (as we described earlier) and run

$ tezos-admin-client-p2p stat

Scroll to the section that says “Known Peers”.In your browser, open tzScan’s alphanet Network Stats page.

Find a peer ID that appears in both lists.

Exercise 3: Find the hashes of the Mainnet, Alphanet and Zeronet genesis blocks. How do they differ?

Exercise 4: Generate four new addresses in your client called eenie, meenie, meinie and mo. Conduct transfers to each address such that eenie has exactly 1 tez, meenie has exactly 2 tez, meinie has exactly 3 and mo has exactly 4.


Originally published at gist.github.com on December 17, 2018.