Docs: new documentation structure using Sphinx/RST
- Provides a toplevel documentation structure using Sphinx - Adds a `doc-html` target to the main Makefile - Converts existing documentation to RST format - Add some new documentation / tutorials - Links the developer manual and OCaml documentation - Synchronized documentation on Gitlab pages This patch is co-authored by: - Benjamin Canou <benjamin@canou.fr> - Bruno Bernardo <bernardobruno@gmail.com> - Pietro Abate <pietro.abate@inria.fr>
This commit is contained in:
parent
64c65558d8
commit
adf860ea40
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,7 +1,10 @@
|
||||
|
||||
.DS_Store
|
||||
__pycache__
|
||||
|
||||
/_build
|
||||
/docs/introduction/readme.rst
|
||||
|
||||
_build
|
||||
*.install
|
||||
|
||||
/tezos-node
|
||||
|
@ -458,3 +458,21 @@ deploy:bootstrap6:
|
||||
# - ./scripts/delete_ci_image.sh gitlab-ci-token "${CI_BUILD_TOKEN}"
|
||||
# "${CI_PROJECT_PATH}/build"
|
||||
# "${CI_BUILD_REF}"
|
||||
|
||||
pages:
|
||||
stage: publish
|
||||
image: ${CI_REGISTRY_IMAGE}/build:${CI_BUILD_REF}
|
||||
only:
|
||||
- master@tezos/tezos
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
script:
|
||||
- cd /home/opam/tezos
|
||||
- sudo apk update && sudo apk upgrade
|
||||
- sudo apk add py3-sphinx py3-sphinx_rtd_theme
|
||||
- sudo ln -s /usr/bin/sphinx-build-3 /usr/bin/sphinx-build
|
||||
- opam install odoc
|
||||
- make doc-html && sudo mv docs/_build "${CI_PROJECT_DIR}"/public
|
||||
dependencies:
|
||||
- build
|
||||
|
@ -1,6 +1,9 @@
|
||||
For the next reset
|
||||
Alphanet changelog
|
||||
==================
|
||||
|
||||
For the next reset
|
||||
------------------
|
||||
|
||||
[Alpha]
|
||||
|
||||
- Do not allow revealing the same endorsement twice.
|
||||
@ -34,7 +37,7 @@ For the next reset
|
||||
|
||||
|
||||
Reset 2017-11-20
|
||||
==================
|
||||
------------------
|
||||
|
||||
[Alphanet]
|
||||
|
||||
@ -104,7 +107,7 @@ Reset 2017-11-20
|
||||
- Add `SIZE` on lists.
|
||||
|
||||
Reset 2017-11-17
|
||||
================
|
||||
----------------
|
||||
|
||||
[Node]
|
||||
|
||||
@ -112,7 +115,7 @@ Reset 2017-11-17
|
||||
- Irmin: restore usage `git-repack`... (mistakenly removed)
|
||||
|
||||
Reset 2017-10-13
|
||||
================
|
||||
----------------
|
||||
|
||||
[Client]
|
||||
|
||||
@ -134,7 +137,7 @@ Reset 2017-10-13
|
||||
prevent an error raised when using an unrevealed key.
|
||||
|
||||
Reset 2017-09-21
|
||||
================
|
||||
----------------
|
||||
|
||||
[Node]
|
||||
|
||||
@ -156,7 +159,7 @@ Reset 2017-09-21
|
||||
prevent an error raised when using an unrevealed key.
|
||||
|
||||
Reset 2017-08-10
|
||||
================
|
||||
----------------
|
||||
|
||||
This update includes changes in the on-disk state of the node and in
|
||||
the format of blocks and operations. It thus requires a chain reset.
|
||||
|
5
Makefile
5
Makefile
@ -12,6 +12,10 @@ all:
|
||||
|
||||
doc-html:
|
||||
@jbuilder build @doc ${DEV}
|
||||
@mkdir -p $$(pwd)/docs/_build/api/odoc
|
||||
@rm -rf $$(pwd)/docs/_build/api/odoc/*
|
||||
@cp -r $$(pwd)/_build/default/_doc/* $$(pwd)/docs/_build/api/odoc/
|
||||
@make -C docs
|
||||
|
||||
build-test:
|
||||
@jbuilder build @buildtest ${DEV}
|
||||
@ -28,5 +32,6 @@ docker-image:
|
||||
clean:
|
||||
@-jbuilder clean
|
||||
@-rm -f tezos-node tezos-client tezos-protocol-compiler
|
||||
@-make -C docs clean
|
||||
|
||||
.PHONY: all test build-deps docker-image clean
|
||||
|
413
README.md
413
README.md
@ -1,413 +0,0 @@
|
||||
TEZOS
|
||||
=====
|
||||
|
||||
Tezos is a distributed consensus platform with meta-consensus capability. Tezos
|
||||
not only comes to consensus about state, like BTC or ETH. It also comes to
|
||||
consensus about how the protocol and the nodes should adapt and upgrade.
|
||||
|
||||
See https://www.tezos.com/ for more information about the project.
|
||||
|
||||
|
||||
Build instructions
|
||||
------------------
|
||||
|
||||
To compile Tezos, you need an OCaml compiler (version 4.04.2) and all the
|
||||
libraries listed in the various `tezos-*.opam` files.
|
||||
|
||||
The best way to install all dependencies is by first installing
|
||||
[OPAM](https://opam.ocaml.org/), the OCaml package manager.
|
||||
|
||||
Then, you need to create a new switch alias for Tezos. A switch is
|
||||
your own version of the OPAM configuration, including the OCaml
|
||||
compiler, all packages, and package manager configuration related to
|
||||
your project. This is necessary so that the project doesn't conflict
|
||||
with other OCaml projects or other versions of Tezos.
|
||||
|
||||
```
|
||||
opam update
|
||||
opam switch "tezos" --alias-of 4.04.2
|
||||
```
|
||||
|
||||
Note that if you previously created a switch named `tezos` but with an
|
||||
older OCaml version you need to remove the switch with `opam switch
|
||||
remove "tezos"`.
|
||||
|
||||
When the switch is ready, you need to activate it:
|
||||
|
||||
```
|
||||
eval `opam config env`
|
||||
```
|
||||
|
||||
Install the libraries which Tezos depends on:
|
||||
|
||||
```
|
||||
make build-deps
|
||||
```
|
||||
|
||||
While building the dependencies, `opam` is able to handle correctly
|
||||
the OCaml libraries but it is not always able to handle all external C
|
||||
libraries we depend on. On most system, it is able to suggest a call
|
||||
to the system package manager but it currently does not handle version
|
||||
check. In particular, the `libsodium-dev` packages on Ubuntu is too
|
||||
old for building Tezos, we rely on version `1.0.11` at least.
|
||||
|
||||
At last, compile the project:
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
This should produce three binaries:
|
||||
|
||||
* `tezos-node`: the tezos daemon itself;
|
||||
* `tezos-client`: a minimal command-line client;
|
||||
* `tezos-protocol-compiler`: a protocol compiler used for developing new version of the economic protocol.
|
||||
|
||||
Currently Tezos is being developed for Linux only. It should work on mac OS,
|
||||
but it has not been tested recently. A Windows port is in progress.
|
||||
|
||||
|
||||
Note that, when executing `make build-deps`, OPAM will detect if
|
||||
required system dependencies are installed. However, it is not able to
|
||||
detect which versions you actually have. Typically, `make` will
|
||||
probably fail if you have an libsodium < 1.0.11. In this case, make
|
||||
sure you have a recent version of libsodium and libsodium-dev, or
|
||||
download and install them from, eg,
|
||||
https://pkgs.org/download/libsodium18 and
|
||||
https://pkgs.org/download/libsodium-dev
|
||||
|
||||
Running the node
|
||||
----------------
|
||||
|
||||
So far there is no official Tezos network being run, but you might run a local
|
||||
test network (the development team is running its own testnet, if you're interested
|
||||
in joining this network, please make a request on our slack channel. We have
|
||||
limited support abilities at the moment but we'll try to help you best we can).
|
||||
|
||||
Use the following command to run a node that will accept incoming
|
||||
connections:
|
||||
|
||||
```
|
||||
./tezos-node identity generate 24.
|
||||
```
|
||||
|
||||
This will first generate a new node identity and compute the
|
||||
associated stamp of proof-of-work. Then, the node will listen to
|
||||
connections coming in on `[::]:9732`. All used data is stored at
|
||||
`$HOME/.tezos-node/`. For example, the default configuration file is
|
||||
at `$HOME/.tezos-node/config.json`.
|
||||
|
||||
To run multiple nodes on the same machine, you can duplicate and edit
|
||||
`$HOME/.tezos-node/config.json` while making sure they don't share paths to the
|
||||
database or any other data file (cf. options `db.store` ; `db.context` ;
|
||||
`db.protocol`, `net.peers-metadata` and `net.identity`).
|
||||
|
||||
You could also let Tezos generate a config file by specifying options on the
|
||||
command line. For instance, if `$dir/config.json` does not exist, the following
|
||||
command will generate it and replace the default values with the values from
|
||||
the command line:
|
||||
|
||||
```
|
||||
./tezos-node run --data-dir "$dir" --net-addr localhost:9733
|
||||
```
|
||||
|
||||
The Tezos server has a built-in mechanism to discover peers on the local
|
||||
network (using UDP packets broadcasted on port 7732).
|
||||
|
||||
If this mechanism is not sufficient, one can provide Tezos with a list of
|
||||
initial peers, either by editing the option `net.bootstrap-peers` in the
|
||||
`config.json` file, or by specifying a command line parameter:
|
||||
|
||||
```
|
||||
./tezos-node run \
|
||||
--data-dir "$dir" --net-addr localhost:2023 \
|
||||
--peer localhost:2021 --peer localhost:2022
|
||||
```
|
||||
|
||||
If `"$dir"/config.json` exists, the command line options override those
|
||||
read in the config file. By default, Tezos won't modify the content of an
|
||||
existing `"$dir"/config.json` file. But, you may explicit ask the node
|
||||
to reset or to update the file according to the command line parameters
|
||||
with the following commands line:
|
||||
|
||||
```
|
||||
./tezos-node config reset --data-dir "$dir" --net-addr localhost:9733
|
||||
./tezos-node config update --data-dir "$dir" --net-addr localhost:9734
|
||||
```
|
||||
|
||||
Running the node in a sandbox
|
||||
-----------------------------
|
||||
|
||||
To run a 'localhost-only' instance of a Tezos network, we provide two
|
||||
helper scripts:
|
||||
|
||||
- `./bin_node/tezos-sandboxed-node.sh`
|
||||
- `./bin_client/tezos-init-sandboxed-client.sh`
|
||||
|
||||
For instance, if you want to run local network with two nodes, in a
|
||||
first terminal, the following command will initialize a node listening
|
||||
for peers on port `19731` and listening for RPC on port `18731`.
|
||||
|
||||
```
|
||||
./bin_node/tezos-sandboxed-node.sh 1
|
||||
```
|
||||
|
||||
This node will store its data in a temporary directory which will be
|
||||
removed when the node is killed.
|
||||
|
||||
To launch the second node, just run the following command, it will
|
||||
listen on port `19739` and `18739`:
|
||||
|
||||
```
|
||||
./bin_node/tezos-sandboxed-node.sh 9
|
||||
```
|
||||
|
||||
You might replace `1` or `9` by any number in between if you want to
|
||||
run more than two nodes. But, if you intend to run a single node
|
||||
network, you might remove the spurious "Too few connections" warnings
|
||||
by lowering the number of expected connection, by running the
|
||||
following command instead:
|
||||
|
||||
```
|
||||
./bin_node/tezos-sandboxed-node.sh 1 --connections 0
|
||||
```
|
||||
|
||||
Once your node(s) is/are running, open a new terminal and initialize
|
||||
the "sandboxed" client data:
|
||||
|
||||
```
|
||||
eval `./bin_client/tezos-init-sandboxed-client.sh 1`
|
||||
```
|
||||
|
||||
It will initialize the client data in a temporary directory. It will
|
||||
also defines in the current shell session an alias `tezos-client`
|
||||
preconfigured for communicating the same-numbered node. For instance:
|
||||
|
||||
```
|
||||
$ tezos-client rpc call blocks/head/hash
|
||||
{ "hash": "BLockGenesisGenesisGenesisGenesisGenesisGeneskvg68z" }
|
||||
```
|
||||
|
||||
When you bootstrap a new network, the network is initialized with a
|
||||
dummy economic protocol, called "genesis". If you want to run the same
|
||||
protocol than the alphanet, `init-sandboxed-client` also defines an
|
||||
alias `tezos-activate-alpha`, that you need to execute once for
|
||||
activating the whole network. For instance:
|
||||
|
||||
```
|
||||
$ tezos-client rpc call blocks/head/protocol
|
||||
{ "protocol": "ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im" }
|
||||
$ tezos-activate-alpha
|
||||
Injected BMBcK869jaHQDc
|
||||
$ tezos-client rpc call blocks/head/protocol
|
||||
{ "protocol": "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK" }
|
||||
```
|
||||
|
||||
Configuration options
|
||||
---------------------
|
||||
|
||||
Here is an example configuration file with all parameters
|
||||
specified. Most of the time it uses default values, except for cases
|
||||
where the default is not explanatory enough (i.e. "bootstrap-peers" is
|
||||
an empty list by default). Comments are not allowed in JSON, so this
|
||||
configuration file would not parse. They are just provided here to
|
||||
help writing your own configuration file if needed.
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
|
||||
/* Location of the data dir on disk. */
|
||||
|
||||
"data-dir": "/home/tezos/my_data_dir"
|
||||
|
||||
/* Configuration of net parameters */
|
||||
|
||||
"net": {
|
||||
|
||||
/* Floating point number between 0 and 256 that represents a
|
||||
difficulty, 24 signifies for example that at least 24 leading
|
||||
zeroes are expected in the hash. */
|
||||
|
||||
"expected-proof-of-work": 24.5,
|
||||
|
||||
/* List of hosts. Tezos can connect to both IPv6 and IPv4
|
||||
hosts. If the port is not specified, default port 9732 will be
|
||||
assumed. */
|
||||
|
||||
"bootstrap-peers": ["::1:10732", "::ffff:192.168.1.3:9733", "mynode.tezos.com"],
|
||||
|
||||
/* Specify if the network is closed or not. A closed network
|
||||
allows only peers listed in "bootstrap-peers". */
|
||||
|
||||
"closed": false,
|
||||
|
||||
/* Network limits */
|
||||
|
||||
"limits": {
|
||||
|
||||
/* Delay granted to a peer to perform authentication, in
|
||||
seconds. */
|
||||
|
||||
"authentication-timeout": 5,
|
||||
|
||||
/* Strict minimum number of connections (triggers an urgent
|
||||
maintenance). */
|
||||
|
||||
"min-connections": 50,
|
||||
|
||||
/* Targeted number of connections to reach when bootstraping /
|
||||
maintaining. */
|
||||
|
||||
"expected-connections": 100,
|
||||
|
||||
/* Maximum number of connections (exceeding peers are
|
||||
disconnected). */
|
||||
|
||||
"max-connections": 200,
|
||||
|
||||
/* Number above which pending incoming connections are
|
||||
immediately rejected. */
|
||||
|
||||
"backlog": 20,
|
||||
|
||||
/* Maximum allowed number of incoming connections that are
|
||||
pending authentication. */
|
||||
|
||||
"max-incoming-connections": 20,
|
||||
|
||||
/* Max download and upload speeds in KiB/s. */
|
||||
|
||||
"max-download-speed": 1024,
|
||||
"max-upload-speed": 1024,
|
||||
|
||||
/* Size of the buffer passed to read(2). */
|
||||
|
||||
"read-buffer-size": 16384,
|
||||
}
|
||||
},
|
||||
|
||||
/* Configuration of rpc parameters */
|
||||
|
||||
"rpc": {
|
||||
|
||||
/* Host to listen to. If the port is not specified, the default
|
||||
port 8732 will be assumed. */
|
||||
|
||||
"listen-addr": "localhost:8733",
|
||||
|
||||
/* Cross Origin Resource Sharing parameters, see
|
||||
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing. */
|
||||
|
||||
"cors-origin": [],
|
||||
"cors-headers": [],
|
||||
|
||||
/* Certificate and key files (necessary when TLS is used). */
|
||||
|
||||
"crt": "tezos-node.crt",
|
||||
"key": "tezos-node.key"
|
||||
},
|
||||
|
||||
/* Configuration of log parameters */
|
||||
|
||||
"log": {
|
||||
|
||||
/* Output for the logging function. Either "stdout", "stderr" or
|
||||
the name of a log file . */
|
||||
|
||||
"output": "tezos-node.log",
|
||||
|
||||
/* Verbosity level: one of 'fatal', 'error', 'warn', 'notice',
|
||||
'info', 'debug'. */
|
||||
|
||||
"level": "info",
|
||||
|
||||
/* Fine-grained logging instructions. Same format as described in
|
||||
`tezos-node run --help`, DEBUG section. In the example below,
|
||||
sections "net" and all sections starting by "client" will have
|
||||
their messages logged up to the debug level, whereas the rest of
|
||||
log sections will be logged up to the notice level. */
|
||||
|
||||
"rules": "client* -> debug, net -> debug, * -> notice",
|
||||
|
||||
/* Format for the log file, see
|
||||
http://ocsigen.org/lwt/dev/api/Lwt_log_core#2_Logtemplates. */
|
||||
|
||||
"template": "$(date) - $(section): $(message)"
|
||||
},
|
||||
|
||||
/* Configuration for the validator and mempool parameters */
|
||||
|
||||
"shell": {
|
||||
|
||||
/* The number of peers to synchronize with
|
||||
before declaring the node 'bootstrapped'. */
|
||||
|
||||
"bootstrap_threshold": 4
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
It is possible to set independant log levels for different logging
|
||||
sections in Tezos, as well as specifying an output file for
|
||||
logging. See the description of log parameters above as well as
|
||||
documentation under the DEBUG section diplayed by `tezos-node run
|
||||
--help'.
|
||||
|
||||
|
||||
JSON/RPC interface
|
||||
------------------
|
||||
|
||||
The Tezos node provides a JSON/RPC interface. Note that it is an RPC, and it is
|
||||
JSON based, but it does not follow the "JSON-RPC" protocol. It is not active by
|
||||
default and it must be explicitely activated with the `--rpc-addr` option.
|
||||
Typically, if you are not trying to run a local network and just want to
|
||||
explore the RPC, you would run:
|
||||
|
||||
```
|
||||
./tezos-node run --rpc-addr localhost
|
||||
```
|
||||
|
||||
The RPC interface is self-documented and the `tezos-client` executable is able
|
||||
to pretty-print the RPC API. For instance, to see the API provided by the Tezos
|
||||
Shell:
|
||||
|
||||
```
|
||||
./tezos-client rpc list
|
||||
```
|
||||
|
||||
To get API attached to the "genesis" block, including the remote procedures
|
||||
provided by the associated economic protocol version:
|
||||
|
||||
```
|
||||
./tezos-client rpc list /blocks/genesis/
|
||||
```
|
||||
|
||||
You might also want the JSON schema describing the expected input and output of
|
||||
a RPC. For instance:
|
||||
|
||||
```
|
||||
./tezos-client rpc schema /blocks/genesis/hash
|
||||
```
|
||||
|
||||
Note: you can get the same information, but as a raw JSON object, with a simple
|
||||
HTTP request:
|
||||
|
||||
```
|
||||
wget --post-data '{ "recursive": true }' -O - http://localhost:8732/describe
|
||||
wget --post-data '{ "recursive": true }' -O - http://localhost:8732/describe/blocks/genesis
|
||||
wget -O - http://localhost:8732/describe/blocks/genesis/hash
|
||||
```
|
||||
|
||||
|
||||
|
||||
The minimal CLI client
|
||||
----------------------
|
||||
|
||||
Work in progress.
|
||||
|
||||
See `./tezos-client -help` for available commands.
|
409
README.rst
Normal file
409
README.rst
Normal file
@ -0,0 +1,409 @@
|
||||
TEZOS
|
||||
=====
|
||||
|
||||
Tezos is a distributed consensus platform with meta-consensus
|
||||
capability. Tezos not only comes to consensus about state, like BTC or
|
||||
ETH. It also comes to consensus about how the protocol and the nodes
|
||||
should adapt and upgrade.
|
||||
|
||||
See https://www.tezos.com/ for more information about the project.
|
||||
|
||||
Build instructions
|
||||
------------------
|
||||
|
||||
To compile Tezos, you need an OCaml compiler (version 4.04.2) and all
|
||||
the libraries listed in the various ``tezos-*.opam`` files.
|
||||
|
||||
The best way to install all dependencies is by first installing
|
||||
`OPAM <https://opam.ocaml.org/>`__, the OCaml package manager.
|
||||
|
||||
Then, you need to create a new switch alias for Tezos. A switch is your
|
||||
own version of the OPAM configuration, including the OCaml compiler, all
|
||||
packages, and package manager configuration related to your project.
|
||||
This is necessary so that the project doesn’t conflict with other OCaml
|
||||
projects or other versions of Tezos.
|
||||
|
||||
::
|
||||
|
||||
opam update
|
||||
opam switch "tezos" --alias-of 4.04.2
|
||||
|
||||
Note that if you previously created a switch named ``tezos`` but with an
|
||||
older OCaml version you need to remove the switch with
|
||||
``opam switch remove "tezos"``.
|
||||
|
||||
When the switch is ready, you need to activate it:
|
||||
|
||||
::
|
||||
|
||||
eval `opam config env`
|
||||
|
||||
Install the libraries which Tezos depends on:
|
||||
|
||||
::
|
||||
|
||||
make build-deps
|
||||
|
||||
While building the dependencies, ``opam`` is able to handle correctly
|
||||
the OCaml libraries but it is not always able to handle all external C
|
||||
libraries we depend on. On most system, it is able to suggest a call to
|
||||
the system package manager but it currently does not handle version
|
||||
check. In particular, the ``libsodium-dev`` packages on Ubuntu is too
|
||||
old for building Tezos, we rely on version ``1.0.11`` at least.
|
||||
|
||||
At last, compile the project:
|
||||
|
||||
::
|
||||
|
||||
make
|
||||
|
||||
This should produce three binaries:
|
||||
|
||||
- ``tezos-node``: the tezos daemon itself;
|
||||
- ``tezos-client``: a minimal command-line client;
|
||||
- ``tezos-protocol-compiler``: a protocol compiler used for developing
|
||||
new version of the economic protocol.
|
||||
|
||||
Currently Tezos is being developed for Linux only. It should work on mac
|
||||
OS, but it has not been tested recently. A Windows port is in progress.
|
||||
|
||||
Note that, when executing ``make build-deps``, OPAM will detect if
|
||||
required system dependencies are installed. However, it is not able to
|
||||
detect which versions you actually have. Typically, ``make`` will
|
||||
probably fail if you have an libsodium < 1.0.11. In this case, make sure
|
||||
you have a recent version of libsodium and libsodium-dev, or download
|
||||
and install them from, eg, https://pkgs.org/download/libsodium18 and
|
||||
https://pkgs.org/download/libsodium-dev
|
||||
|
||||
Running the node
|
||||
----------------
|
||||
|
||||
So far there is no official Tezos network being run, but you might run a
|
||||
local test network (the development team is running its own testnet, if
|
||||
you’re interested in joining this network, please make a request on our
|
||||
slack channel. We have limited support abilities at the moment but we’ll
|
||||
try to help you best we can).
|
||||
|
||||
Use the following command to run a node that will accept incoming
|
||||
connections:
|
||||
|
||||
::
|
||||
|
||||
./tezos-node identity generate 24.
|
||||
|
||||
This will first generate a new node identity and compute the associated
|
||||
stamp of proof-of-work. Then, the node will listen to connections coming
|
||||
in on ``[::]:9732``. All used data is stored at ``$HOME/.tezos-node/``.
|
||||
For example, the default configuration file is at
|
||||
``$HOME/.tezos-node/config.json``.
|
||||
|
||||
To run multiple nodes on the same machine, you can duplicate and edit
|
||||
``$HOME/.tezos-node/config.json`` while making sure they don’t share
|
||||
paths to the database or any other data file (cf. options ``db.store`` ;
|
||||
``db.context`` ; ``db.protocol``, ``net.peers-metadata`` and
|
||||
``net.identity``).
|
||||
|
||||
You could also let Tezos generate a config file by specifying options on
|
||||
the command line. For instance, if ``$dir/config.json`` does not exist,
|
||||
the following command will generate it and replace the default values
|
||||
with the values from the command line:
|
||||
|
||||
::
|
||||
|
||||
./tezos-node run --data-dir "$dir" --net-addr localhost:9733
|
||||
|
||||
The Tezos server has a built-in mechanism to discover peers on the local
|
||||
network (using UDP packets broadcasted on port 7732).
|
||||
|
||||
If this mechanism is not sufficient, one can provide Tezos with a list
|
||||
of initial peers, either by editing the option ``net.bootstrap-peers``
|
||||
in the ``config.json`` file, or by specifying a command line parameter:
|
||||
|
||||
::
|
||||
|
||||
./tezos-node run \
|
||||
--data-dir "$dir" --net-addr localhost:2023 \
|
||||
--peer localhost:2021 --peer localhost:2022
|
||||
|
||||
If ``"$dir"/config.json`` exists, the command line options override
|
||||
those read in the config file. By default, Tezos won’t modify the
|
||||
content of an existing ``"$dir"/config.json`` file. But, you may
|
||||
explicit ask the node to reset or to update the file according to the
|
||||
command line parameters with the following commands line:
|
||||
|
||||
::
|
||||
|
||||
./tezos-node config reset --data-dir "$dir" --net-addr localhost:9733
|
||||
./tezos-node config update --data-dir "$dir" --net-addr localhost:9734
|
||||
|
||||
Running the node in a sandbox
|
||||
-----------------------------
|
||||
|
||||
To run a ‘localhost-only’ instance of a Tezos network, we provide two
|
||||
helper scripts:
|
||||
|
||||
- ``./bin_node/tezos-sandboxed-node.sh``
|
||||
- ``./bin_client/tezos-init-sandboxed-client.sh``
|
||||
|
||||
For instance, if you want to run local network with two nodes, in a
|
||||
first terminal, the following command will initialize a node listening
|
||||
for peers on port ``19731`` and listening for RPC on port ``18731``.
|
||||
|
||||
::
|
||||
|
||||
./bin_node/tezos-sandboxed-node.sh 1
|
||||
|
||||
This node will store its data in a temporary directory which will be
|
||||
removed when the node is killed.
|
||||
|
||||
To launch the second node, just run the following command, it will
|
||||
listen on port ``19739`` and ``18739``:
|
||||
|
||||
::
|
||||
|
||||
./bin_node/tezos-sandboxed-node.sh 9
|
||||
|
||||
You might replace ``1`` or ``9`` by any number in between if you want to
|
||||
run more than two nodes. But, if you intend to run a single node
|
||||
network, you might remove the spurious “Too few connections” warnings by
|
||||
lowering the number of expected connection, by running the following
|
||||
command instead:
|
||||
|
||||
::
|
||||
|
||||
./bin_node/tezos-sandboxed-node.sh 1 --connections 0
|
||||
|
||||
Once your node(s) is/are running, open a new terminal and initialize the
|
||||
“sandboxed” client data:
|
||||
|
||||
::
|
||||
|
||||
eval `./bin_client/tezos-init-sandboxed-client.sh 1`
|
||||
|
||||
It will initialize the client data in a temporary directory. It will
|
||||
also defines in the current shell session an alias ``tezos-client``
|
||||
preconfigured for communicating the same-numbered node. For instance:
|
||||
|
||||
::
|
||||
|
||||
$ tezos-client rpc call blocks/head/hash
|
||||
{ "hash": "BLockGenesisGenesisGenesisGenesisGenesisGeneskvg68z" }
|
||||
|
||||
When you bootstrap a new network, the network is initialized with a
|
||||
dummy economic protocol, called “genesis”. If you want to run the same
|
||||
protocol than the alphanet, ``init-sandboxed-client`` also defines an
|
||||
alias ``tezos-activate-alpha``, that you need to execute once for
|
||||
activating the whole network. For instance:
|
||||
|
||||
::
|
||||
|
||||
$ tezos-client rpc call blocks/head/protocol
|
||||
{ "protocol": "ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im" }
|
||||
$ tezos-activate-alpha
|
||||
Injected BMBcK869jaHQDc
|
||||
$ tezos-client rpc call blocks/head/protocol
|
||||
{ "protocol": "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK" }
|
||||
|
||||
Configuration options
|
||||
---------------------
|
||||
|
||||
Here is an example configuration file with all parameters specified.
|
||||
Most of the time it uses default values, except for cases where the
|
||||
default is not explanatory enough (i.e. “bootstrap-peers” is an empty
|
||||
list by default). Comments are not allowed in JSON, so this
|
||||
configuration file would not parse. They are just provided here to help
|
||||
writing your own configuration file if needed.
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
|
||||
/* Location of the data dir on disk. */
|
||||
|
||||
"data-dir": "/home/tezos/my_data_dir"
|
||||
|
||||
/* Configuration of net parameters */
|
||||
|
||||
"net": {
|
||||
|
||||
/* Floating point number between 0 and 256 that represents a
|
||||
difficulty, 24 signifies for example that at least 24 leading
|
||||
zeroes are expected in the hash. */
|
||||
|
||||
"expected-proof-of-work": 24.5,
|
||||
|
||||
/* List of hosts. Tezos can connect to both IPv6 and IPv4
|
||||
hosts. If the port is not specified, default port 9732 will be
|
||||
assumed. */
|
||||
|
||||
"bootstrap-peers": ["::1:10732", "::ffff:192.168.1.3:9733", "mynode.tezos.com"],
|
||||
|
||||
/* Specify if the network is closed or not. A closed network
|
||||
allows only peers listed in "bootstrap-peers". */
|
||||
|
||||
"closed": false,
|
||||
|
||||
/* Network limits */
|
||||
|
||||
"limits": {
|
||||
|
||||
/* Delay granted to a peer to perform authentication, in
|
||||
seconds. */
|
||||
|
||||
"authentication-timeout": 5,
|
||||
|
||||
/* Strict minimum number of connections (triggers an urgent
|
||||
maintenance). */
|
||||
|
||||
"min-connections": 50,
|
||||
|
||||
/* Targeted number of connections to reach when bootstraping /
|
||||
maintaining. */
|
||||
|
||||
"expected-connections": 100,
|
||||
|
||||
/* Maximum number of connections (exceeding peers are
|
||||
disconnected). */
|
||||
|
||||
"max-connections": 200,
|
||||
|
||||
/* Number above which pending incoming connections are
|
||||
immediately rejected. */
|
||||
|
||||
"backlog": 20,
|
||||
|
||||
/* Maximum allowed number of incoming connections that are
|
||||
pending authentication. */
|
||||
|
||||
"max-incoming-connections": 20,
|
||||
|
||||
/* Max download and upload speeds in KiB/s. */
|
||||
|
||||
"max-download-speed": 1024,
|
||||
"max-upload-speed": 1024,
|
||||
|
||||
/* Size of the buffer passed to read(2). */
|
||||
|
||||
"read-buffer-size": 16384,
|
||||
}
|
||||
},
|
||||
|
||||
/* Configuration of rpc parameters */
|
||||
|
||||
"rpc": {
|
||||
|
||||
/* Host to listen to. If the port is not specified, the default
|
||||
port 8732 will be assumed. */
|
||||
|
||||
"listen-addr": "localhost:8733",
|
||||
|
||||
/* Cross Origin Resource Sharing parameters, see
|
||||
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing. */
|
||||
|
||||
"cors-origin": [],
|
||||
"cors-headers": [],
|
||||
|
||||
/* Certificate and key files (necessary when TLS is used). */
|
||||
|
||||
"crt": "tezos-node.crt",
|
||||
"key": "tezos-node.key"
|
||||
},
|
||||
|
||||
/* Configuration of log parameters */
|
||||
|
||||
"log": {
|
||||
|
||||
/* Output for the logging function. Either "stdout", "stderr" or
|
||||
the name of a log file . */
|
||||
|
||||
"output": "tezos-node.log",
|
||||
|
||||
/* Verbosity level: one of 'fatal', 'error', 'warn', 'notice',
|
||||
'info', 'debug'. */
|
||||
|
||||
"level": "info",
|
||||
|
||||
/* Fine-grained logging instructions. Same format as described in
|
||||
`tezos-node run --help`, DEBUG section. In the example below,
|
||||
sections "net" and all sections starting by "client" will have
|
||||
their messages logged up to the debug level, whereas the rest of
|
||||
log sections will be logged up to the notice level. */
|
||||
|
||||
"rules": "client* -> debug, net -> debug, * -> notice",
|
||||
|
||||
/* Format for the log file, see
|
||||
http://ocsigen.org/lwt/dev/api/Lwt_log_core#2_Logtemplates. */
|
||||
|
||||
"template": "$(date) - $(section): $(message)"
|
||||
},
|
||||
|
||||
/* Configuration for the validator and mempool parameters */
|
||||
|
||||
"shell": {
|
||||
|
||||
/* The number of peers to synchronize with
|
||||
before declaring the node 'bootstrapped'. */
|
||||
|
||||
"bootstrap_threshold": 4
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
It is possible to set independant log levels for different logging
|
||||
sections in Tezos, as well as specifying an output file for logging. See
|
||||
the description of log parameters above as well as documentation under
|
||||
the DEBUG section diplayed by \`tezos-node run –help’.
|
||||
|
||||
JSON/RPC interface
|
||||
------------------
|
||||
|
||||
The Tezos node provides a JSON/RPC interface. Note that it is an RPC,
|
||||
and it is JSON based, but it does not follow the “JSON-RPC” protocol. It
|
||||
is not active by default and it must be explicitely activated with the
|
||||
``--rpc-addr`` option. Typically, if you are not trying to run a local
|
||||
network and just want to explore the RPC, you would run:
|
||||
|
||||
::
|
||||
|
||||
./tezos-node run --rpc-addr localhost
|
||||
|
||||
The RPC interface is self-documented and the ``tezos-client`` executable
|
||||
is able to pretty-print the RPC API. For instance, to see the API
|
||||
provided by the Tezos Shell:
|
||||
|
||||
::
|
||||
|
||||
./tezos-client rpc list
|
||||
|
||||
To get API attached to the “genesis” block, including the remote
|
||||
procedures provided by the associated economic protocol version:
|
||||
|
||||
::
|
||||
|
||||
./tezos-client rpc list /blocks/genesis/
|
||||
|
||||
You might also want the JSON schema describing the expected input and
|
||||
output of a RPC. For instance:
|
||||
|
||||
::
|
||||
|
||||
./tezos-client rpc schema /blocks/genesis/hash
|
||||
|
||||
Note: you can get the same information, but as a raw JSON object, with a
|
||||
simple HTTP request:
|
||||
|
||||
::
|
||||
|
||||
wget --post-data '{ "recursive": true }' -O - http://localhost:8732/describe
|
||||
wget --post-data '{ "recursive": true }' -O - http://localhost:8732/describe/blocks/genesis
|
||||
wget -O - http://localhost:8732/describe/blocks/genesis/hash
|
||||
|
||||
The minimal CLI client
|
||||
----------------------
|
||||
|
||||
Work in progress.
|
||||
|
||||
See ``./tezos-client -help`` for available commands.
|
25
docs/Makefile
Normal file
25
docs/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS = -aE -n
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = Tezos
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
all: html linkcheck
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck "$(SOURCEDIR)" "$(BUILDDIR)"
|
||||
|
||||
introduction/readme.rst: ../README.rst
|
||||
sed 's/TEZOS/How to build and run/' $< > $@
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
html: Makefile introduction/readme.rst
|
||||
@$(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
|
||||
|
||||
clean:
|
||||
@-rm -Rf "$(BUILDDIR)"
|
||||
@-rm introduction/readme.rst
|
@ -1,321 +0,0 @@
|
||||
Tezos (alphanet)
|
||||
================
|
||||
|
||||
Welcome to the Tezos alphanet, which is a pre-release network for the
|
||||
Tezos blockchain. Currently, the chain is reset every few weeks.
|
||||
|
||||
For news and support about the alphanet, please join IRC (`#tezos` on
|
||||
freenode). Please, report bugs related to the alphanet on the IRC
|
||||
channel before filling Github issues.
|
||||
|
||||
For more information about the project in general, see:
|
||||
|
||||
https://www.tezos.com/
|
||||
|
||||
|
||||
How to join the alphanet ?
|
||||
==========================
|
||||
|
||||
We provide two ways of joining the alphanet :
|
||||
|
||||
- use `docker` and prebuilt binaries
|
||||
(recommended way, tested on windows/mac/linux)
|
||||
- manual compilation and installation
|
||||
(linux and mac only)
|
||||
|
||||
The `alphanet.sh` script
|
||||
------------------------
|
||||
|
||||
The recommended way for running an up-to-date Tezos node connected to
|
||||
the alphanet is to use `scripts/alphanet.sh`. Its only requirement is a
|
||||
working installation of Docker:
|
||||
|
||||
https://www.docker.com/community-edition
|
||||
|
||||
First, you need to download the script:
|
||||
|
||||
```
|
||||
wget https://raw.githubusercontent.com/tezos/tezos/alphanet/scripts/alphanet.sh
|
||||
chmod +x alphanet.sh
|
||||
```
|
||||
|
||||
You are now one step away from a working node:
|
||||
|
||||
```
|
||||
./alphanet.sh start
|
||||
```
|
||||
|
||||
This will launch a docker container running the various daemons that
|
||||
form a working tezos node. The first launch might take a few minutes
|
||||
to synchronize the chain.
|
||||
|
||||
On first launch the script will also create a cryptographic identity
|
||||
(nicknamed `my_identity`) and provide you with free tezzies on a fresh
|
||||
account (nicknamed `my_account`). You might check your balance with:
|
||||
|
||||
```
|
||||
./alphanet.sh client get balance for my_account
|
||||
```
|
||||
|
||||
On some circumstances the account creation might fail. If so, see
|
||||
section "Known issues" below on how to force the account creation.
|
||||
|
||||
See `./alphanet.sh --help` for more informations about the script.
|
||||
In particular see `./alphanet.sh client --help` and
|
||||
`scripts/README.master` for more information about the client.
|
||||
|
||||
Every call to `alphanet.sh` will check for updates of the node and
|
||||
will fail if your node is not up-to-date. For updating the node,
|
||||
simply run:
|
||||
|
||||
```
|
||||
./alphanet.sh restart
|
||||
```
|
||||
|
||||
If you prefer to temporarily disable automatic updates, you just
|
||||
have to set an environment variable:
|
||||
|
||||
```
|
||||
export TEZOS_ALPHANET_DO_NOT_PULL=yes
|
||||
```
|
||||
|
||||
|
||||
Compilation from sources
|
||||
------------------------
|
||||
|
||||
The `alphanet` branch in the tezos git repository will always contain
|
||||
the up-to-date sources of the tezos-node required for running the
|
||||
alphanet. See `docs/README.master` on how to compile it.
|
||||
|
||||
Once built, you might launch the a node by running:
|
||||
|
||||
```
|
||||
./tezos-node identity generate 24.
|
||||
./tezos-node run --rpc-addr localhost
|
||||
```
|
||||
|
||||
By default this instance will store its data in `$HOME/.tezos-node`
|
||||
and will listen to incoming peers on port 9732. It will also listen
|
||||
to RPC requests on port 8732 (only from `localhost`). You might find
|
||||
more options by running `./tezos-node config --help`.
|
||||
|
||||
If you want to stake (see below for more details), you will also have
|
||||
to run:
|
||||
|
||||
```
|
||||
./tezos-client launch daemon
|
||||
```
|
||||
|
||||
That's all. For the rest of the document, to execute the example
|
||||
commands, you will have to replace `./alphanet.sh client` by
|
||||
`./tezos-client`.
|
||||
|
||||
|
||||
How to observe the network ?
|
||||
============================
|
||||
|
||||
The alphanet script provides a basic command `./alhpanet.sh head` that
|
||||
allows you to see if your own node is synchronized.
|
||||
|
||||
The Tezos client also offers a lot of commands to introspect the state of
|
||||
the node, and also to list and call the RPCs of the nodes.
|
||||
|
||||
Enthusiastic Tezos adopter fredcy has also developed a nice block explorer
|
||||
for the alphanet. See [https://github.com/fredcy/tezos-client].
|
||||
|
||||
In an upcoming version, we will also provide an opt-in tool for node runners
|
||||
that will allow us to provide a global monitoring panel of the alphanet.
|
||||
|
||||
|
||||
How to obtain free Tez from the faucet contract ?
|
||||
=================================================
|
||||
|
||||
The alphanet contains an ad-hoc faucet contract, that will generate
|
||||
new tezzies for you to test. Obviously, this contract will not be
|
||||
available outside of the test network.
|
||||
|
||||
First, if you don't have any cryptographic identity yet, you need to
|
||||
generate one (replace `my_identity` with any name that suits you
|
||||
best):
|
||||
|
||||
```
|
||||
./alphanet.sh client gen keys "my_identity"
|
||||
```
|
||||
|
||||
Then, you have to generate a new "free" account (replace `my_account`
|
||||
with any name that suits you best and `my_identity` by the name used
|
||||
in the previous command):
|
||||
|
||||
```
|
||||
./alphanet.sh client originate free account "my_account" for "my_identity"
|
||||
```
|
||||
|
||||
That's all. You might check your balance:
|
||||
|
||||
```
|
||||
./alphanet.sh client get balance for "my_account"
|
||||
```
|
||||
|
||||
If you want MORE tezzies, you need to generate as many free accounts as
|
||||
you need (you should receive ꜩ100.000 per account) and then transfer
|
||||
the tezzies into a single account. For instance:
|
||||
|
||||
```
|
||||
./alphanet.sh client originate free account "my_alt_account" for "my_identity"
|
||||
./alphanet.sh client transfer 100,000.00 from "my_alt_account" to "my_account" -fee 0.00
|
||||
./alphanet.sh client forget contract "my_alt_account"
|
||||
```
|
||||
|
||||
Note that the test network is kind enough to accept transactions
|
||||
without fees...
|
||||
|
||||
|
||||
|
||||
How to play with smart-contracts ?
|
||||
==================================
|
||||
|
||||
An advanced documentation of the smart contract language is in
|
||||
|
||||
`/docs/language.md`
|
||||
|
||||
Some test contracts are in
|
||||
|
||||
`/tests/contracts/`
|
||||
|
||||
For details and examples, see:
|
||||
|
||||
http://www.michelson-lang.com/
|
||||
|
||||
|
||||
|
||||
How to stake on the alphanet ?
|
||||
==============================
|
||||
|
||||
By default, the faucet of the alphanet (the one behind `./alphanet.sh
|
||||
originate free account "my_account" for "my_identity"`) creates
|
||||
contracts which are managed by `my_identity` but whose staking rights
|
||||
are delegated to the baker of the block including the
|
||||
origination. That way we are sure that staking rights are attributed
|
||||
to an active baker.
|
||||
|
||||
But, nonetheless, you might claim your staking rights!
|
||||
|
||||
The following command returns the current delegate of a contract:
|
||||
|
||||
```
|
||||
./alphanet.sh client get delegate for "my_account"
|
||||
```
|
||||
|
||||
If it is one the following, it is indeed one of our "bootstrap"
|
||||
contracts!
|
||||
|
||||
- `tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc`
|
||||
- `tz1irovm9SKduvL3npv8kDM54PSWY5VJXoyz`
|
||||
- `tz1UsgSSdRwwhYrqq7iVp2jMbYvNsGbWTozp`
|
||||
- `tz1TwYbKYYJxw7AyubY4A9BUm2BMCPq7moaC`
|
||||
- `tz1QWft73Zhj5VSA1sCuEi9HhDDJqywE6BtC`
|
||||
|
||||
You might change the delegate of a contract with a single command:
|
||||
|
||||
```
|
||||
./alphanet.sh client set delegate for "my_account" to "my_identity"
|
||||
|
||||
```
|
||||
|
||||
You now have staking rights!
|
||||
|
||||
Well, almost.
|
||||
|
||||
You should wait.
|
||||
|
||||
A little bit.
|
||||
|
||||
At most two cycles. Which, on the alphanet is 128 blocks (something
|
||||
around 2 hours). On the mainnet, this will be between 2 weeks and a
|
||||
month.
|
||||
|
||||
|
||||
But, to enforce your right a last step is required. When baking or
|
||||
endorsing a block, a bond is taken out of the default account
|
||||
associated to the public key of the delegate. Hence, in order to
|
||||
stake, you must be provisioning for bond deposit.
|
||||
|
||||
```
|
||||
./alphanet.sh client transfer 50,000.00 from "my_account" to "my_identity"
|
||||
```
|
||||
|
||||
On the alphanet, a bond is ꜩ1000. Hence, with the previous command you
|
||||
provisioned 50 bonds. If you want more, see section "How to obtain
|
||||
free Tez from the faucet contract ?".
|
||||
|
||||
Now, you are settled. The `alphanet` docker image runs a baker daemon
|
||||
and a endorser daemon, by default for all your keys.
|
||||
|
||||
To know if you staked, just run:
|
||||
|
||||
```
|
||||
./alphanet.sh baker log
|
||||
./alphanet.sh endorser log
|
||||
```
|
||||
|
||||
You should see lines such as:
|
||||
|
||||
```
|
||||
Injected block BLxzbB7PBW1axq for bootstrap5 after BLSrg4dXzL2aqq (level 1381, slot 0, fitness 00::0000000000005441, operations 21)
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```
|
||||
Injected endorsement for block 'BLSrg4dXzL2aqq' (level 1381, slot 3, contract bootstrap5) 'oo524wKiEWBoPD'
|
||||
```
|
||||
|
||||
On the alphanet, rewards for staking are credited after 24 hours. The
|
||||
reward for baking or endorsing a block is ꜩ150. The safety bond is
|
||||
returned together with the reward.
|
||||
|
||||
To know when you will be allowed to stake in the current cycle, you
|
||||
might try the following RPCs, where you replaced `tz1iFY8ads...` by
|
||||
the appropriate value:
|
||||
|
||||
```
|
||||
$ ./alphanet.sh client list known identities
|
||||
my_identity: tz1iFY8aDskx9QGbgBy68SNAGgkc7AE2iG9H (public key known) (secret key known)
|
||||
$ ./alphanet.sh client rpc call /blocks/head/proto/helpers/rights/baking/delegate/tz1iFY8aDskx9QGbgBy68SNAGgkc7AE2iG9H with '{}'
|
||||
{ "ok":
|
||||
[ { "level": 1400.000000, "priority": 2.000000,
|
||||
"timestamp": "2017-05-19T03:21:52Z" },
|
||||
... ] }
|
||||
```
|
||||
|
||||
|
||||
Known issues
|
||||
============
|
||||
|
||||
Missing account `my_account`
|
||||
----------------------------
|
||||
|
||||
The chain synchronization has not been optimized yet and the
|
||||
`alphanet.sh` script might misdetect the end of the synchronization
|
||||
step. If so, it will try to create your free account in an outdated
|
||||
context and your new account will never be included in the chain.
|
||||
|
||||
To fix this, just wait for your node to be synchronized: for that run
|
||||
the following command, in the middle of a (raw) json object, it should
|
||||
display the date of the last block (which should not be too far in the
|
||||
past):
|
||||
|
||||
```
|
||||
./alphanet.sh head
|
||||
```
|
||||
|
||||
Please note that the printed date is GMT, don't forget the time shift.
|
||||
|
||||
Then, you need to remove from the client state the non-existant
|
||||
contract and regenerate a new one:
|
||||
|
||||
```
|
||||
./alphanet.sh client forget contract "my_account"
|
||||
./alphanet.sh client originate free account "my_account" for "my_identity"
|
||||
```
|
||||
|
40
docs/README.rst
Normal file
40
docs/README.rst
Normal file
@ -0,0 +1,40 @@
|
||||
******************************
|
||||
Building documentation locally
|
||||
******************************
|
||||
|
||||
Building instructions
|
||||
---------------------
|
||||
|
||||
To build the documentaion, you can use the main Makefile target ``doc-html``
|
||||
|
||||
.. code:: bash
|
||||
|
||||
make doc-html
|
||||
|
||||
The documentation is built by Sphinx, and uses the Read The Docs theme.
|
||||
|
||||
On a debian system, you can install the needed dependencies with:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
sudo apt install \
|
||||
python3-recommonmark \
|
||||
python3-sphinx \
|
||||
python3-sphinx-rtd-theme
|
||||
|
||||
OCaml documentation
|
||||
-------------------
|
||||
|
||||
Odoc is used for OCaml API generation, that you can install with:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
opam install odoc
|
||||
|
||||
Tezos generates the API documentation for all libraries in HTML format. The
|
||||
generated HTML pages in ``_build/<context>/_doc``. It creates one sub-directory
|
||||
per public library and generates an ``index.html`` file in each sub-directory.
|
||||
|
||||
The documentation is not installed on the system by Tezos. It is meant to be
|
||||
read locally while developing and then published on the www when releasing
|
||||
packages.
|
@ -1,10 +0,0 @@
|
||||
Tezos (zeronet)
|
||||
===============
|
||||
|
||||
The Tezos zeronet is a development network that might be broken
|
||||
multiple times a day.
|
||||
|
||||
If you wish to experiment with Tezos we recommend you to test the `alphanet`,
|
||||
which is a more stable test network.
|
||||
|
||||
In particular, we offer no support about the zeronet.
|
@ -1,65 +0,0 @@
|
||||
# Tezos Code Tutorial
|
||||
|
||||
## Introduction
|
||||
|
||||
The purpose of this document is to help contributors get started with the Tezos
|
||||
codebase. The code is organized in several layers in a way which largely reflects the philosophy of the project. It creates a very strict separation between the "node", which implements the network protocol described in the white paper and between the protocols themselves. Of course the seed protocol itself is a very important part of the Tezos project and it follows a similar organization. The economic protocol sits on top of a few layers of abstractions dealing primarily with storing and retrieving data from the current context.
|
||||
|
||||
## Overview of the source
|
||||
|
||||
This section presents a brief overview of the layout of the source files and their significance.
|
||||
|
||||
### node
|
||||
The network shell
|
||||
#### node/db
|
||||
Persistent data structures used by the shell to store its state.
|
||||
#### note/net
|
||||
Connectivity for the gossip network and primitives to create RPC services
|
||||
#### node/shell
|
||||
The shell itself
|
||||
#### node/updater
|
||||
Manage on-the-fly updates to the protocol
|
||||
|
||||
### proto
|
||||
This is where the protocols live
|
||||
#### proto/environment
|
||||
|
||||
#### proto/current
|
||||
|
||||
### utils
|
||||
|
||||
### compiler
|
||||
|
||||
### client
|
||||
### client/embedded
|
||||
|
||||
### Node, the network shell
|
||||
|
||||
### Storing the context
|
||||
|
||||
## Irmin
|
||||
|
||||
Tezos needs to store potentially different version of its context, corresponding to potentially different branches of the blockchain. This also implies the ability to roll back the changes made by any single block, and to make atomic changes to the structure on disk for eac block to avoid corruption.
|
||||
|
||||
To that extent, Tezos borrows from the MirageOS project a module called [Irmin](https://github.com/mirage/irmin "Irmin")
|
||||
|
||||
> Irmin is a library to persist and synchronize distributed data structures both on-disk and in-memory. It enables a style of programming very similar to the Git workflow, where distributed nodes fork, fetch, merge and push data between each other. The general idea is that you want every active node to get a local (partial) copy of a global database and always be very explicit about how and when data is shared and migrated.
|
||||
|
||||
Caveat: although Tezos **is** a distributed ledger, it does **not** rely on Irmin's distributed store capabilities. For our purposes we only use Irmin as a local storage. The git structure is particularly well suited to represent the versionning implicit in the state of a blockchain based ledger. In fact, the context of Tezos can be observed by running "git checkout" in the data directory.
|
||||
|
||||
## Netbits and Jsont
|
||||
|
||||
Netbits and Jsont are modules which allow for the typesafe serialization of OCaml objects in a binary format and in Json (respectively). Importantly, it does not make of the potentially brittle representation created by the compiler and access through the Obj.magic function. Serializers are created using a set of type constructors defined in a GADT.
|
||||
|
||||
|
||||
## (MISC STUFF TO BE ORGANIZED IN THE DOCUMENT)
|
||||
|
||||
The "Main" module represents the fixed interface between the economic protocol and the shell.
|
||||
|
||||
A protocol consists of several .ml and .mli files and a file name TEZOS_PROTOCOL which lists the modules in order of inclusion for the compilation. Lower level modules sit below high level modules.
|
||||
|
||||
|
||||
What are \\_repr modules?
|
||||
|
||||
These modules handle the low level representation of a type, in particular its serialization in Json and in binary format. A module based on this type will often exist on top of it and provide functionality around that type. This provides finely grained layers of encapsulation to mininize the visible interfaces.
|
||||
|
16
docs/_extensions/tezos_custom_roles.py
Normal file
16
docs/_extensions/tezos_custom_roles.py
Normal file
@ -0,0 +1,16 @@
|
||||
from docutils import nodes
|
||||
import os
|
||||
import os.path
|
||||
|
||||
def setup(app):
|
||||
app.add_role('package', package_role)
|
||||
|
||||
def package_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
rel_lvl = inliner.document.current_source.replace(os.getcwd(),'').count('/')
|
||||
if not os.path.exists('_build/api/odoc/'+text):
|
||||
raise ValueError('opam package ' + text + ' does not exist in the odoc')
|
||||
url = "api/api-inline.html#" + text + '/index.html'
|
||||
for i in range(1,rel_lvl):
|
||||
url = '../' + url
|
||||
node = nodes.reference(rawtext, text, refuri=url, **options)
|
||||
return [node], []
|
19
docs/api/api-inline.rst
Normal file
19
docs/api/api-inline.rst
Normal file
@ -0,0 +1,19 @@
|
||||
**************************
|
||||
Online OCaml Documentation
|
||||
**************************
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe id="docframe" height="100%" width="100%" src="../api/odoc/index.html"></iframe>
|
||||
<style>
|
||||
@media (max-width: 771px) {
|
||||
#docframe { border:none; position: fixed; top: 70px; bottom: 0; right: 0; left: 0; }
|
||||
}
|
||||
@media (min-width: 770px) {
|
||||
#docframe { border:none; position: fixed; top: 0px; bottom: 0; right: 0; left: 300px; }
|
||||
}
|
||||
</style>
|
||||
<script language="JavaScript">
|
||||
if (window.location.hash.endsWith(".html"))
|
||||
document.getElementById('docframe').src = "../api/odoc/" + window.location.hash.slice(1)
|
||||
</script>
|
175
docs/conf.py
Normal file
175
docs/conf.py
Normal file
@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Tezos documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Jan 17 18:04:32 2018.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('.') + '/_extensions')
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.extlinks', 'tezos_custom_roles']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Tezos'
|
||||
copyright = '2017, Dynamic Ledger Solutions, Inc. <contact@tezos.com>'
|
||||
author = 'Dynamic Ledger Solutions, Inc. <contact@tezos.com>'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# Deactivate syntax highlighting
|
||||
# - http://www.sphinx-doc.org/en/stable/markup/code.html#code-examples
|
||||
# - http://www.sphinx-doc.org/en/stable/config.html#confval-highlight_language
|
||||
highlight_language = 'none'
|
||||
# TODO write a Pygments lexer for Michelson
|
||||
# cf. http://pygments.org/docs/lexerdevelopment/ and http://pygments.org/docs/lexers/
|
||||
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
html_theme_options = {'logo_only': True}
|
||||
html_logo = "logo.svg"
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
# html_sidebars = {
|
||||
# '**': [
|
||||
# 'relations.html', # needs 'show_related': True theme option to display
|
||||
# 'searchbox.html',
|
||||
# ]
|
||||
# }
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Tezosdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'Tezos.tex', 'Tezos Documentation',
|
||||
'Dynamic Ledger Solutions, Inc. \\textless{}contact@tezos.com\\textgreater{}', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'tezos', 'Tezos Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'Tezos', 'Tezos Documentation',
|
||||
author, 'Tezos', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
@ -1,172 +0,0 @@
|
||||
* The data_encoding library
|
||||
Throughout the Tezos protocol, data is serialized so that it can be used via RPC,
|
||||
written to disk, or placed in a block. This serialization/deserialization is handled
|
||||
via the [[../lib_data_encoding/data_encoding.mli][data_encoding library]]
|
||||
by providing a set primitive encodings and a variety of combinators.
|
||||
|
||||
** Examples/Tutorial
|
||||
*** Encoding an integer
|
||||
|
||||
Integers are defined as other concrete data types with a generic encoding type =type 'a encoding=.
|
||||
This means that it is an encoding to/from type =int=. There are a variety of ways to encode an integer,
|
||||
depending on what binary serialization you want to achieve:
|
||||
- =Data_encoding.int8=
|
||||
- =Data_encoding.uint8=
|
||||
- =Data_encoding.int16=
|
||||
- =Data_encoding.uint16=
|
||||
- =Data_encoding.int31=
|
||||
- =Data_encoding.int32=
|
||||
- =Data_encoding.int64=
|
||||
|
||||
For example, an encoding that represents a 31 bit integer has type
|
||||
=Data_encoding.int31 = int Data_encoding.encoding=.
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
let int31_encoding = Data_encoding.int31
|
||||
#+END_SRC
|
||||
|
||||
|
||||
*** Encoding an object
|
||||
Encoding a single integer is fairly uninteresting. The Data_encoding library provides a number of
|
||||
combinators that can be used to build more complicated objects. Consider the type that represents an
|
||||
interval from the first number to the second:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
type interval = int64 * int64
|
||||
#+END_SRC
|
||||
|
||||
We can define an encoding for this type as:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
let interval_encoding =
|
||||
Data_encoding.(obj2 (req "min" int64) (req "max" int64))
|
||||
#+END_SRC
|
||||
|
||||
In the example above we construct a new value =interval_encoding= by combining
|
||||
two int64 integers using the =obj2= constructor.
|
||||
|
||||
The library provides diffrent constructors, i.e. for objects
|
||||
that have no data (=Data_encoding.empty=), constructors for object up to 10 fields,
|
||||
contructors for tuples, list, etc.
|
||||
|
||||
These are serialized to binary by converting each internal object to binary and
|
||||
placing them in the order of the original object and to JSON as a JSON object with field names.
|
||||
|
||||
*** Lists, arrays, and options
|
||||
List, Arrays and options types can by built on top of ground data types.
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
type interval_list = interval list
|
||||
|
||||
type interval_array = interval array
|
||||
|
||||
type interval_option = interval option
|
||||
#+END_SRC
|
||||
|
||||
And the encoders for these types as
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
let interval_list_encoding = Data_encoding.list interval_encoding
|
||||
let interval_array_encoding = Data_encoding.array interval_encoding
|
||||
let interval_option_encoding = Data_encoding.option interval_encoding
|
||||
#+END_SRC
|
||||
|
||||
*** Union types
|
||||
The Tezos codebase makes heavy use of variant types. Consider the following
|
||||
variant type:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
type variant = B of bool
|
||||
| S of string
|
||||
#+END_SRC
|
||||
|
||||
Encoding for this types can be expressed as:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
let variant_encoding =
|
||||
Data_encoding.(union ~tag_size:`Uint8
|
||||
[ case
|
||||
bool
|
||||
(function B b -> Some b | _ -> None)
|
||||
(fun b -> B b) ;
|
||||
case
|
||||
string
|
||||
(function S s -> Some s | _ -> None)
|
||||
(fun s -> S s) ])
|
||||
#+END_SRC
|
||||
|
||||
This variant encoding is a bit more complicated. Let's look at the parts of the type:
|
||||
- We include an optimization hint to the binary encoding to inform it of the number of elements we expect in the tag.
|
||||
In most cases, we can use =`Uint8=, which allows you to have up to 256 possible cases (default).
|
||||
- We provide a function to wrap the datatype. The encoding works by repeatedly trying to
|
||||
decode the datatype using these functions until one returns =Some payload=. This payload
|
||||
is then encoded using the data_encoding specified.
|
||||
- We specify a function from the encoded type to the actual datatype.
|
||||
|
||||
Since the library does not provide an exhaustivity check on these constructors,
|
||||
the user must be careful when constructucting unin types to avoid unfortunate runtime failures.
|
||||
|
||||
** How the Data_encoding module works
|
||||
|
||||
This section is 100% optional. You do not need to understand this section to use the library.
|
||||
|
||||
The library uses GADTs to provide type-safe serialization/deserialization. From there,
|
||||
a runtime representation of JSON objects is parsed into the typesafe version.
|
||||
|
||||
First we define an untyped JSON AST:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
type json =
|
||||
[ `O of (string * json) list
|
||||
| `Bool of bool
|
||||
| `Float of float
|
||||
| `A of json list
|
||||
| `Null
|
||||
| `String of string ]
|
||||
#+END_SRC
|
||||
|
||||
This is then parsed into a typed AST ( we eliminate several cases for clarity):
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
type 'a desc =
|
||||
| Null : unit desc
|
||||
| Empty : unit desc
|
||||
| Bool : bool desc
|
||||
| Int64 : Int64.t desc
|
||||
| Float : float desc
|
||||
| Bytes : Kind.length -> MBytes.t desc
|
||||
| String : Kind.length -> string desc
|
||||
| String_enum : Kind.length * (string * 'a) list -> 'a desc
|
||||
| Array : 'a t -> 'a array desc
|
||||
| List : 'a t -> 'a list desc
|
||||
| Obj : 'a field -> 'a desc
|
||||
| Objs : Kind.t * 'a t * 'b t -> ('a * 'b) desc
|
||||
| Tup : 'a t -> 'a desc
|
||||
| Union : Kind.t * tag_size * 'a case list -> 'a desc
|
||||
| Mu : Kind.enum * string * ('a t -> 'a t) -> 'a desc
|
||||
| Conv :
|
||||
{ proj : ('a -> 'b) ;
|
||||
inj : ('b -> 'a) ;
|
||||
encoding : 'b t ;
|
||||
schema : Json_schema.schema option } -> 'a desc
|
||||
| Describe :
|
||||
{ title : string option ;
|
||||
description : string option ;
|
||||
encoding : 'a t } -> 'a desc
|
||||
| Def : { name : string ;
|
||||
encoding : 'a t } -> 'a desc
|
||||
#+END_SRC
|
||||
|
||||
- The first set of constructures define all ground types.
|
||||
- The constructors for =Bytes=, =String= and =String_enum= includes a length fields in order to provide safe binary serialization.
|
||||
- The constructors for =Array= and =List= are used by the combinators we saw earlier.
|
||||
- The =Obj= and =Objs= constructors create JSON objects.
|
||||
These are wrapped in the =Conv= constructor to remove nesting that results when these constructors are used naively.
|
||||
- The =Mu= constructor is used to create self-referential definitions.
|
||||
- The =Conv= constructor allows you to clean up a nested definition or compute another type from an existing one.
|
||||
- The =Describe= and =Def= constructors are used to add documentation
|
||||
|
||||
The library also provides various wrappers and convenience functions to make constructing these objects easier.
|
||||
Reading the documentation in the [[../src/minutils/data_encoding.mli][mli file]] should orient
|
||||
you on how to use these functions and their purposes.
|
||||
|
@ -1,276 +0,0 @@
|
||||
* The Error Monad
|
||||
This has been adapted from a blog post on [[www.michelson-lang.com][michelson-lang.com]].
|
||||
|
||||
If you're not familiar with monads, go take a few minutes and read a tutorial. I personally got a lot out of this [[http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf][paper]] by Philip Wadler, but there are a ton of others available online. Find one that works for you. The error monad isn't terribly scary as Monads go, so once you feel like you understand the gist, come on back and see if you can understand what's going on.
|
||||
|
||||
I'm going to omit some convenience operations that a lot of monads provide in the examples below. If you want to add them, they're not difficult.
|
||||
|
||||
** Why you want the error monad
|
||||
In Tezos, we don't want to have the node be crashable by an improper input. To avoid this possibility, it was decided that the system should not use exceptions for error handling. Instead, it uses an error monad. This design forces errors to be handled or carried through before an output can be used. Exceptions are still occasionally used, but this is mostly in the client and only for internal errors.
|
||||
|
||||
We also mix in the Lwt library, which we use for concurrency. This is combined with the error monad and is once again used pervasively throughout the codebase. The Lwt monad is a lot like promises in other languages.
|
||||
|
||||
Without further ado, let's write an error monad.
|
||||
|
||||
** A simple version of the error monad
|
||||
Here's a very simple error monad.
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
module Error : sig
|
||||
type 'a t
|
||||
(* Create a value of type t *)
|
||||
val return : 'a -> 'a t
|
||||
(* For when a computation fails *)
|
||||
val error : 'a t
|
||||
(* Apply an operation to a value in the error monad *)
|
||||
val (>>?) : 'a t -> ('a -> 'b t) -> 'b t (* bind *)
|
||||
end = struct
|
||||
type 'a t = Ok of 'a | Error
|
||||
let return x = Ok x
|
||||
let error = Error
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error -> Error
|
||||
end
|
||||
#+END_SRC
|
||||
|
||||
So, is this what Tezos uses? We actually already have a lot of the structure that we'll use later. The basic idea is that you return a value that's correct and return an error if the operation failed. Outside of the error module, you can't actually introspect an error value. You can only dispatch on the correctness/incorrectness of the value using bind.
|
||||
|
||||
What's wrong here?
|
||||
- We can't report any information about an error case
|
||||
- We can't report error traces, something that's used to improve the quality of error messages throughout Tezos
|
||||
- We can't handle some errors and continue executing
|
||||
|
||||
** A slight improvement
|
||||
Let's now enhance our error reporting by allowing errors to contain a description string. Now we can report messages along with our errors. Is this enough of an improvement? Not really. We don't have any flexibility about how the printing works. We still can't create error traces and we can't handle errors and resume executing the program.
|
||||
#+BEGIN_SRC ocaml
|
||||
module Error : sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val error : string -> 'a t
|
||||
val (>>?) : 'a t -> ('a -> 'b t) -> 'b t (* bind *)
|
||||
val print_value : ('a -> string) -> 'a t -> unit
|
||||
end = struct
|
||||
type 'a t = Ok of 'a | Error of string
|
||||
let return x = Ok x
|
||||
let error s = Error s
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error s -> Error s
|
||||
let print_value func = function
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error s -> Printf.printf "Error: %s\n" s
|
||||
end
|
||||
#+END_SRC
|
||||
|
||||
** Traces
|
||||
Now that we have the basic structure down, we can add a mechanism to let us include traces. As a note, the error type I had above is exactly the =result= type from the OCaml standard library. The traces are just lists of error messages. If you have a call you think might fail, and you want to provide a series of errors, you can wrap that result in the =trace= function. If that call fails, an additional error is added.
|
||||
#+BEGIN_SRC ocaml
|
||||
module Error : sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val error : string -> 'a t
|
||||
val (>>?) : 'a t -> ('a -> 'b t) -> 'b t (* bind *)
|
||||
val print_value : ('a -> string) -> 'a t -> unit
|
||||
val trace : string -> 'a t -> 'a t
|
||||
end = struct
|
||||
type 'a t = ('a, string list) result
|
||||
let return x = Ok x
|
||||
let error s = Error [ s ]
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error errs -> Error errs
|
||||
let print_value func = function
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error [ s ] -> Printf.printf "Error: %s\n" s
|
||||
| Error errors -> Printf.printf "Errors:\t%s\n" (String.concat "\n\t" errors)
|
||||
let trace error = function
|
||||
| Ok x -> Ok x
|
||||
| Error errors -> Error (error :: errors)
|
||||
end
|
||||
#+END_SRC
|
||||
|
||||
** A more descriptive message
|
||||
Even though traces are nice, we really want to be able to store more interesting data in the messages. We're going to use an extensible variant type to do this. Extensible variants allow us to add a new case to a variant type at the cost of exhaustivity checking. We're going to need two new mechanisms to make this work well. The first is an error registration scheme. In the actual error monad, this involves the data encoding module, which is how all data is encoded/decoded in Tezos. This module is another decently complicated part of the codebase that should probably the subject of a future post. Since you can declare arbitrary new errors, we'll have a way of adding a printer for each error.
|
||||
|
||||
When we add a new error handler, we'll use the =register_handler= function. This function will take a function that takes an error and returns a =string option=. These functions will look something like this:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
type error += Explosion_failure of string * int;;
|
||||
|
||||
register_error
|
||||
(function
|
||||
| Explosion_failure (s, i) ->
|
||||
Some (Printf.sprintf "Everything exploded: %s at %d" s i)
|
||||
| _ -> None)
|
||||
#+END_SRC
|
||||
|
||||
I'm also renaming the =error= function to =fail=. This is the convention used by the actual Error_monad module. I'm also exposing the ='a t= type so that you can dispatch on it if you need to. This is used several times in the Tezos codebase.
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
module Error : sig
|
||||
type error = ..
|
||||
type 'a t = ('a, error list) result
|
||||
val return : 'a -> 'a t
|
||||
val fail : error -> 'a t
|
||||
val (>>?) : ('a -> 'b t) -> 'a t -> 'b t (* bind *)
|
||||
val print_value : ('a -> string) -> 'a t -> unit
|
||||
val trace : error -> 'a t -> 'a t
|
||||
end = struct
|
||||
type error = ..
|
||||
type 'a t = ('a, error list) result
|
||||
let fail error = Error [ error ]
|
||||
let return x = Ok x
|
||||
let (>>?) func = function
|
||||
| Ok x -> func x
|
||||
| Error errs -> Error errs
|
||||
let registered = ref []
|
||||
let register_error handler =
|
||||
registered := (handler :: !registered)
|
||||
let default_handler error =
|
||||
"Unregistered error: " ^ Obj.(extension_name @@ extension_constructor error)
|
||||
let to_string error =
|
||||
let rec find_handler = function
|
||||
| [] -> default_handler error
|
||||
| handler :: handlers ->
|
||||
begin match handler error with
|
||||
| None -> find_handler handlers
|
||||
| Some s -> s
|
||||
end
|
||||
in find_handler !registered
|
||||
let print_value func = function
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error [ s ] -> Printf.printf "Error: %s\n" (to_string s)
|
||||
| Error errors -> Printf.printf "Errors:\t%s\n" (String.concat "\n\t" (List.map to_string errors))
|
||||
let trace error = function
|
||||
| Ok x -> Ok x
|
||||
| Error errors -> Error (error :: errors)
|
||||
end
|
||||
#+END_SRC
|
||||
|
||||
** Putting =Lwt.t= in the mix
|
||||
Tezos uses the [[http://ocsigen.org/lwt/][Lwt library]] for threading. The Lwt monad is mixed in with the error monad module. This requires us to add some extra combinators and reexport some functions from Lwt.
|
||||
|
||||
I'm also renaming the type =t= to =tzresult=, as used in the Tezos codebase.
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
module Error : sig
|
||||
type error = ..
|
||||
type 'a tzresult = ('a, error list) result
|
||||
val ok : 'a -> 'a tzresult
|
||||
val return : 'a -> 'a tzresult Lwt.t
|
||||
val error : error -> 'a tzresult
|
||||
val fail : error -> 'a tzresult Lwt.t
|
||||
val (>>?) : 'a tzresult -> ('a -> 'b tzresult) -> 'b tzresult (* bind *)
|
||||
val (>>=?) : 'a tzresult Lwt.t -> ('a -> 'b tzresult Lwt.t) -> 'b tzresult Lwt.t
|
||||
val (>>=) : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
|
||||
val print_value : ('a -> string) -> 'a tzresult Lwt.t -> unit Lwt.t
|
||||
val trace : error -> 'a tzresult Lwt.t -> 'a tzresult Lwt.t
|
||||
end = struct
|
||||
type error = ..
|
||||
type 'a tzresult = ('a, error list) result
|
||||
let fail error = Lwt.return (Error [ error ])
|
||||
let error error = (Error [ error ])
|
||||
let ok x = Ok x
|
||||
let return x = Lwt.return (ok x)
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error errs -> Error errs
|
||||
let (>>=) = Lwt.bind
|
||||
let (>>=?) value func =
|
||||
value >>= function
|
||||
| Ok x -> func x
|
||||
| Error errs -> Lwt.return (Error errs)
|
||||
let registered = ref []
|
||||
let register_error handler =
|
||||
registered := (handler :: !registered)
|
||||
let default_handler error =
|
||||
"Unregistered error: " ^ Obj.(extension_name @@ extension_constructor error)
|
||||
let to_string error =
|
||||
let rec find_handler = function
|
||||
| [] -> default_handler error
|
||||
| handler :: handlers ->
|
||||
begin match handler error with
|
||||
| None -> find_handler handlers
|
||||
| Some s -> s
|
||||
end
|
||||
in find_handler !registered
|
||||
let print_value func value =
|
||||
value >>= fun value ->
|
||||
begin match value with
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error [ s ] -> Printf.printf "Error: %s\n" (to_string s)
|
||||
| Error errors -> Printf.printf "Errors:\t%s\n" (String.concat "\n\t" (List.map to_string errors))
|
||||
end; Lwt.return ()
|
||||
let trace error value =
|
||||
value >>= function
|
||||
| Ok x -> return x
|
||||
| Error errors -> Lwt.return (Error (error :: errors))
|
||||
end
|
||||
#+END_SRC
|
||||
** The actual Tezos error monad
|
||||
The actual Tezos error monad adds a few things. Firstly, there are three categories of errors:
|
||||
- =`Temporary= - An error resulting from an operation that might be valid in the future, for example, a contract's balance being too low to execute the intended operation. This can be fixed by adding more to the contract's balance.
|
||||
- =`Branch= - An error that occurs in one branch of the chain, but may not occur in a different one. For example, receiving an operation for an old or future protocol version.
|
||||
- =`Permanent= - An error that is not recoverable because the operation is never going to be valid. For example, an invalid ꜩ notation.
|
||||
|
||||
The registration scheme also uses data encodings. Here's an example from the [[file:~/tezos/src/node/shell/validator.ml][validator]]:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
register_error_kind
|
||||
`Permanent
|
||||
~id:"validator.wrong_level"
|
||||
~title:"Wrong level"
|
||||
~description:"The block level is not the expected one"
|
||||
~pp:(fun ppf (e, g) ->
|
||||
Format.fprintf ppf
|
||||
"The declared level %ld is not %ld" g e)
|
||||
Data_encoding.(obj2
|
||||
(req "expected" int32)
|
||||
(req "provided" int32))
|
||||
(function Wrong_level (e, g) -> Some (e, g) | _ -> None)
|
||||
(fun (e, g) -> Wrong_level (e, g))
|
||||
#+END_SRC
|
||||
|
||||
An error takes a category, id, title, description, and encoding. You must specify a function to take an error to an optional value of the encoding type and a function to take a value of the encoded type and create an error value. A pretty printer can optionally be specified, but may also be omitted.
|
||||
|
||||
|
||||
The actual error monad and it's tracing features can be seen in this function which parses contracts:
|
||||
|
||||
#+BEGIN_SRC ocaml
|
||||
let parse_script
|
||||
: ?type_logger: (int * (Script.expr list * Script.expr list) -> unit) ->
|
||||
context -> Script.storage -> Script.code -> ex_script tzresult Lwt.t
|
||||
= fun ?type_logger ctxt
|
||||
{ storage; storage_type = init_storage_type }
|
||||
{ code; arg_type; ret_type; storage_type } ->
|
||||
trace
|
||||
(Ill_formed_type (Some "parameter", arg_type))
|
||||
(Lwt.return (parse_ty arg_type)) >>=? fun (Ex_ty arg_type) ->
|
||||
trace
|
||||
(Ill_formed_type (Some "return", ret_type))
|
||||
(Lwt.return (parse_ty ret_type)) >>=? fun (Ex_ty ret_type) ->
|
||||
trace
|
||||
(Ill_formed_type (Some "initial storage", init_storage_type))
|
||||
(Lwt.return (parse_ty init_storage_type)) >>=? fun (Ex_ty init_storage_type) ->
|
||||
trace
|
||||
(Ill_formed_type (Some "storage", storage_type))
|
||||
(Lwt.return (parse_ty storage_type)) >>=? fun (Ex_ty storage_type) ->
|
||||
let arg_type_full = Pair_t (arg_type, storage_type) in
|
||||
let ret_type_full = Pair_t (ret_type, storage_type) in
|
||||
Lwt.return (ty_eq init_storage_type storage_type) >>=? fun (Eq _) ->
|
||||
trace
|
||||
(Ill_typed_data (None, storage, storage_type))
|
||||
(parse_data ?type_logger ctxt storage_type storage) >>=? fun storage ->
|
||||
trace
|
||||
(Ill_typed_contract (code, arg_type, ret_type, storage_type, []))
|
||||
(parse_returning (Toplevel { storage_type }) ctxt ?type_logger arg_type_full ret_type_full code)
|
||||
>>=? fun code ->
|
||||
return (Ex_script { code; arg_type; ret_type; storage; storage_type })
|
||||
#+END_SRC
|
||||
|
||||
Each specific type error from the typechecking process is wrapped in a more general error that explains which part of the program was malformed. This improves the error reporting. You can also see the bind operator used between functions to continue only if an error does not occur. This function also operates in the =Lwt= monad, which is largely hidden via the error monad.
|
53
docs/index.rst
Normal file
53
docs/index.rst
Normal file
@ -0,0 +1,53 @@
|
||||
.. Tezos documentation master file, created by
|
||||
sphinx-quickstart on Sat Nov 11 11:08:48 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Tezos's documentation!
|
||||
=================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Introduction:
|
||||
|
||||
introduction/readme
|
||||
introduction/contributing
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: The Alphanet:
|
||||
|
||||
introduction/alphanet
|
||||
introduction/alphanet_changes
|
||||
introduction/zeronet
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: White doc:
|
||||
|
||||
whitedoc/the_big_picture
|
||||
whitedoc/michelson
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Tutorials:
|
||||
|
||||
tutorials/data_encoding
|
||||
tutorials/error_monad
|
||||
tutorials/michelson_anti_patterns
|
||||
tutorials/entering_alpha
|
||||
tutorials/protocol_environment
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: OCaml API:
|
||||
|
||||
README
|
||||
api/api-inline
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
309
docs/introduction/alphanet.rst
Normal file
309
docs/introduction/alphanet.rst
Normal file
@ -0,0 +1,309 @@
|
||||
Participating in the Alphanet
|
||||
=============================
|
||||
|
||||
Welcome to the Tezos alphanet, which is a pre-release network for the
|
||||
Tezos blockchain. Currently, the chain is reset every few weeks.
|
||||
|
||||
For news and support about the alphanet, please join IRC (``#tezos`` on
|
||||
freenode). Please, report bugs related to the alphanet on the IRC
|
||||
channel before filling Github issues.
|
||||
|
||||
For more information about the project in general, see:
|
||||
|
||||
https://www.tezos.com/
|
||||
|
||||
How to join the alphanet ?
|
||||
--------------------------
|
||||
|
||||
We provide two ways of joining the alphanet :
|
||||
|
||||
- use ``docker`` and prebuilt binaries (recommended way, tested on
|
||||
windows/mac/linux)
|
||||
- manual compilation and installation (linux and mac only)
|
||||
|
||||
The ``alphanet.sh`` script
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The recommended way for running an up-to-date Tezos node connected to
|
||||
the alphanet is to use ``scripts/alphanet.sh``. Its only requirement is
|
||||
a working installation of Docker:
|
||||
|
||||
https://www.docker.com/community-edition
|
||||
|
||||
First, you need to download the script:
|
||||
|
||||
::
|
||||
|
||||
wget https://raw.githubusercontent.com/tezos/tezos/alphanet/scripts/alphanet.sh
|
||||
chmod +x alphanet.sh
|
||||
|
||||
You are now one step away from a working node:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh start
|
||||
|
||||
This will launch a docker container running the various daemons that
|
||||
form a working tezos node. The first launch might take a few minutes to
|
||||
synchronize the chain.
|
||||
|
||||
On first launch the script will also create a cryptographic identity
|
||||
(nicknamed ``my_identity``) and provide you with free tezzies on a fresh
|
||||
account (nicknamed ``my_account``). You might check your balance with:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client get balance for my_account
|
||||
|
||||
On some circumstances the account creation might fail. If so, see
|
||||
section “Known issues” below on how to force the account creation.
|
||||
|
||||
See ``./alphanet.sh --help`` for more informations about the script. In
|
||||
particular see ``./alphanet.sh client --help`` and
|
||||
``scripts/README.master`` for more information about the client.
|
||||
|
||||
Every call to ``alphanet.sh`` will check for updates of the node and
|
||||
will fail if your node is not up-to-date. For updating the node, simply
|
||||
run:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh restart
|
||||
|
||||
If you prefer to temporarily disable automatic updates, you just have to
|
||||
set an environment variable:
|
||||
|
||||
::
|
||||
|
||||
export TEZOS_ALPHANET_DO_NOT_PULL=yes
|
||||
|
||||
Compilation from sources
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``alphanet`` branch in the tezos git repository will always contain
|
||||
the up-to-date sources of the tezos-node required for running the
|
||||
alphanet. See ``docs/README.master`` on how to compile it.
|
||||
|
||||
Once built, you might launch the a node by running:
|
||||
|
||||
::
|
||||
|
||||
./tezos-node identity generate 24.
|
||||
./tezos-node run --rpc-addr localhost
|
||||
|
||||
By default this instance will store its data in ``$HOME/.tezos-node``
|
||||
and will listen to incoming peers on port 9732. It will also listen to
|
||||
RPC requests on port 8732 (only from ``localhost``). You might find more
|
||||
options by running ``./tezos-node config --help``.
|
||||
|
||||
If you want to stake (see below for more details), you will also have to
|
||||
run:
|
||||
|
||||
::
|
||||
|
||||
./tezos-client launch daemon
|
||||
|
||||
That’s all. For the rest of the document, to execute the example
|
||||
commands, you will have to replace ``./alphanet.sh client`` by
|
||||
``./tezos-client``.
|
||||
|
||||
How to observe the network ?
|
||||
----------------------------
|
||||
|
||||
The alphanet script provides a basic command ``./alphanet.sh head`` that
|
||||
allows you to see if your own node is synchronized.
|
||||
|
||||
The Tezos client also offers a lot of commands to introspect the state
|
||||
of the node, and also to list and call the RPCs of the nodes.
|
||||
|
||||
Enthusiastic Tezos adopter fredcy has also developed a nice block
|
||||
explorer for the alphanet. See https://github.com/fredcy/tezos-client.
|
||||
|
||||
In an upcoming version, we will also provide an opt-in tool for node
|
||||
runners that will allow us to provide a global monitoring panel of the
|
||||
alphanet.
|
||||
|
||||
How to obtain free Tez from the faucet contract ?
|
||||
-------------------------------------------------
|
||||
|
||||
The alphanet contains an ad-hoc faucet contract, that will generate new
|
||||
tezzies for you to test. Obviously, this contract will not be available
|
||||
outside of the test network.
|
||||
|
||||
First, if you don’t have any cryptographic identity yet, you need to
|
||||
generate one (replace ``my_identity`` with any name that suits you
|
||||
best):
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client gen keys "my_identity"
|
||||
|
||||
Then, you have to generate a new “free” account (replace ``my_account``
|
||||
with any name that suits you best and ``my_identity`` by the name used
|
||||
in the previous command):
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client originate free account "my_account" for "my_identity"
|
||||
|
||||
That’s all. You might check your balance:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client get balance for "my_account"
|
||||
|
||||
If you want MORE tezzies, you need to generate as many free accounts as
|
||||
you need (you should receive ꜩ100.000 per account) and then transfer the
|
||||
tezzies into a single account. For instance:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client originate free account "my_alt_account" for "my_identity"
|
||||
./alphanet.sh client transfer 100,000.00 from "my_alt_account" to "my_account" -fee 0.00
|
||||
./alphanet.sh client forget contract "my_alt_account"
|
||||
|
||||
Note that the test network is kind enough to accept transactions without
|
||||
fees…
|
||||
|
||||
How to play with smart-contracts ?
|
||||
----------------------------------
|
||||
|
||||
An advanced documentation of the smart contract language is in
|
||||
|
||||
``/docs/language.md``
|
||||
|
||||
Some test contracts are in
|
||||
|
||||
``/tests/contracts/``
|
||||
|
||||
For details and examples, see:
|
||||
|
||||
http://www.michelson-lang.com/
|
||||
|
||||
How to stake on the alphanet ?
|
||||
------------------------------
|
||||
|
||||
By default, the faucet of the alphanet (the one behind
|
||||
``./alphanet.sh originate free account "my_account" for "my_identity"``)
|
||||
creates contracts which are managed by ``my_identity`` but whose staking
|
||||
rights are delegated to the baker of the block including the
|
||||
origination. That way we are sure that staking rights are attributed to
|
||||
an active baker.
|
||||
|
||||
But, nonetheless, you might claim your staking rights!
|
||||
|
||||
The following command returns the current delegate of a contract:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client get delegate for "my_account"
|
||||
|
||||
If it is one the following, it is indeed one of our “bootstrap”
|
||||
contracts!
|
||||
|
||||
- ``tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc``
|
||||
- ``tz1irovm9SKduvL3npv8kDM54PSWY5VJXoyz``
|
||||
- ``tz1UsgSSdRwwhYrqq7iVp2jMbYvNsGbWTozp``
|
||||
- ``tz1TwYbKYYJxw7AyubY4A9BUm2BMCPq7moaC``
|
||||
- ``tz1QWft73Zhj5VSA1sCuEi9HhDDJqywE6BtC``
|
||||
|
||||
You might change the delegate of a contract with a single command:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client set delegate for "my_account" to "my_identity"
|
||||
|
||||
You now have staking rights!
|
||||
|
||||
Well, almost.
|
||||
|
||||
You should wait.
|
||||
|
||||
A little bit.
|
||||
|
||||
At most two cycles. Which, on the alphanet is 128 blocks (something
|
||||
around 2 hours). On the mainnet, this will be between 2 weeks and a
|
||||
month.
|
||||
|
||||
But, to enforce your right a last step is required. When baking or
|
||||
endorsing a block, a bond is taken out of the default account associated
|
||||
to the public key of the delegate. Hence, in order to stake, you must be
|
||||
provisioning for bond deposit.
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client transfer 50,000.00 from "my_account" to "my_identity"
|
||||
|
||||
On the alphanet, a bond is ꜩ1000. Hence, with the previous command you
|
||||
provisioned 50 bonds. If you want more, see section “How to obtain free
|
||||
Tez from the faucet contract ?”.
|
||||
|
||||
Now, you are settled. The ``alphanet`` docker image runs a baker daemon
|
||||
and a endorser daemon, by default for all your keys.
|
||||
|
||||
To know if you staked, just run:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh baker log
|
||||
./alphanet.sh endorser log
|
||||
|
||||
You should see lines such as:
|
||||
|
||||
::
|
||||
|
||||
Injected block BLxzbB7PBW1axq for bootstrap5 after BLSrg4dXzL2aqq (level 1381, slot 0, fitness 00::0000000000005441, operations 21)
|
||||
|
||||
Or:
|
||||
|
||||
::
|
||||
|
||||
Injected endorsement for block 'BLSrg4dXzL2aqq' (level 1381, slot 3, contract bootstrap5) 'oo524wKiEWBoPD'
|
||||
|
||||
On the alphanet, rewards for staking are credited after 24 hours. The
|
||||
reward for baking or endorsing a block is ꜩ150. The safety bond is
|
||||
returned together with the reward.
|
||||
|
||||
To know when you will be allowed to stake in the current cycle, you
|
||||
might try the following RPCs, where you replaced ``tz1iFY8ads...`` by
|
||||
the appropriate value:
|
||||
|
||||
::
|
||||
|
||||
$ ./alphanet.sh client list known identities
|
||||
my_identity: tz1iFY8aDskx9QGbgBy68SNAGgkc7AE2iG9H (public key known) (secret key known)
|
||||
$ ./alphanet.sh client rpc call /blocks/head/proto/helpers/rights/baking/delegate/tz1iFY8aDskx9QGbgBy68SNAGgkc7AE2iG9H with '{}'
|
||||
{ "ok":
|
||||
[ { "level": 1400.000000, "priority": 2.000000,
|
||||
"timestamp": "2017-05-19T03:21:52Z" },
|
||||
... ] }
|
||||
|
||||
Known issues
|
||||
------------
|
||||
|
||||
Missing account ``my_account``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The chain synchronization has not been optimized yet and the
|
||||
``alphanet.sh`` script might mis-detect the end of the synchronization
|
||||
step. If so, it will try to create your free account in an outdated
|
||||
context and your new account will never be included in the chain.
|
||||
|
||||
To fix this, just wait for your node to be synchronized: for that run
|
||||
the following command, in the middle of a (raw) json object, it should
|
||||
display the date of the last block (which should not be too far in the
|
||||
past):
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh head
|
||||
|
||||
Please note that the printed date is GMT, don’t forget the time shift.
|
||||
|
||||
Then, you need to remove from the client state the non-existent contract
|
||||
and regenerate a new one:
|
||||
|
||||
::
|
||||
|
||||
./alphanet.sh client forget contract "my_account"
|
||||
./alphanet.sh client originate free account "my_account" for "my_identity"
|
1
docs/introduction/alphanet_changes.md
Symbolic link
1
docs/introduction/alphanet_changes.md
Symbolic link
@ -0,0 +1 @@
|
||||
../../CHANGES.alphanet
|
1
docs/introduction/alphanet_changes.rst
Symbolic link
1
docs/introduction/alphanet_changes.rst
Symbolic link
@ -0,0 +1 @@
|
||||
../../CHANGES.alphanet
|
82
docs/introduction/contributing.rst
Normal file
82
docs/introduction/contributing.rst
Normal file
@ -0,0 +1,82 @@
|
||||
How to contribute
|
||||
=================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The purpose of this document is to help contributors get started with
|
||||
the Tezos OCaml codebase.
|
||||
|
||||
First steps
|
||||
-----------
|
||||
|
||||
First, make sure that you are proficient enough in OCaml. The community
|
||||
Website http://www.ocaml.org below gives a few pointer for that. In
|
||||
particular, we use a lot of functors, and a few GADTs in the codebase,
|
||||
so you may want to make sure that you master these advanced concepts.
|
||||
|
||||
Then, if you don’t know well about the Lwt library, that’s what you want
|
||||
to learn. This library is used extensively throughout the code base, as
|
||||
that’s the one we use to handle concurrency, and Tezos is a very
|
||||
concurrent system. You can use the online documentation. The `PDF
|
||||
manual <https://ocsigen.org/download/lwt-manual.pdf>`__ is also quite
|
||||
well written, but unfortunately not up to date, in particular the syntax
|
||||
extension has changed. It is still a valid resource for the basic
|
||||
concepts. The chapter on concurrency of `Real World
|
||||
OCaml <https://github.com/dkim/rwo-lwt>`__ has also been ported to Lwt.
|
||||
|
||||
After that, it is a good idea to read the tutorials for
|
||||
:ref:`error_monad<error_monad>` and
|
||||
:ref:`data_encoding <data_encoding>`, two homegrown
|
||||
libraries that we use pervasively.
|
||||
|
||||
Where to start
|
||||
--------------
|
||||
|
||||
While you familiarize yourself with the basics as suggested above, you
|
||||
can have a look at the :ref:`software architecture
|
||||
<the_big_picture>` of Tezos. It will
|
||||
give you the main components and their interactions, and links to the
|
||||
documentations for the various parts.
|
||||
|
||||
Our git workflow
|
||||
----------------
|
||||
|
||||
First, the repository is http://gitlab.com/tezos/tezos, the github one
|
||||
is just a clone that exists for historical reasons. So if you want to
|
||||
contribute, simply create an account there.
|
||||
|
||||
Then, there are many ways to use Git, here is ours.
|
||||
|
||||
We use almost only merge requests to push into master. Meaning, nobody
|
||||
should push directly into master. Once a merge request is ready, it is
|
||||
reviewed and approved, we merge it using the ``--fast-forward`` option
|
||||
of Git, in order to maintain a linear history without merge patches.
|
||||
|
||||
For that to work, it means that merge requests must be direct suffixes
|
||||
of the master branch. So whenever ``origin/master`` changes, you have to
|
||||
rebase your branch on it, so that you patches always sit on top of
|
||||
master. When that happens, you may have to edit your patches during the
|
||||
rebase, and then use ``push -f`` in your branch to rewrite the history.
|
||||
|
||||
We also enforce a few hygiene rules, so make sure your MR respects them:
|
||||
|
||||
- Prefer small atomic commits over a large one that do many things.
|
||||
- Don’t mix refactoring and new features.
|
||||
- Never mix reindentation, whitespace deletion, or other style changes
|
||||
with actual code changes.
|
||||
- Try as much as possible to make every patch compile, not only the
|
||||
last.
|
||||
- If you add new functions into a documented interface, don’t forget to
|
||||
extend the documentation for your addition.
|
||||
- For parts whose specification is in the repository (e.g. Michelson),
|
||||
make sure to keep it in sync with the implementation.
|
||||
- Try and mimic the style of commit messages, and for non trivial
|
||||
commits, add an extended commit message.
|
||||
|
||||
As per the hygiene of MRs themselves:
|
||||
|
||||
- Give appropriate titles to the MRs, and when non trivial add a
|
||||
detailed motivated explanation.
|
||||
- Give meaningful and consistent names to branches.
|
||||
- Don’t forget to put a ``WIP:`` flag when it is a work in progress.
|
10
docs/introduction/zeronet.rst
Normal file
10
docs/introduction/zeronet.rst
Normal file
@ -0,0 +1,10 @@
|
||||
The zeronet network
|
||||
===================
|
||||
|
||||
The Tezos zeronet is a development network that might be broken multiple
|
||||
times a day.
|
||||
|
||||
If you wish to experiment with Tezos we recommend you to test the
|
||||
``alphanet``, which is a more stable test network.
|
||||
|
||||
In particular, we offer no support about the zeronet.
|
129
docs/logo.svg
Normal file
129
docs/logo.svg
Normal file
@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="48.739811mm"
|
||||
height="22.914806mm"
|
||||
viewBox="0 0 48.739811 22.914806"
|
||||
version="1.1"
|
||||
id="svg5074"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="logo.svg">
|
||||
<defs
|
||||
id="defs5068" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="152.39587"
|
||||
inkscape:cy="33.859768"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1311"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="55"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:measure-start="71.0714,60.5357"
|
||||
inkscape:measure-end="109.286,25.8929"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true" />
|
||||
<metadata
|
||||
id="metadata5071">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-58.404451,-81.16997)">
|
||||
<path
|
||||
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke-width:1.22451675"
|
||||
d="m 61.072266,-14.291016 a 32.000002,32.000002 0 0 0 -32,32 32.000002,32.000002 0 0 0 32,32 h 1.21289 94.751954 1.21289 a 32.000002,32.000002 0 0 0 32,-32 32.000002,32.000002 0 0 0 -32,-32 h -1.21289 -94.751954 z"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,56.807283,87.908796)"
|
||||
id="path4516"
|
||||
inkscape:connector-curvature="0" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke-width:0.35944793"
|
||||
id="path4529"
|
||||
cx="69.861855"
|
||||
cy="92.627373"
|
||||
r="11.457403" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:18.16365242px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.45409128"
|
||||
x="64.233612"
|
||||
y="94.434319"
|
||||
id="text5621"
|
||||
transform="scale(0.98259142,1.017717)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5619"
|
||||
x="64.233612"
|
||||
y="94.434319"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Code2000;-inkscape-font-specification:Code2000;fill:#ffffff;stroke-width:0.45409128">ꜩ</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="84.777519"
|
||||
y="86.395416"
|
||||
id="text5633"><tspan
|
||||
sodipodi:role="line"
|
||||
x="84.777519"
|
||||
y="95.759186"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.88055563px;line-height:0;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332"
|
||||
id="tspan5650" /></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:3.88055563px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="91.852211"
|
||||
y="95.18335"
|
||||
id="text5660"><tspan
|
||||
sodipodi:role="line"
|
||||
x="91.852211"
|
||||
y="95.18335"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0;font-family:Lato;-inkscape-font-specification:Lato;text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332"
|
||||
id="tspan4521">Developer</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:3.88055563px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="83.283157"
|
||||
y="98.462372"
|
||||
id="text4535"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4533"
|
||||
x="83.283157"
|
||||
y="98.462372"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Lato;-inkscape-font-specification:Lato;fill:#ffffff;stroke-width:0.26458332">Resources</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:3.88055563px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="85.401657"
|
||||
y="90.288643"
|
||||
id="text4541"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4539"
|
||||
x="85.401657"
|
||||
y="90.288643"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888903px;font-family:'Roboto Slab';-inkscape-font-specification:'Roboto Slab';fill:#ffffff;stroke-width:0.26458332">Tezos</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
2109
docs/michelson.md
2109
docs/michelson.md
File diff suppressed because it is too large
Load Diff
@ -1,88 +0,0 @@
|
||||
* Michelson Anti-Patterns
|
||||
Even though Michelson is designed to make it easy to write secure contracts and difficult to write vulnerable ones, it is still possible to write buggy contracts that leak data and funds. This is a list of mistakes that you can make when writing or interacting with contracts on the Tezos blockchain and alternative ways to write code that avoid these problems.
|
||||
|
||||
_Note_: We are currently reworking the concurrency model of Michelson (how and when subtransactions ar made), so that some of these patterns will be prevented by the language itself.
|
||||
|
||||
** Refunding to a list of contracts
|
||||
One common pattern in contracts is to refund a group of people's funds at once.
|
||||
This is problematic if you accepted arbitrary contracts as a malicious user can
|
||||
do cause various issues for you.
|
||||
|
||||
*** Possible issues:
|
||||
|
||||
- One contract swallows all the gas through a series of callbacks
|
||||
- One contract writes transactions until the block is full
|
||||
- Reentrancy bugs. Michelson intentionally makes these difficult to write, but it is still possible if you try.
|
||||
- A contract calls the `FAIL` instruction, stopping all computation.
|
||||
|
||||
|
||||
*** Alternatives/Solutions:
|
||||
|
||||
- Create a default account from people's keys. Default accounts cannot execute code, avoiding the bugs above. Have people submit keys rather than contracts.
|
||||
- Have people pull their funds individually. Each user can break their own withdrawal only. *This does not protect against reentrancy bugs.*
|
||||
|
||||
** Avoid batch operations when users can increase the size of the batch
|
||||
Contracts that rely on linear or super-linear operations are vulnerable to
|
||||
malicious users supplying values until the contract cannot finish without
|
||||
running into fuel limits. This can deadlock your contract.
|
||||
|
||||
*** Possible issues:
|
||||
- Malicious users can force your contract into a pathological worst case, stopping it from finishing with available gas. Note that in the absence of hard gas limits, this can still be disabling as node operators may not want to run contracts that take more than a certain amount of gas.
|
||||
- You may hit the slow case of an amortized algorithm or data structure at an inopportune time, using up all of your contract's available gas.
|
||||
|
||||
*** Alternatives/Solutions:
|
||||
- Avoid data structures and algorithms that rely on amortized operations, especially when users may add data.
|
||||
- Restrict the amount of data your contract can store to a level that will not overwhelm the available gas.
|
||||
- Write your contract so that it may pause and resume batch operations. This would complicate these sequences and require constant checking of available gas, but it prevents various attacks.
|
||||
|
||||
*Do not assume an attack will be prohibitively expensive*
|
||||
Cryptocurrencies have extreme price fluctuations frequently and an extremely motivated attacker may decide that an enormous expense is justified. Remember, an attack that disables a contract is not just targeted at the authors, but also the users of that contract.
|
||||
|
||||
** Signatures alone do not prevent replay attacks
|
||||
If your contract uses signatures to authenticate messages, beware of replay attacks. If a user ever signs a piece of data, you /must/ make sure that that piece of data is never again a valid message to the contract. If you do not do this, anyone else can call your contract with the same input and piggyback on the earlier approval.
|
||||
|
||||
*** Possible issues:
|
||||
- A previously approved action can be replayed.
|
||||
|
||||
*** Alternatives/Solutions
|
||||
- Use an internal counter to make the data you ask users to sign unique. This counter should be per key so that users can find out what they need to approve. This should be paired with a signed hash of your contract to prevent cross-contract replays.
|
||||
- Use the =SOURCE= instruction to verify that the expected sender is the source of the message.
|
||||
|
||||
*** Do not assume users will use a unique key for every smart contract
|
||||
Users should always use a different key for every contract with which they interact. If this is not the case, a message the user signed for another contract can be sent to your contract. An internal counter alone does not protect against this attack. It /must/ be paired with a hash of your contract. You must verify the source of the message.
|
||||
|
||||
** Storing/transferring private data
|
||||
Once data is published to anyone, including broadcasting a transaction, that data is public. Never transmit secret information via any part of the blockchain ecosystem. As soon as you have broadcast a transaction including that piece of information, anyone can see it. Furthermore, malicious nodes in the system can manipulate unsigned transactions by delaying, modifying, or reordering them.
|
||||
|
||||
*** Possible Issues
|
||||
- If data is not signed, it can be modified
|
||||
- Transactions can be delayed
|
||||
- Secret information will become public
|
||||
|
||||
*** Alternatives/Solutions
|
||||
- Do not store private information on the blockchain or broadcast it in transactions.
|
||||
- Sign all transactions that contain information that, if manipulated, could be abused.
|
||||
- Use counters to enforce transaction orders.
|
||||
This will at least create a logical clock on messages sent to your contract.
|
||||
|
||||
** Not setting all state before a transfer
|
||||
Reentrancy is a potential issue on the blockchain. When a contract makes a transfer to another contract, that contract can execute its own code,
|
||||
and can make arbitrary further transfers, including back to the original contract. If state has not been updated before the transfer is made, a contract can call back in and execute actions based on old state.
|
||||
|
||||
*** Possible Issues
|
||||
- Multiple withdrawals/actions
|
||||
- Generating illegal state if state is updated twice later
|
||||
|
||||
*** Alternatives/Solutions
|
||||
- Forbid reentrancy by means of a flag in your storage, unless you have a good reason to allow users to reenter your contract, this is likely the best option.
|
||||
- Only make transfers to trusted contracts or default accounts. Default accounts cannot execute code, so it is always safe to transfer to them. Before trusting a contract, make sure that its behavior cannot be modified and that you have an extremely high degree of confidence in it.
|
||||
|
||||
** Do not store funds for others in spendable contracts
|
||||
Tezos allows contracts to be marked as spendable. Managers of spendable contracts can make transfers using the funds stored inside the contract. This can subvert guarantees about the contract's behavior that are visibile in the code.
|
||||
|
||||
*** Possible Issues
|
||||
- The funds of a contract can be removed.
|
||||
- A contract may not be able to meet its obligations
|
||||
|
||||
*** Alternatives/Solutions
|
||||
- Do not store funds in spendable contracts that you do not control.
|
206
docs/tutorials/data_encoding.rst
Normal file
206
docs/tutorials/data_encoding.rst
Normal file
@ -0,0 +1,206 @@
|
||||
.. _data_encoding:
|
||||
|
||||
The ``data_encoding`` library
|
||||
=============================
|
||||
|
||||
Throughout the Tezos protocol, data is serialized so that it can be used
|
||||
via RPC, written to disk, or placed in a block. This
|
||||
serialization/de-serialization is handled via the :package:`tezos-data-encoding`
|
||||
library by providing a set primitive encodings and a variety of combinators.
|
||||
|
||||
Examples/Tutorial
|
||||
-----------------
|
||||
|
||||
Encoding an integer
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Integers are defined as other concrete data types with a generic
|
||||
encoding type ``type 'a encoding``. This means that it is an encoding
|
||||
to/from type ``int``. There are a variety of ways to encode an integer,
|
||||
depending on what binary serialization you want to achieve:
|
||||
|
||||
- ``Data_encoding.int8``
|
||||
- ``Data_encoding.uint8``
|
||||
- ``Data_encoding.int16``
|
||||
- ``Data_encoding.uint16``
|
||||
- ``Data_encoding.int31``
|
||||
- ``Data_encoding.int32``
|
||||
- ``Data_encoding.int64``
|
||||
|
||||
For example, an encoding that represents a 31 bit integer has type
|
||||
``Data_encoding.int31 = int Data_encoding.encoding``.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
let int31_encoding = Data_encoding.int31
|
||||
|
||||
Encoding an object
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Encoding a single integer is fairly uninteresting. The Dataencoding
|
||||
library provides a number of combinators that can be used to build more
|
||||
complicated objects. Consider the type that represents an interval from
|
||||
the first number to the second:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
type interval = int64 * int64
|
||||
|
||||
We can define an encoding for this type as:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
let interval_encoding =
|
||||
Data_encoding.(obj2 (req "min" int64) (req "max" int64))
|
||||
|
||||
In the example above we construct a new value ``interval_encoding`` by
|
||||
combining two int64 integers using the ``obj2`` constructor.
|
||||
|
||||
The library provides different constructors, i.e. for objects that have
|
||||
no data (``Data_encoding.empty``), constructors for object up to 10
|
||||
fields, constructors for tuples, list, etc.
|
||||
|
||||
These are serialized to binary by converting each internal object to
|
||||
binary and placing them in the order of the original object and to JSON
|
||||
as a JSON object with field names.
|
||||
|
||||
Lists, arrays, and options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
List, Arrays and options types can by built on top of ground data types.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
type interval_list = interval list
|
||||
|
||||
type interval_array = interval array
|
||||
|
||||
type interval_option = interval option
|
||||
|
||||
And the encoders for these types as
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
let interval_list_encoding = Data_encoding.list interval_encoding
|
||||
let interval_array_encoding = Data_encoding.array interval_encoding
|
||||
let interval_option_encoding = Data_encoding.option interval_encoding
|
||||
|
||||
Union types
|
||||
~~~~~~~~~~~
|
||||
|
||||
The Tezos codebase makes heavy use of variant types. Consider the
|
||||
following variant type:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
type variant = B of bool
|
||||
| S of string
|
||||
|
||||
Encoding for this types can be expressed as:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
let variant_encoding =
|
||||
Data_encoding.(union ~tag_size:`Uint8
|
||||
[ case
|
||||
bool
|
||||
(function B b -> Some b | _ -> None)
|
||||
(fun b -> B b) ;
|
||||
case
|
||||
string
|
||||
(function S s -> Some s | _ -> None)
|
||||
(fun s -> S s) ])
|
||||
|
||||
This variant encoding is a bit more complicated. Let’s look at the parts
|
||||
of the type:
|
||||
|
||||
- We include an optimization hint to the binary encoding to inform it
|
||||
of the number of elements we expect in the tag. In most cases, we can
|
||||
use :literal:`\`Uint8`, which allows you to have up to 256 possible
|
||||
cases (default).
|
||||
- We provide a function to wrap the datatype. The encoding works by
|
||||
repeatedly trying to decode the datatype using these functions until
|
||||
one returns ``Some payload``. This payload is then encoded using the
|
||||
dataencoding specified.
|
||||
- We specify a function from the encoded type to the actual datatype.
|
||||
|
||||
Since the library does not provide an exhaustive check on these
|
||||
constructors, the user must be careful when constructing unin types to
|
||||
avoid unfortunate runtime failures.
|
||||
|
||||
How the Dataencoding module works
|
||||
---------------------------------
|
||||
|
||||
This section is 100% optional. You do not need to understand this
|
||||
section to use the library.
|
||||
|
||||
The library uses GADTs to provide type-safe
|
||||
serialization/de-serialization. From there, a runtime representation of
|
||||
JSON objects is parsed into the type-safe version.
|
||||
|
||||
First we define an untyped JSON AST:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
type json =
|
||||
[ `O of (string * json) list
|
||||
| `Bool of bool
|
||||
| `Float of float
|
||||
| `A of json list
|
||||
| `Null
|
||||
| `String of string ]
|
||||
|
||||
This is then parsed into a typed AST ( we eliminate several cases for
|
||||
clarity):
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
type 'a desc =
|
||||
| Null : unit desc
|
||||
| Empty : unit desc
|
||||
| Bool : bool desc
|
||||
| Int64 : Int64.t desc
|
||||
| Float : float desc
|
||||
| Bytes : Kind.length -> MBytes.t desc
|
||||
| String : Kind.length -> string desc
|
||||
| String_enum : Kind.length * (string * 'a) list -> 'a desc
|
||||
| Array : 'a t -> 'a array desc
|
||||
| List : 'a t -> 'a list desc
|
||||
| Obj : 'a field -> 'a desc
|
||||
| Objs : Kind.t * 'a t * 'b t -> ('a * 'b) desc
|
||||
| Tup : 'a t -> 'a desc
|
||||
| Union : Kind.t * tag_size * 'a case list -> 'a desc
|
||||
| Mu : Kind.enum * string * ('a t -> 'a t) -> 'a desc
|
||||
| Conv :
|
||||
{ proj : ('a -> 'b) ;
|
||||
inj : ('b -> 'a) ;
|
||||
encoding : 'b t ;
|
||||
schema : Json_schema.schema option } -> 'a desc
|
||||
| Describe :
|
||||
{ title : string option ;
|
||||
description : string option ;
|
||||
encoding : 'a t } -> 'a desc
|
||||
| Def : { name : string ;
|
||||
encoding : 'a t } -> 'a desc
|
||||
|
||||
- The first set of constructors define all ground types.
|
||||
- The constructors for ``Bytes``, ``String`` and ``String_enum``
|
||||
includes a length fields in order to provide safe binary
|
||||
serialization.
|
||||
- The constructors for ``Array`` and ``List`` are used by the
|
||||
combinators we saw earlier.
|
||||
- The ``Obj`` and ``Objs`` constructors create JSON objects. These are
|
||||
wrapped in the ``Conv`` constructor to remove nesting that results
|
||||
when these constructors are used naively.
|
||||
- The ``Mu`` constructor is used to create self-referential
|
||||
definitions.
|
||||
- The ``Conv`` constructor allows you to clean up a nested definition
|
||||
or compute another type from an existing one.
|
||||
- The ``Describe`` and ``Def`` constructors are used to add
|
||||
documentation
|
||||
|
||||
The library also provides various wrappers and convenience functions to
|
||||
make constructing these objects easier. Reading the documentation in the
|
||||
`mli file
|
||||
<../api/odoc/tezos-data-encoding/Tezos_data_encoding/Data_encoding/index.html>`__
|
||||
should orient you on how to use these functions and their purposes.
|
196
docs/tutorials/entering_alpha.rst
Normal file
196
docs/tutorials/entering_alpha.rst
Normal file
@ -0,0 +1,196 @@
|
||||
How to start reading protocol Alpha
|
||||
===================================
|
||||
|
||||
Protocol Alpha, whose Alpha has nothing to do with the one in Alphanet,
|
||||
is the name of the initial economic protocol. Alpha is a placeholder
|
||||
name, while we decide on the naming convention for protocol versions.
|
||||
|
||||
Before reading that document, you may want to:
|
||||
|
||||
- read the whitepaper,
|
||||
- read :ref:`how the economic protocol is
|
||||
sandboxed <protocol_environment>`.
|
||||
|
||||
As all protocols, Alpha is made of a series of OCaml interface and
|
||||
implementation files, accompanied by a ``TEZOS_PROTOCOL`` file.
|
||||
|
||||
The ``TEZOS_PROTOCOL`` structure
|
||||
--------------------------------
|
||||
|
||||
If you look at this file in the repository, you will see that it is
|
||||
composed of the hash of the sources, and the list of its modules, in
|
||||
linking order.
|
||||
|
||||
Protocol Alpha is structured as a tower of abstraction layers, a coding
|
||||
discipline that we designed to have OCaml check as many invariants as
|
||||
possible at typing time. You will also see empty lines in
|
||||
``TEZOS_PROTOCOL`` that denotate these layers of abstraction.
|
||||
|
||||
These layers follow the linking order: the first modules are the tower’s
|
||||
foundation that talk to the raw key-value store, and going forward in
|
||||
the module list means climbing up the abstraction tower.
|
||||
|
||||
The big abstraction barrier: ``Tezos_context``
|
||||
----------------------------------------------
|
||||
|
||||
the proof-of-stake algorithm, as described in the white paper, relies on
|
||||
an abstract state of the ledger, that is read and transformed during
|
||||
validation of a block.
|
||||
|
||||
Due to the polymorphic nature of Tezos, the ledger’s state (that we call
|
||||
**context** in the code), cannot be specific to protocol Alpha’s need.
|
||||
The proof-of-stake is thus implemented over a generic key-value store
|
||||
whose keys and associated binary data must implement the abstract
|
||||
structure.
|
||||
|
||||
The ``Tezos_context`` module enforces the separation of concerns
|
||||
between, on one hand, mapping the abstract state of the ledger to the
|
||||
concrete structure of the key-value store, and, on the other hand,
|
||||
implementing the proof-of-stake algorithm over this state.
|
||||
|
||||
In more practical terms, ``Tezos_context`` defines a type ``t`` that
|
||||
represents a state of the ledger. This state is an abstracted out
|
||||
version of the key-value store that can only be manipulated through the
|
||||
use of the few selected manipulations reexported by ``Tezos_context``,
|
||||
that always preserve the well-typed aspect and internal consistency
|
||||
invariants of the state.
|
||||
|
||||
When validating a block, the low-level state that result from the
|
||||
predecessor block is read from the disk, then abstracted out to a
|
||||
``Tezos_context.t``, which is then only updated by high level operations
|
||||
that preserve consistency, and finally, the low level state is extracted
|
||||
to be committed on disk.
|
||||
|
||||
This way, we have two well separated parts in the code. The code below
|
||||
``Tezos_context`` implements the ledger’s state storage, while the code
|
||||
on top of it is the proof-of-stake algorithm. Thanks to this barrier,
|
||||
the latter can remain nice, readable OCaml that only manipulates plain
|
||||
OCaml values.
|
||||
|
||||
Below the ``Tezos_context``
|
||||
---------------------------
|
||||
|
||||
For this part, in a first discovery of the source code, you can start by
|
||||
relying mostly on this coarse grained description, with a little bit of
|
||||
cherry-picking when you’re curious about how a specific invariant is
|
||||
enforced.
|
||||
|
||||
The ``*_repr`` modules
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These modules abstract the values of the raw key-value context by using
|
||||
:ref:`Data_encoding<data_encoding>`.
|
||||
|
||||
These modules define the data types used by the protocol that need to be
|
||||
serialized (amounts, contract handles, script expressions, etc.). For
|
||||
each type, it also defines its serialization format using
|
||||
:ref:`Data_encoding<data_encoding>`.
|
||||
|
||||
Above this layer, the code should never see the byte sequences in the
|
||||
database, the ones of transmitted blocks and operations, or the raw JSON
|
||||
of data transmitted via RPCs. It only manipulates OCaml values.
|
||||
|
||||
The ``Storage`` module and storage functors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Even with the concrete formats of values in the context abstracted out,
|
||||
type (or consistency) errors can still occur if the code accesses a
|
||||
value with a wrong key, or a key bound to another value. The next
|
||||
abstraction barrier is a remedy to that.
|
||||
|
||||
The storage module is the single place in the protocol where key
|
||||
litterals are defined. Hence, it is the only module necessary to audit,
|
||||
to know that the keys are not colliding.
|
||||
|
||||
It also abstracts the keys, so that each kind of key get its own
|
||||
accessors. For instance, module ``Storage.Contract.Balance`` contains
|
||||
accessors specific to contracts’ balances.
|
||||
|
||||
Moreover, the keys bear the type of the values they point to. For
|
||||
instance, only values of type ``Tez_repr.t`` can by stored at keys
|
||||
``Storage.Contract.Balance``. And in case a key is not a global key, but
|
||||
a parametric one, this key is parametered by an OCaml value, and not the
|
||||
raw key part.
|
||||
|
||||
So in the end, the only way to be used when accessing a contract balance
|
||||
is ``Storage.Contract.Balance.get``, which takes a ``Contract_repr.t``
|
||||
and gives a ``Tez_repr.t``.
|
||||
|
||||
All these well-typed operations are generated by a set of functors, that
|
||||
come just before ``Storage`` in ``TEZOS_CONTEXT``.
|
||||
|
||||
The ``*_storage`` modules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The two previous steps ensure that the ledger’s state is always accessed
|
||||
and updated in a well-typed way.
|
||||
|
||||
However, it does not enforce that, for instance, when a contract is
|
||||
deleted, all of the keys that store its state in the context are indeed
|
||||
deleted.
|
||||
|
||||
This last series of modules named ``*_storage`` is there to enforce just
|
||||
that kind of invariants: ensuring the insternal consistency of the
|
||||
context structure.
|
||||
|
||||
These transaction do not go as far as checking that, for instance, when
|
||||
the destination of a transaction is credited, the source is also
|
||||
debitted, as in some cases, it might not be the case.
|
||||
|
||||
Above the ``Tezos_context``
|
||||
---------------------------
|
||||
|
||||
The three next sections describe the main entrypoints to the protocol:
|
||||
validation of blocks by the shell (that we often also call application),
|
||||
smart contracts, and RPC services.
|
||||
|
||||
The ``Main`` module is the entrypoint that’s used by the shell. It
|
||||
respects the module type that all protocol must follow. For that, its
|
||||
code is mostly plumbing,
|
||||
|
||||
Starting from ``Apply``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is were you want to start on your first read. Even if some plumbing
|
||||
code is woven in, such as error cases declaration and registration, most
|
||||
of the proof-of-stake code has been written in a verbose style, to be
|
||||
understood with minimum OCaml knowledge.
|
||||
|
||||
You want to start from the shell entry points (validation of the block
|
||||
header, validation of an operation, finalization of a block validation),
|
||||
and follow the control flow until you hit the ``Tezos_context``
|
||||
abstraction barrier. This will lead you to reading modules ``Baking``
|
||||
and ``Amendment``.
|
||||
|
||||
Smart contracts
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
From ``Apply``, you will also end up in modules ``Script_ir_translator``
|
||||
and ``Script_interpreter``. The former is the typechecker of Michelson
|
||||
that is called when creating a new smart contract, and the latter is the
|
||||
interpreter that is called when transfering tokens to a new smart
|
||||
contract.
|
||||
|
||||
Protocol RPC API
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Finally, the RPCs specific to Alpha are also defined above the
|
||||
``Tezos_context`` barrier. The definition is split into two parts.
|
||||
|
||||
The first part, ``Services``, defines the RPC API: URL schemes with the
|
||||
types of parameters, and input and output JSON schemas. This interface
|
||||
serves three purposes. As it is thourouhgly tyoed, it makes sure that
|
||||
the handlers have the right input and output types. It is also used by
|
||||
the client to perform RPC calls, to make sure that the URL schemes and
|
||||
JSON formats and consistent between the two parties. These two features
|
||||
are extremely useful when refactoring, as the OCaml typechecker will
|
||||
help us track the effects of an RPC API change on the whole codebase.
|
||||
The third purpose is of course, to make automatic documentation
|
||||
generation possible (as in ``tezos client rpc list/format``).
|
||||
|
||||
It can be useful if you are a third party developer who wants to read
|
||||
the OCaml definition of the service hierarchy directly, instead of the
|
||||
automatically generated JSON hierarchy.
|
||||
|
||||
The second part, ``Services_registration``, is responsible for plugging
|
||||
the OCaml handler functions that implement the RPC API.
|
366
docs/tutorials/error_monad.rst
Normal file
366
docs/tutorials/error_monad.rst
Normal file
@ -0,0 +1,366 @@
|
||||
.. _error_monad:
|
||||
|
||||
The Error Monad
|
||||
===============
|
||||
|
||||
This has been adapted from a blog post on *michelson-lang.com*.
|
||||
|
||||
If you’re not familiar with monads, go take a few minutes and read a
|
||||
tutorial. I personally got a lot out of this
|
||||
`paper <http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf>`__
|
||||
by Philip Wadler, but there are a ton of others available online. Find
|
||||
one that works for you. The error monad isn’t terribly scary as Monads
|
||||
go, so once you feel like you understand the gist, come on back and see
|
||||
if you can understand what’s going on.
|
||||
|
||||
I’m going to omit some convenience operations that a lot of monads
|
||||
provide in the examples below. If you want to add them, they’re not
|
||||
difficult.
|
||||
|
||||
Why you want the error monad
|
||||
----------------------------
|
||||
|
||||
In Tezos, we don’t want to have the node be crashable by an improper
|
||||
input. To avoid this possibility, it was decided that the system should
|
||||
not use exceptions for error handling. Instead, it uses an error monad.
|
||||
This design forces errors to be handled or carried through before an
|
||||
output can be used. Exceptions are still occasionally used, but this is
|
||||
mostly in the client and only for internal errors.
|
||||
|
||||
We also mix in the Lwt library, which we use for concurrency. This is
|
||||
combined with the error monad and is once again used pervasively
|
||||
throughout the codebase. The Lwt monad is a lot like promises in other
|
||||
languages.
|
||||
|
||||
Without further ado, let’s write an error monad.
|
||||
|
||||
A simple version of the error monad
|
||||
-----------------------------------
|
||||
|
||||
Here’s a very simple error monad.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
module Error : sig
|
||||
type 'a t
|
||||
(* Create a value of type t *)
|
||||
val return : 'a -> 'a t
|
||||
(* For when a computation fails *)
|
||||
val error : 'a t
|
||||
(* Apply an operation to a value in the error monad *)
|
||||
val (>>?) : 'a t -> ('a -> 'b t) -> 'b t (* bind *)
|
||||
end = struct
|
||||
type 'a t = Ok of 'a | Error
|
||||
let return x = Ok x
|
||||
let error = Error
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error -> Error
|
||||
end
|
||||
|
||||
So, is this what Tezos uses? We actually already have a lot of the
|
||||
structure that we’ll use later. The basic idea is that you return a
|
||||
value that’s correct and return an error if the operation failed.
|
||||
Outside of the error module, you can’t actually introspect an error
|
||||
value. You can only dispatch on the correctness/incorrectness of the
|
||||
value using bind.
|
||||
|
||||
What’s wrong here?
|
||||
|
||||
- We can’t report any information about an error case
|
||||
- We can’t report error traces, something that’s used to improve the
|
||||
quality of error messages throughout Tezos
|
||||
- We can’t handle some errors and continue executing
|
||||
|
||||
A slight improvement
|
||||
--------------------
|
||||
|
||||
Let’s now enhance our error reporting by allowing errors to contain a
|
||||
description string. Now we can report messages along with our errors. Is
|
||||
this enough of an improvement? Not really. We don’t have any flexibility
|
||||
about how the printing works. We still can’t create error traces and we
|
||||
can’t handle errors and resume executing the program.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
module Error : sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val error : string -> 'a t
|
||||
val (>>?) : 'a t -> ('a -> 'b t) -> 'b t (* bind *)
|
||||
val print_value : ('a -> string) -> 'a t -> unit
|
||||
end = struct
|
||||
type 'a t = Ok of 'a | Error of string
|
||||
let return x = Ok x
|
||||
let error s = Error s
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error s -> Error s
|
||||
let print_value func = function
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error s -> Printf.printf "Error: %s\n" s
|
||||
end
|
||||
|
||||
Traces
|
||||
------
|
||||
|
||||
Now that we have the basic structure down, we can add a mechanism to let
|
||||
us include traces. As a note, the error type I had above is exactly the
|
||||
``result`` type from the OCaml standard library. The traces are just
|
||||
lists of error messages. If you have a call you think might fail, and
|
||||
you want to provide a series of errors, you can wrap that result in the
|
||||
``trace`` function. If that call fails, an additional error is added.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
module Error : sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val error : string -> 'a t
|
||||
val (>>?) : 'a t -> ('a -> 'b t) -> 'b t (* bind *)
|
||||
val print_value : ('a -> string) -> 'a t -> unit
|
||||
val trace : string -> 'a t -> 'a t
|
||||
end = struct
|
||||
type 'a t = ('a, string list) result
|
||||
let return x = Ok x
|
||||
let error s = Error [ s ]
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error errs -> Error errs
|
||||
let print_value func = function
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error [ s ] -> Printf.printf "Error: %s\n" s
|
||||
| Error errors -> Printf.printf "Errors:\t%s\n" (String.concat "\n\t" errors)
|
||||
let trace error = function
|
||||
| Ok x -> Ok x
|
||||
| Error errors -> Error (error :: errors)
|
||||
end
|
||||
|
||||
A more descriptive message
|
||||
--------------------------
|
||||
|
||||
Even though traces are nice, we really want to be able to store more
|
||||
interesting data in the messages. We’re going to use an extensible
|
||||
variant type to do this. Extensible variants allow us to add a new case
|
||||
to a variant type at the cost of exhaustivity checking. We’re going to
|
||||
need two new mechanisms to make this work well. The first is an error
|
||||
registration scheme. In the actual error monad, this involves the data
|
||||
encoding module, which is how all data is encoded/decoded in Tezos. This
|
||||
module is another decently complicated part of the codebase that should
|
||||
probably the subject of a future post. Since you can declare arbitrary
|
||||
new errors, we’ll have a way of adding a printer for each error.
|
||||
|
||||
When we add a new error handler, we’ll use the ``register_handler``
|
||||
function. This function will take a function that takes an error and
|
||||
returns a ``string option``. These functions will look something like
|
||||
this:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
type error += Explosion_failure of string * int;;
|
||||
|
||||
register_error
|
||||
(function
|
||||
| Explosion_failure (s, i) ->
|
||||
Some (Printf.sprintf "Everything exploded: %s at %d" s i)
|
||||
| _ -> None)
|
||||
|
||||
I’m also renaming the ``error`` function to ``fail``. This is the
|
||||
convention used by the actual Errormonad module. I’m also exposing the
|
||||
``'a t`` type so that you can dispatch on it if you need to. This is
|
||||
used several times in the Tezos codebase.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
module Error : sig
|
||||
type error = ..
|
||||
type 'a t = ('a, error list) result
|
||||
val return : 'a -> 'a t
|
||||
val fail : error -> 'a t
|
||||
val (>>?) : ('a -> 'b t) -> 'a t -> 'b t (* bind *)
|
||||
val print_value : ('a -> string) -> 'a t -> unit
|
||||
val trace : error -> 'a t -> 'a t
|
||||
end = struct
|
||||
type error = ..
|
||||
type 'a t = ('a, error list) result
|
||||
let fail error = Error [ error ]
|
||||
let return x = Ok x
|
||||
let (>>?) func = function
|
||||
| Ok x -> func x
|
||||
| Error errs -> Error errs
|
||||
let registered = ref []
|
||||
let register_error handler =
|
||||
registered := (handler :: !registered)
|
||||
let default_handler error =
|
||||
"Unregistered error: " ^ Obj.(extension_name @@ extension_constructor error)
|
||||
let to_string error =
|
||||
let rec find_handler = function
|
||||
| [] -> default_handler error
|
||||
| handler :: handlers ->
|
||||
begin match handler error with
|
||||
| None -> find_handler handlers
|
||||
| Some s -> s
|
||||
end
|
||||
in find_handler !registered
|
||||
let print_value func = function
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error [ s ] -> Printf.printf "Error: %s\n" (to_string s)
|
||||
| Error errors -> Printf.printf "Errors:\t%s\n" (String.concat "\n\t" (List.map to_string errors))
|
||||
let trace error = function
|
||||
| Ok x -> Ok x
|
||||
| Error errors -> Error (error :: errors)
|
||||
end
|
||||
|
||||
Putting ``Lwt.t`` in the mix
|
||||
----------------------------
|
||||
|
||||
Tezos uses the `Lwt library <http://ocsigen.org/lwt/>`__ for threading.
|
||||
The Lwt monad is mixed in with the error monad module. This requires us
|
||||
to add some extra combinators and reexport some functions from Lwt.
|
||||
|
||||
I’m also renaming the type ``t`` to ``tzresult``, as used in the Tezos
|
||||
codebase.
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
module Error : sig
|
||||
type error = ..
|
||||
type 'a tzresult = ('a, error list) result
|
||||
val ok : 'a -> 'a tzresult
|
||||
val return : 'a -> 'a tzresult Lwt.t
|
||||
val error : error -> 'a tzresult
|
||||
val fail : error -> 'a tzresult Lwt.t
|
||||
val (>>?) : 'a tzresult -> ('a -> 'b tzresult) -> 'b tzresult (* bind *)
|
||||
val (>>=?) : 'a tzresult Lwt.t -> ('a -> 'b tzresult Lwt.t) -> 'b tzresult Lwt.t
|
||||
val (>>=) : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
|
||||
val print_value : ('a -> string) -> 'a tzresult Lwt.t -> unit Lwt.t
|
||||
val trace : error -> 'a tzresult Lwt.t -> 'a tzresult Lwt.t
|
||||
end = struct
|
||||
type error = ..
|
||||
type 'a tzresult = ('a, error list) result
|
||||
let fail error = Lwt.return (Error [ error ])
|
||||
let error error = (Error [ error ])
|
||||
let ok x = Ok x
|
||||
let return x = Lwt.return (ok x)
|
||||
let (>>?) value func =
|
||||
match value with
|
||||
| Ok x -> func x
|
||||
| Error errs -> Error errs
|
||||
let (>>=) = Lwt.bind
|
||||
let (>>=?) value func =
|
||||
value >>= function
|
||||
| Ok x -> func x
|
||||
| Error errs -> Lwt.return (Error errs)
|
||||
let registered = ref []
|
||||
let register_error handler =
|
||||
registered := (handler :: !registered)
|
||||
let default_handler error =
|
||||
"Unregistered error: " ^ Obj.(extension_name @@ extension_constructor error)
|
||||
let to_string error =
|
||||
let rec find_handler = function
|
||||
| [] -> default_handler error
|
||||
| handler :: handlers ->
|
||||
begin match handler error with
|
||||
| None -> find_handler handlers
|
||||
| Some s -> s
|
||||
end
|
||||
in find_handler !registered
|
||||
let print_value func value =
|
||||
value >>= fun value ->
|
||||
begin match value with
|
||||
| Ok x -> Printf.printf "Success: %s\n" (func x)
|
||||
| Error [ s ] -> Printf.printf "Error: %s\n" (to_string s)
|
||||
| Error errors -> Printf.printf "Errors:\t%s\n" (String.concat "\n\t" (List.map to_string errors))
|
||||
end; Lwt.return ()
|
||||
let trace error value =
|
||||
value >>= function
|
||||
| Ok x -> return x
|
||||
| Error errors -> Lwt.return (Error (error :: errors))
|
||||
end
|
||||
|
||||
The actual Tezos error monad
|
||||
----------------------------
|
||||
|
||||
The actual Tezos error monad adds a few things. Firstly, there are three
|
||||
categories of errors:
|
||||
|
||||
- :literal:`\`Temporary` - An error resulting from an operation that
|
||||
might be valid in the future, for example, a contract’s balance being
|
||||
too low to execute the intended operation. This can be fixed by
|
||||
adding more to the contract’s balance.
|
||||
- :literal:`\`Branch` - An error that occurs in one branch of the
|
||||
chain, but may not occur in a different one. For example, receiving
|
||||
an operation for an old or future protocol version.
|
||||
- :literal:`\`Permanent` - An error that is not recoverable because the
|
||||
operation is never going to be valid. For example, an invalid ꜩ
|
||||
notation.
|
||||
|
||||
The registration scheme also uses data encodings. Here’s an example from
|
||||
the `validator <../api/odoc/tezos-node-shell/Tezos_node_shell/Validator/index.html>`__:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
register_error_kind
|
||||
`Permanent
|
||||
~id:"validator.wrong_level"
|
||||
~title:"Wrong level"
|
||||
~description:"The block level is not the expected one"
|
||||
~pp:(fun ppf (e, g) ->
|
||||
Format.fprintf ppf
|
||||
"The declared level %ld is not %ld" g e)
|
||||
Data_encoding.(obj2
|
||||
(req "expected" int32)
|
||||
(req "provided" int32))
|
||||
(function Wrong_level (e, g) -> Some (e, g) | _ -> None)
|
||||
(fun (e, g) -> Wrong_level (e, g))
|
||||
|
||||
An error takes a category, id, title, description, and encoding. You
|
||||
must specify a function to take an error to an optional value of the
|
||||
encoding type and a function to take a value of the encoded type and
|
||||
create an error value. A pretty printer can optionally be specified, but
|
||||
may also be omitted.
|
||||
|
||||
The actual error monad and it’s tracing features can be seen in this
|
||||
function which parses contracts:
|
||||
|
||||
.. code:: ocaml
|
||||
|
||||
let parse_script
|
||||
: ?type_logger: (int * (Script.expr list * Script.expr list) -> unit) ->
|
||||
context -> Script.storage -> Script.code -> ex_script tzresult Lwt.t
|
||||
= fun ?type_logger ctxt
|
||||
{ storage; storage_type = init_storage_type }
|
||||
{ code; arg_type; ret_type; storage_type } ->
|
||||
trace
|
||||
(Ill_formed_type (Some "parameter", arg_type))
|
||||
(Lwt.return (parse_ty arg_type)) >>=? fun (Ex_ty arg_type) ->
|
||||
trace
|
||||
(Ill_formed_type (Some "return", ret_type))
|
||||
(Lwt.return (parse_ty ret_type)) >>=? fun (Ex_ty ret_type) ->
|
||||
trace
|
||||
(Ill_formed_type (Some "initial storage", init_storage_type))
|
||||
(Lwt.return (parse_ty init_storage_type)) >>=? fun (Ex_ty init_storage_type) ->
|
||||
trace
|
||||
(Ill_formed_type (Some "storage", storage_type))
|
||||
(Lwt.return (parse_ty storage_type)) >>=? fun (Ex_ty storage_type) ->
|
||||
let arg_type_full = Pair_t (arg_type, storage_type) in
|
||||
let ret_type_full = Pair_t (ret_type, storage_type) in
|
||||
Lwt.return (ty_eq init_storage_type storage_type) >>=? fun (Eq _) ->
|
||||
trace
|
||||
(Ill_typed_data (None, storage, storage_type))
|
||||
(parse_data ?type_logger ctxt storage_type storage) >>=? fun storage ->
|
||||
trace
|
||||
(Ill_typed_contract (code, arg_type, ret_type, storage_type, []))
|
||||
(parse_returning (Toplevel { storage_type }) ctxt ?type_logger arg_type_full ret_type_full code)
|
||||
>>=? fun code ->
|
||||
return (Ex_script { code; arg_type; ret_type; storage; storage_type })
|
||||
|
||||
Each specific type error from the typechecking process is wrapped in a
|
||||
more general error that explains which part of the program was
|
||||
malformed. This improves the error reporting. You can also see the bind
|
||||
operator used between functions to continue only if an error does not
|
||||
occur. This function also operates in the ``Lwt`` monad, which is
|
||||
largely hidden via the error monad.
|
203
docs/tutorials/michelson_anti_patterns.rst
Normal file
203
docs/tutorials/michelson_anti_patterns.rst
Normal file
@ -0,0 +1,203 @@
|
||||
Michelson Anti-Patterns
|
||||
=======================
|
||||
|
||||
Even though Michelson is designed to make it easy to write secure
|
||||
contracts and difficult to write vulnerable ones, it is still possible
|
||||
to write buggy contracts that leak data and funds. This is a list of
|
||||
mistakes that you can make when writing or interacting with contracts on
|
||||
the Tezos blockchain and alternative ways to write code that avoid these
|
||||
problems.
|
||||
|
||||
Note: We are currently reworking the concurrency model of Michelson (how
|
||||
and when sub-transactions are made), so that some of these patterns will
|
||||
be prevented by the language itself.
|
||||
|
||||
Refunding to a list of contracts
|
||||
--------------------------------
|
||||
|
||||
One common pattern in contracts is to refund a group of people’s funds
|
||||
at once. This is problematic if you accepted arbitrary contracts as a
|
||||
malicious user can do cause various issues for you.
|
||||
|
||||
Possible issues:
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- One contract swallows all the gas through a series of callbacks
|
||||
- One contract writes transactions until the block is full
|
||||
- Reentrancy bugs. Michelson intentionally makes these difficult to
|
||||
write, but it is still possible if you try.
|
||||
- A contract calls the \`FAIL\` instruction, stopping all computation.
|
||||
|
||||
Alternatives/Solutions:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Create a default account from people’s keys. Default accounts cannot
|
||||
execute code, avoiding the bugs above. Have people submit keys rather
|
||||
than contracts.
|
||||
- Have people pull their funds individually. Each user can break their
|
||||
own withdrawal only. **This does not protect against reentrancy
|
||||
bugs.**
|
||||
|
||||
Avoid batch operations when users can increase the size of the batch
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Contracts that rely on linear or super-linear operations are vulnerable
|
||||
to malicious users supplying values until the contract cannot finish
|
||||
without running into fuel limits. This can deadlock your contract.
|
||||
|
||||
.. _possible-issues-1:
|
||||
|
||||
Possible issues:
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- Malicious users can force your contract into a pathological worst
|
||||
case, stopping it from finishing with available gas. Note that in the
|
||||
absence of hard gas limits, this can still be disabling as node
|
||||
operators may not want to run contracts that take more than a certain
|
||||
amount of gas.
|
||||
- You may hit the slow case of an amortized algorithm or data structure
|
||||
at an inopportune time, using up all of your contract’s available
|
||||
gas.
|
||||
|
||||
.. _alternativessolutions-1:
|
||||
|
||||
Alternatives/Solutions:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Avoid data structures and algorithms that rely on amortized
|
||||
operations, especially when users may add data.
|
||||
- Restrict the amount of data your contract can store to a level that
|
||||
will not overwhelm the available gas.
|
||||
- Write your contract so that it may pause and resume batch operations.
|
||||
This would complicate these sequences and require constant checking
|
||||
of available gas, but it prevents various attacks.
|
||||
|
||||
\*Do not assume an attack will be prohibitively expensive\*
|
||||
Cryptocurrencies have extreme price fluctuations frequently and an
|
||||
extremely motivated attacker may decide that an enormous expense is
|
||||
justified. Remember, an attack that disables a contract is not just
|
||||
targeted at the authors, but also the users of that contract.
|
||||
|
||||
Signatures alone do not prevent replay attacks
|
||||
----------------------------------------------
|
||||
|
||||
If your contract uses signatures to authenticate messages, beware of
|
||||
replay attacks. If a user ever signs a piece of data, you *must* make
|
||||
sure that that piece of data is never again a valid message to the
|
||||
contract. If you do not do this, anyone else can call your contract with
|
||||
the same input and piggyback on the earlier approval.
|
||||
|
||||
.. _possible-issues-2:
|
||||
|
||||
Possible issues:
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- A previously approved action can be replayed.
|
||||
|
||||
.. _alternativessolutions-2:
|
||||
|
||||
Alternatives/Solutions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Use an internal counter to make the data you ask users to sign
|
||||
unique. This counter should be per key so that users can find out
|
||||
what they need to approve. This should be paired with a signed hash
|
||||
of your contract to prevent cross-contract replays.
|
||||
- Use the ``SOURCE`` instruction to verify that the expected sender is
|
||||
the source of the message.
|
||||
|
||||
Do not assume users will use a unique key for every smart contract
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Users should always use a different key for every contract with which
|
||||
they interact. If this is not the case, a message the user signed for
|
||||
another contract can be sent to your contract. An internal counter alone
|
||||
does not protect against this attack. It *must* be paired with a hash of
|
||||
your contract. You must verify the source of the message.
|
||||
|
||||
Storing/transferring private data
|
||||
---------------------------------
|
||||
|
||||
Once data is published to anyone, including broadcasting a transaction,
|
||||
that data is public. Never transmit secret information via any part of
|
||||
the blockchain ecosystem. As soon as you have broadcast a transaction
|
||||
including that piece of information, anyone can see it. Furthermore,
|
||||
malicious nodes in the system can manipulate unsigned transactions by
|
||||
delaying, modifying, or reordering them.
|
||||
|
||||
.. _possible-issues-3:
|
||||
|
||||
Possible Issues
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
- If data is not signed, it can be modified
|
||||
- Transactions can be delayed
|
||||
- Secret information will become public
|
||||
|
||||
.. _alternativessolutions-3:
|
||||
|
||||
Alternatives/Solutions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Do not store private information on the blockchain or broadcast it in
|
||||
transactions.
|
||||
- Sign all transactions that contain information that, if manipulated,
|
||||
could be abused.
|
||||
- Use counters to enforce transaction orders.
|
||||
|
||||
This will at least create a logical clock on messages sent to your
|
||||
contract.
|
||||
|
||||
Not setting all state before a transfer
|
||||
---------------------------------------
|
||||
|
||||
Reentrancy is a potential issue on the blockchain. When a contract makes
|
||||
a transfer to another contract, that contract can execute its own code,
|
||||
and can make arbitrary further transfers, including back to the original
|
||||
contract. If state has not been updated before the transfer is made, a
|
||||
contract can call back in and execute actions based on old state.
|
||||
|
||||
.. _possible-issues-4:
|
||||
|
||||
Possible Issues
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
- Multiple withdrawals/actions
|
||||
- Generating illegal state if state is updated twice later
|
||||
|
||||
.. _alternativessolutions-4:
|
||||
|
||||
Alternatives/Solutions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Forbid reentrancy by means of a flag in your storage, unless you have
|
||||
a good reason to allow users to reenter your contract, this is likely
|
||||
the best option.
|
||||
- Only make transfers to trusted contracts or default accounts. Default
|
||||
accounts cannot execute code, so it is always safe to transfer to
|
||||
them. Before trusting a contract, make sure that its behavior cannot
|
||||
be modified and that you have an extremely high degree of confidence
|
||||
in it.
|
||||
|
||||
Do not store funds for others in spendable contracts
|
||||
----------------------------------------------------
|
||||
|
||||
Tezos allows contracts to be marked as spendable. Managers of spendable
|
||||
contracts can make transfers using the funds stored inside the contract.
|
||||
This can subvert guarantees about the contract’s behavior that are
|
||||
visible in the code.
|
||||
|
||||
.. _possible-issues-5:
|
||||
|
||||
Possible Issues
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
- The funds of a contract can be removed.
|
||||
- A contract may not be able to meet its obligations
|
||||
|
||||
.. _alternativessolutions-5:
|
||||
|
||||
Alternatives/Solutions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Do not store funds in spendable contracts that you do not control.
|
48
docs/tutorials/protocol_environment.rst
Normal file
48
docs/tutorials/protocol_environment.rst
Normal file
@ -0,0 +1,48 @@
|
||||
.. _protocol_environment:
|
||||
|
||||
Economic protocol sandboxing
|
||||
============================
|
||||
|
||||
In Alpha, as in any sound future protocols, updates are approved by
|
||||
voting. That way, the responsibility of switching to a new protocol code
|
||||
is the responsibility of voters, and one could argue that it is up to
|
||||
them to check that the code does not call, for instance, unsafe array
|
||||
access functions.
|
||||
|
||||
Yet, we decided to introduce a minimum level of machine checks, by
|
||||
compiling with a specific compiler that checks that no known-unsafe
|
||||
function is used. This static form of sandboxing is performed by the
|
||||
OCaml typechecker: we simply compile protocols in a restricted set of
|
||||
modules with restricted interfaces that hide any unsafe, non wanted
|
||||
feature.
|
||||
|
||||
Another goal of that specific environment is maintaining a stable OCaml
|
||||
API for protocol development. Imagine that at some point, the OCaml
|
||||
standard library changes (a function is added or removed, a type is
|
||||
changed), then we will be able to upgrade to the new OCaml while still
|
||||
remaining compatible with past protocols, by providing an adapter layer.
|
||||
|
||||
Here is a quick description of each file in this environment:
|
||||
|
||||
- Files ``array.mli``, ``buffer.mli``, ``bytes.mli``, ``format.mli``,
|
||||
``int32.mli``, ``int64.mli``, ``list.mli``, ``map.mli``,
|
||||
``pervasives.mli``, ``set.mli`` and ``string.mli`` are stripped down
|
||||
interfaces to the OCaml standard library modules. The removed
|
||||
elements are: effects on toplevel references or channels, unsafe
|
||||
functions, functions that are known sources of bugs, and anything
|
||||
deprecated.
|
||||
- As we removed polymorphic comparison operators, ``compare.mli``
|
||||
implements monomorphic operators for standard OCaml and Tezos types.
|
||||
An example use is ``Compare.Int.(3 = 4)`` instead of plain OCaml
|
||||
``(3 = 4)``.
|
||||
- Files ``lwt*`` is the stripped down interface to Lwt, of which we
|
||||
removed any non deterministic functions, since we only use Lwt for
|
||||
asynchronous access to the storage.
|
||||
- Files ``data_encoding.mli``, ``error_monad.mli``, ``mBytes.mli``,
|
||||
``hash.mli``, ``base58.mli``, ``blake2B.mli``, ``ed25519.mli``,
|
||||
``hex_encode.mli``, ``json.mli``, ``time.mli``, ``z.mli``,
|
||||
``micheline.mli`` and files ``RPC_*`` are stripped down versions of
|
||||
the Tezos standard library.
|
||||
- Files ``tezos_data.mli``, ``context.mli``, ``fitness.mli`` and
|
||||
``updater.mli`` are interfaces to the shell’s data definitions and
|
||||
storage accessors that are accessible to the protocol.
|
2344
docs/whitedoc/michelson.rst
Normal file
2344
docs/whitedoc/michelson.rst
Normal file
File diff suppressed because it is too large
Load Diff
1558
docs/whitedoc/octopus.svg
Normal file
1558
docs/whitedoc/octopus.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 437 KiB |
59
docs/whitedoc/the_big_picture.rst
Normal file
59
docs/whitedoc/the_big_picture.rst
Normal file
@ -0,0 +1,59 @@
|
||||
.. _the_big_picture:
|
||||
|
||||
Tezos Software Architecture
|
||||
===========================
|
||||
|
||||
The diagram below shows a very coarse grained architecture of Tezos.
|
||||
|Tezos architecture diagram|
|
||||
|
||||
The characteristic that makes Tezos unique is its self-amending
|
||||
property. The part that amends itself is called the *economic protocol*
|
||||
(the green eye of the octopus), sometimes abbreviated by protocol or
|
||||
even proto in the source code. The rest of a Tezos node is what we call
|
||||
the *shell* (the blue octopus).
|
||||
|
||||
The protocol is responsible for interpreting the transactions and other
|
||||
administrative operations. It also has the responsibility to detect
|
||||
erroneous blocks.
|
||||
|
||||
An important thing to notice is that the protocol always sees only one
|
||||
block chain. In other words, a linear sequence of blocks since the
|
||||
genesis. It does not know that it lives in an open network where nodes
|
||||
can propose alternative heads.
|
||||
|
||||
Only the shell knows about the multiple heads. It is responsible for
|
||||
choosing between the various chain proposals that come from the bakers
|
||||
(the programs that cook new blocks) of the network. The shell has the
|
||||
responsibility of selecting and downloading alternative chains, feed
|
||||
them to the protocol, which in turn has the responsibility to check them
|
||||
for errors, and give them an absolute score. The shell then simply
|
||||
selects the valid head of highest absolute score. This part of the shell
|
||||
is called the validator.
|
||||
|
||||
The rest of the shell includes the peer-to-peer layer, the disk storage
|
||||
of blocks, the operations to allow the node to transmit the chain data
|
||||
to new nodes and the versioned state of the ledger. Inbetween the
|
||||
validator, the peer-to-peer layer and the storage sits a component
|
||||
called the distributed database, that abstracts the fetching and
|
||||
replication of new chain data to the validator.
|
||||
|
||||
Protocols are compiled using a tweaked OCaml compiler (green part on the
|
||||
left of the picture) that does two things. First, it checks that the
|
||||
protocol’s main module has the right type. A good analogy is to see
|
||||
protocol as plug-ins, and in this case, it means that it respects the
|
||||
common plugin interface. Then, it restricts the typing environment of
|
||||
the protocol’s code so that it only calls authorized modules and
|
||||
functions. Seeing protocols as plug-ins, it means that the code only
|
||||
called primitives from the plug-in API. It is a form of statically
|
||||
enforced sandboxing.
|
||||
|
||||
Finally, the RPC layer (in yellow on the right in the picture) is an
|
||||
important part of the node. It is how the client, third party
|
||||
applications and daemons can interact with the node and introspect its
|
||||
state. This component uses the mainstream JSON format and HTTP protocol.
|
||||
It uses in-house libraries ``ocplib-resto`` and ``ocplib-json-typed``
|
||||
(via the module :ref:`Data_encoding <data_encoding>`). It
|
||||
is fully inter-operable, and auto descriptive, using JSON schema.
|
||||
|
||||
.. |Tezos architecture diagram| image:: octopus.svg
|
||||
|
85
scripts/ocamldot.py
Executable file
85
scripts/ocamldot.py
Executable file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
from sets import Set
|
||||
|
||||
alldeps={}
|
||||
allmodules={}
|
||||
|
||||
def sanitize(s):
|
||||
s=re.sub('.*/','',s)
|
||||
s=re.sub('[^0-9a-zA-Z]+', '_', s)
|
||||
return s
|
||||
|
||||
def cleanName(s):
|
||||
ml = os.path.basename(s)
|
||||
(mod,_) = os.path.splitext(ml)
|
||||
return mod.capitalize()
|
||||
|
||||
def mangle(f,modulename):
|
||||
|
||||
dictionary = {}
|
||||
for line in open(f):
|
||||
s=line.split();
|
||||
for x in range(2, len(s)):
|
||||
mod = cleanName(s[0])
|
||||
dep = sanitize(s[x])
|
||||
if mod in dictionary :
|
||||
dictionary[mod].append(dep)
|
||||
else :
|
||||
dictionary[mod] = [dep]
|
||||
allmodules.update({mod : modulename})
|
||||
|
||||
alldeps[modulename] = dictionary
|
||||
|
||||
def cleanup(alldeps):
|
||||
# remove references to external libraries
|
||||
for (name,dictionary) in alldeps.iteritems() :
|
||||
for (mod,deps) in dictionary.iteritems() :
|
||||
dictionary[mod] = [x for x in deps if x in allmodules]
|
||||
alldeps[name] = dictionary
|
||||
return alldeps
|
||||
|
||||
def print_graph(alldeps):
|
||||
print("strict digraph G {")
|
||||
print('graph [fontsize=10 fontname="Verdana"];')
|
||||
print('node [shape=record fontsize=10 fontname="Verdana" compound=true];')
|
||||
counter = 0
|
||||
l = { x: i for i,x in enumerate(alldeps.keys())}
|
||||
for (name,dictionary) in alldeps.iteritems() :
|
||||
names = ['"%s"' % mod for mod in dictionary.keys()]
|
||||
if len(names) > 0 :
|
||||
print ('subgraph cluster_%i { label = "%s"; color=blue; node [style=filled];' % (l[name],name))
|
||||
counter += 1
|
||||
for (mod,deps) in dictionary.iteritems() :
|
||||
for dep in deps :
|
||||
if dep in dictionary :
|
||||
print ('"%s" -> "%s";' % (mod,dep))
|
||||
print "}"
|
||||
# for (mod,deps) in dictionary.iteritems() :
|
||||
# for dep in deps :
|
||||
# if dep not in dictionary :
|
||||
# print ('"%s" -> "%s" [ltail=cluster_%i lhead=cluster_%i];' % (mod,dep,l[name],l[allmodules[dep]]))
|
||||
print "}"
|
||||
|
||||
def scan(directories):
|
||||
ext = ".depends.ocamldep-output"
|
||||
for directory in directories:
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for f in files:
|
||||
if f.endswith(ext):
|
||||
mangle(os.path.join(root, f),f[:-len(ext)])
|
||||
print_graph(cleanup(alldeps))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='OcamlDep Dependency Tree')
|
||||
parser.add_argument('inputdirs', type=str, nargs='*', help="directories to scan")
|
||||
args = parser.parse_args()
|
||||
|
||||
scan(args.inputdirs)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -95,7 +95,7 @@ val complete: ?alphabet:Alphabet.t -> string -> string list Lwt.t
|
||||
|
||||
(** {1 Low-level: distinct registering function for economic protocol} *)
|
||||
|
||||
(** See [src/environment/v1/base58.mli]} for an inlined
|
||||
(** See [src/environment/v1/base58.mli] for an inlined
|
||||
documentation. *)
|
||||
module Make(C: sig type context end) : sig
|
||||
|
||||
|
@ -25,7 +25,7 @@ val shift: t -> int -> t
|
||||
|
||||
val blit: t -> int -> t -> int -> int -> unit
|
||||
(** [blit src ofs_src dst ofs_dst len] copy [len] bytes from [src]
|
||||
starting at [ofs_src] into [dst] starting at [ofs_dst].] *)
|
||||
starting at [ofs_src] into [dst] starting at [ofs_dst]. *)
|
||||
|
||||
val blit_from_string: string -> int -> t -> int -> int -> unit
|
||||
(** See [blit] *)
|
||||
|
@ -34,7 +34,7 @@ val shift: t -> int -> t
|
||||
|
||||
val blit: t -> int -> t -> int -> int -> unit
|
||||
(** [blit src ofs_src dst ofs_dst len] copy [len] bytes from [src]
|
||||
starting at [ofs_src] into [dst] starting at [ofs_dst].] *)
|
||||
starting at [ofs_src] into [dst] starting at [ofs_dst]. *)
|
||||
|
||||
val blit_from_string: string -> int -> t -> int -> int -> unit
|
||||
(** See [blit] *)
|
||||
|
@ -33,7 +33,7 @@ val arg : doc:string -> parameter:string ->
|
||||
('p option, 'ctx) arg
|
||||
|
||||
(** Create an argument that will contain the [~default] value if it is not provided.
|
||||
@see arg *)
|
||||
see arg *)
|
||||
val default_arg : doc:string -> parameter:string ->
|
||||
default:string ->
|
||||
('p, 'ctx) parameter ->
|
||||
|
Loading…
Reference in New Issue
Block a user