Upgrade of documentation: build, config, contributing, readme

pull/50/head
Jakub Matys 2018-08-01 15:49:06 +02:00
parent 81d8780a92
commit ef12666f70
15 changed files with 809 additions and 343 deletions

85
CONTRIBUTING.md 100644
View File

@ -0,0 +1,85 @@
# Blockbook Contributor Guide
Blockbook is back-end service for Trezor wallet. Although it is open source, design and development of core packages
is done by Trezor developers in order to keep Blockbook compatible with Trezor. If you feel you could use Blockbook
for another purposes, we recommend you to make a fork.
However you can still help us find bugs or add support for new coins.
## Development environment
Instructions to set up your development environment and build Blockbook are described in separated
[document](/docs/build.md).
## How can I contribute?
### Reporting bugs
### Adding coin support
Trezor harware wallet supports over 500 coins, see https://trezor.io/coins/. You are free to add support for any of
them to Blockbook. Actually implemented coins are listed [here](/docs/ports.md).
You should follow few steps bellow to get smooth merge of your PR.
> Altough we are happy for support of new coins we have not enough capacity to run them all on our infrastructure.
> Actually we can run Blockbook instances only for coins supported by Trezor wallet. If you want to have Blockbook
> instance for your coin, you will have to deploy your own server.
#### Add coin definition
Coin definitions are stored in JSON files in *configs/coins* directory. They are single source of Blockbook
configuration, Blockbook and back-end package definition and build metadata. Since Blockbook supports only single
coin index per running instance, every coin (including testnet) must have single definition file.
All options of coin definition are described in [config.md](/docs/config.md).
Because most of coins are fork of Bitcoin and they have similar way to install and configure their daemon, we use
templates to generate package definition and configuration files during build process. It is similar to build Blockbook
package too. Templates are filled with data from coin definition. Although build process generate packages
automatically, there is sometimes necessary see intermediate step. You can generate all files by calling
`go run build/templates/generate.go coin` where *coin* is name of definition file without .json extension. Files are
generated to *build/pkg-defs* directory.
Good examples of coin configuration are
[*configs/coins/bitcoin.json*](configs/coins/bitcoin.json) and
[*configs/coins/ethereum.json*](configs/coins/ethereum.json) for Bitcoin-like coins and different coins, respectively.
Usually you have to update only few options that differ from Bitcoin definition. At first there are base information
about coin in section *coin* name, alias etc. Then update port information in *port* section. We keep port series as
listed in [port registry](/docs/ports.md). Select next port numbers in series. Port numbers must be unique across all
port definitions.
In section *backend* update information how to build and configure backend service. When back-end package is built,
build process downloads installation archive, verify and extract it. How it is done is described in
[build guide](/docs/build.md#on-back-end-building). Naming conventions and versioning are described
also in [build guide](/docs/build.md#on-naming-conventions-and-versioning). You have to update *package_name*,
*package_revision*, *system_user*, *version*, *binary_url*, *verification_type*, *verification_source*, *extract_command* and
*exclude_files*. Also update information whether service runs mainnet or testnet network in *mainnet* option.
In section *blockbook* update information how to build and configure Blockbook service. Usually they are only
*package_name*, *system_user* and *explorer_url* options. Naming conventions are are described
[here](/docs/build.md#on-naming-conventions-and-versioning).
Update *package_maintainer* and *package_maintainer_email* options in section *meta*.
Execute script *contrib/scripts/generate-port-registry.go* that will update *docs/ports.md*.
Now you can try generate package definitions as described above in order to check outputs.
##### Go template evaluation note
We use *text/template* package to generate package definitions and configuration files. Some options in coin definition
are also templates and are executed inside base template. Use `{{.path}}` syntax to refer values in coin definition,
where *.path* can be for example *.Blockbook.BlockChain.Parse*. Go uses CammelCase notation so references inside templates
as well. Note that dot at the beginning is mandatory. Go template syntax is fully documented
[here](https://godoc.org/text/template).
TODO:
* script that checks unique port numbers
#### Add coin implementation
#### Add tests
#### Deploy public server

153
README.md
View File

@ -1,161 +1,40 @@
# blockbook
# Blockbook
## **blockbook is currently in the state of heavy development, do not expect this documentation to be up to date**
> **Blockbook is currently in the state of heavy development, do not expect this documentation to be up to date**
## Build and install using docker
## Build and installation instructions
Run in the project root
```
make all
```
to create blockbook debian packages.
## Install manually
Setup go environment (Debian 9):
```
sudo apt-get update && apt-get install -y \
build-essential git wget pkg-config lxc-dev libzmq3-dev libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev
cd /opt
wget https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz && tar xf go1.9.2.linux-amd64.tar.gz
sudo ln -s /opt/go/bin/go /usr/bin/go
go help gopath
```
Install RocksDB: https://github.com/facebook/rocksdb/blob/master/INSTALL.md
and compile the static_lib and tools
```
git clone https://github.com/facebook/rocksdb.git
cd rocksdb
make release
```
Setup variables for gorocksdb: https://github.com/tecbot/gorocksdb
```
export CGO_CFLAGS="-I/path/to/rocksdb/include"
export CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4"
```
Install ZeroMQ: https://github.com/zeromq/libzmq
Install go-dep tool:
```
RUN go get github.com/golang/dep/cmd/dep
```
Get blockbook sources, install dependencies, build:
```
cd $GOPATH/src
git clone https://github.com/trezor/blockbook.git
cd blockbook
dep ensure
go build
```
## Usage
```
./blockbook --help
```
## Example command
To run blockbook with fast synchronization, connection to ZeroMQ and providing https and socket.io interface, with database in local directory *data* and connected to local bitcoind with configuration specified by parameter *-blockchaincfg*:
```
./blockbook -sync -blockchaincfg=configs/bitcoin_testnet.json -internal=127.0.0.1:8333 -public=127.0.0.1:8334 -certfile=server/testcert -logtostderr
```
Blockbook logs to stderr *-logtostderr* or to directory specified by parameter *-log_dir* . Verbosity of logs can be tuned by command line parameters *-v* and *-vmodule*, details at https://godoc.org/github.com/golang/glog
Develper build guide is [here](/docs/build.md).
Sysadmin installation guide is [here](https://wiki.trezor.io/Blockbook).
# Implemented coins
- [Bitcoin](bchain/coins/btc/btc.md)
- [Bitcoin Testnet](bchain/coins/btc/btctestnet.md)
The most significant coins implemented by Blockbook are:
- Bitcoin
- Bitcoin Testnet
- Bcash
- Bcash Testnet
- Bgold
- [ZCash](bchain/coins/zec/zec.md)
- ZCash
- ZCash Testnet
- Dash
- Dash Testnet
- Litecoin
- Litecoin Testnet
- [Ethereum](bchain/coins/eth/eth.md)
- [Ethereum Testnet Ropsten](bchain/coins/eth/ethropsten.md)
- Ethereum
- Ethereum Testnet Ropsten
They are also supported by Trezor wallet. List of all coins is [here](/docs/ports.md).
# Data storage in RocksDB
Blockbook stores data the key-value store RocksDB. Data are stored in binary form to save space.
The data are separated to different column families:
- **default**
at the moment not used, will store statistical data etc.
- **height** - maps *block height* to *block hash*
*Block heigh* stored as array of 4 bytes (big endian uint32)
*Block hash* stored as array of 32 bytes
Example - the first four blocks (all data hex encoded)
```
0x00000000 : 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943
0x00000001 : 0x00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206
0x00000002 : 0x000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820
0x00000003 : 0x000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10
```
- **outputs** - maps *output script+block height* to *array of outpoints*
*Output script (ScriptPubKey)+block height* stored as variable length array of bytes for output script + 4 bytes (big endian uint32) block height
*array of outpoints* stored as array of 32 bytes for transaction id + variable length outpoint index for each outpoint
Example - (all data hex encoded)
```
0x001400efeb484a24a1c1240eafacef8566e734da429c000e2df6 : 0x1697966cbd76c75eb9fc736dfa3ba0bc045999bab1e8b10082bc0ba546b0178302
0xa9143e3d6abe282d92a28cb791697ba001d733cefdc7870012c4b1 : 0x7246e79f97b5f82e7f51e291d533964028ec90be0634af8a8ef7d5a903c7f6d301
```
- **inputs** - maps *transaction outpoint* to *input transaction* that spends it
*Transaction outpoint* stored as array of 32 bytes for transaction id + variable length outpoint index
*Input transaction* stored as array of 32 bytes for transaction id + variable length input index
Example - (all data hex encoded)
```
0x7246e79f97b5f82e7f51e291d533964028ec90be0634af8a8ef7d5a903c7f6d300 : 0x0a7aa90ea0269c79f844c516805e4cac594adb8830e56fca894b66aab19136a428
0x7246e79f97b5f82e7f51e291d533964028ec90be0634af8a8ef7d5a903c7f6d301 : 0x4303a9fcfe6026b4d33ba488df6443c9a99bca7b7fcb7c6f6cd65cea24a749b700
```
Blockbook stores data the key-value store RocksDB. Database format is described [here](/docs/rocksdb.md).
## Registry of ports
| coin | blockbook internal port | blockbook public port | backend rpc port | zmq port |
|--------------------------|-------------------------|-----------------------|--------------------|----------|
| Bitcoin | 9030 | 9130 | 8030 | 38330 |
| Bcash | 9031 | 9131 | 8031 | 38331 |
| Zcash | 9032 | 9132 | 8032 | 38332 |
| Dash | 9033 | 9133 | 8033 | 38333 |
| Litecoin | 9034 | 9134 | 8034 | 38334 |
| Bgold | 9035 | 9135 | 8035 | 38335 |
| Ethereum | 9036 | 9136 | 8036 ws, 8136 http | 38336* |
| Ethereum Classic | 9037 | 9137 | 8037 | 38337* |
| Dogecoin | 9038 | 9138 | 8038 | 38338 |
| Namecoin | 9039 | 9139 | 8039 | 38339 |
| Vertcoin | 9040 | 9140 | 8040 | 38340 |
| Bitcoin Testnet | 19030 | 1913 | 18030 | 48330 |
| Bcash Testnet | 19031 | 1913 | 18031 | 48331 |
| Zcash Testnet | 19032 | 1913 | 18032 | 48332 |
| Dash Testnet | 19033 | 1913 | 18033 | 48333 |
| Litecoin Testnet | 19034 | 1913 | 18034 | 48334 |
| Ethereum Testnet Ropsten | 19036 | 19136 | 18036 | 48336* |
| Vertcoin Testnet | 19040 | 19140 | 18040 | 48340 |
\* geth listens on this port, however not as zmq service
Reserved ports are described [here](/docs/ports.md)
## Todo

View File

@ -1,50 +0,0 @@
## BTC Setup
Get Bitcoin Core
```
wget https://bitcoin.org/bin/bitcoin-core-0.16.0/bitcoin-0.15.1-x86_64-linux-gnu.tar.gz
tar -xf bitcoin-0.16.0-x86_64-linux-gnu.tar.gz
```
Data are stored in */data/btc*, in folders */data/btc/bitcoin* for Bitcoin Core data, */data/btc/blockbook* for Blockbook data.
Create configuration file */data/btc/bitcoin/bitcoin.conf* with content
```
daemon=1
server=1
rpcuser=rpc
rpcpassword=rpc
rpcport=8030
txindex=1
```
Create script that starts the bitcoind daemon *run-btc-bitcoind.sh* with increased rpcworkqueue and configured zeromq
```
#!/bin/bash
bitcoin-0.15.1/bin/bitcoind -datadir=/data/btc/bitcoin -rpcworkqueue=32 -zmqpubhashtx=tcp://127.0.0.1:38330 -zmqpubhashblock=tcp://127.0.0.1:38330 -zmqpubrawblock=tcp://127.0.0.1:38330 -zmqpubrawtx=tcp://127.0.0.1:38330
```
Run the *run-btc-bitcoind.sh* to get initial import of data.
Create blockchain configuration file */data/testnet/blockbook/btc.json*
```
{
"rpcURL": "http://127.0.0.1:8030",
"rpcUser": "rpc",
"rpcPass": "rpc",
"rpcTimeout": 25,
"parse": true,
"zeroMQBinding": "tcp://127.0.0.1:38330"
}
```
Create script that runs blockbook *run-btc-blockbook.sh*
```
#!/bin/bash
cd go/src/blockbook
./blockbook -coin=btc -blockchaincfg=/data/btc/blockbook/btc.json -datadir=/data/btc/blockbook/db -sync -internal=:9030 -public=:9130 -certfile=server/testcert -explorer=https://bitcore1.trezor.io/ $1
```
To run blockbook with logging to file (run with nohup or daemonize or using screen)
```
./run-btc-blockbook.sh 2>/data/btc/blockbook/blockbook.log
```

View File

@ -1,49 +0,0 @@
## BTC Testnet Setup
Get Bitcoin Core
```
wget https://bitcoin.org/bin/bitcoin-core-0.16.0/bitcoin-0.15.1-x86_64-linux-gnu.tar.gz
tar -xf bitcoin-0.16.0-x86_64-linux-gnu.tar.gz
```
Data are stored in */data/testnet*, in folders */data/testnet/bitcoin* for Bitcoin Core data, */data/testnet/blockbook* for Blockbook data.
Create configuration file */data/testnet/bitcoin/bitcoin.conf* with content
```
testnet=1
daemon=1
server=1
rpcuser=rpc
rpcpassword=rpc
rpcport=18030
txindex=1
```
Create script that starts the bitcoind daemon *run-testnet-bitcoind.sh* with increased rpcworkqueue and configured zeromq
```
#!/bin/bash
bitcoin-0.15.1/bin/bitcoind -datadir=/data/testnet/bitcoin -rpcworkqueue=32 -zmqpubhashtx=tcp://127.0.0.1:48330 -zmqpubhashblock=tcp://127.0.0.1:48330 -zmqpubrawblock=tcp://127.0.0.1:48330 -zmqpubrawtx=tcp://127.0.0.1:48330
```
Run the *run-testnet-bitcoind.sh* to get initial import of data.
Create blockchain configuration file */data/testnet/blockbook/btc-testnet.json*
```
{
"rpcURL": "http://127.0.0.1:18030",
"rpcUser": "rpc",
"rpcPass": "rpc",
"rpcTimeout": 25,
"parse": true,
"zeroMQBinding": "tcp://127.0.0.1:48330"
}
```
Create script that runs blockbook *run-testnet-blockbook.sh*
```
#!/bin/bash
cd go/src/blockbook
./blockbook -coin=btc-testnet -blockchaincfg=/data/testnet/blockbook/btc-testnet.json -datadir=/data/testnet/blockbook/db -sync -internal=:19030 -public=:19130 -certfile=server/testcert -explorer=https://testnet-bitcore1.trezor.io $1
```
To run blockbook with logging to file (run with nohup or daemonize or using screen)
```
./run-testnet-blockbook.sh 2>/data/testnet/blockbook/blockbook.log
```

View File

@ -1,25 +0,0 @@
## Ethereum Testnet Setup
Get Ethereum
```
git clone https://github.com/ethereum/go-ethereum
cd go-ethereum/
make geth
```
Data are stored in */data/eth*, in folders */data/eth/eth* for Ethereum data, */data/eth/blockbook* for Blockbook data.
Run geth with rpc and websocket interfaces, bound to all ip addresses - insecure! (run with nohup or daemonize or using screen)
```
go-ethereum/build/bin/geth --syncmode "full" --cache 1024 --datadir /data/eth/eth --port "35555" --rpc --rpcport 8545 -rpcaddr 0.0.0.0 --rpccorsdomain "*" --ws --wsaddr 0.0.0.0 --wsport 8546 --wsorigins "*" 2>/data/eth/eth/eth.log
```
Create script that runs blockbook *run-eth-blockbook.sh*
```
#!/bin/bash
cd go/src/blockbook
./blockbook -coin=eth -blockchaincfg=/data/eth/blockbook/eth.json -datadir=/data/eth/blockbook/db -sync -internal=:8555 -public=:8556 -certfile=server/testcert $1
```
To run blockbook with logging to file (run with nohup or daemonize or using screen)
```
./run-eth-blockbook.sh 2>/data/eth/blockbook/blockbook.log
```

View File

@ -1,25 +0,0 @@
## Ethereum Testnet Setup
Get Ethereum
```
git clone https://github.com/ethereum/go-ethereum
cd go-ethereum/
make geth
```
Data are stored in */data/eth-testnet*, in folders */data/eth-testnet/eth* for Ethereum data, */data/eth-testnet/eth/blockbook* for Blockbook data.
Run geth with rpc and websocket interfaces, bound to all ip addresses - insecure! (run with nohup or daemonize or using screen)
```
go-ethereum/build/bin/geth --testnet --datadir /data/eth-testnet/eth --rpc --rpcport 18545 -rpcaddr 0.0.0.0 --rpccorsdomain "*" --ws --wsaddr 0.0.0.0 --wsport 18546 --wsorigins "*" 2>/data/eth-testnet/eth/eth.log
```
Create script that runs blockbook *run-eth-testnet-blockbook.sh*
```
#!/bin/bash
cd go/src/blockbook
./blockbook -coin=eth-testnet -blockchaincfg=/data/eth-testnet/blockbook/eth-testnet.json -datadir=/data/eth-testnet/blockbook/db -sync -internal=:18555 -public=:18556 -certfile=server/testcert $1
```
To run blockbook with logging to file (run with nohup or daemonize or using screen)
```
./run-eth-testnet-blockbook.sh 2>/data/eth-testnet/blockbook/blockbook.log
```

View File

@ -1,57 +0,0 @@
## Zcash Setup
Get Zcash client
```
wget https://z.cash/downloads/zcash-1.0.15-linux64.tar.gz
tar xzf zcash-1.0.15-linux64.tar.gz
```
Run command to download the parameters used to create and verify shielded transactions:
```
zcash-1.0.15/bin/zcash-fetch-params
```
Data are stored in */data/zec* , in folders */data/zec/zcash* for Zcash client data, */data/zec/blockbook* for Blockbook data.
Create configuration file */data/zec/zcash/zcash.conf* with content
```
daemon=1
server=1
rpcuser=rpc
rpcpassword=rpc
rpcport=8032
txindex=1
mainnet=1
addnode=mainnet.z.cash
```
Create script *run-zec-zcashd.sh* that starts the zcashd daemon with increased rpcworkqueue and configured zeromq
```
#!/bin/bash
zcash-1.0.15/bin/zcashd -datadir=/data/zec/zcash -rpcworkqueue=32 -zmqpubhashblock=tcp://127.0.0.1:38332 -zmqpubrawblock=tcp://127.0.0.1:38332 -zmqpubhashtx=tcp://127.0.0.1:38332 -zmqpubrawtx=tcp://127.0.0.1:38332
```
Run the *run-zec-zcashd.sh* to get initial import of data.
Create blockchain configuration file */data/zec/blockbook/zec.json*
```
{
"rpcURL": "http://127.0.0.1:8032",
"rpcUser": "rpc",
"rpcPass": "rpc",
"rpcTimeout": 25,
"parse": true,
"zeroMQBinding": "tcp://127.0.0.1:38332"
}
```
Create *run-zec-blockbook.sh* script that starts blockbook
```
#!/bin/bash
./blockbook -coin=zec -blockchaincfg=/data/zec/blockbook/zec.json -datadir=/data/zec/blockbook/db -sync -internal=:9032 -public=:9132 -certfile=server/testcert -explorer=https://zec-bitcore1.trezor.io $1
```
To run blockbook with logging to file (run with nohup or daemonize using screen)
```
./run-zec-blockbook.sh 2> /data/zec/blockbook/blockbook.log
```

View File

@ -8,6 +8,8 @@
"ports": {
"backend_rpc": 8036,
"backend_message_queue": 0,
"backend_p2p": 38336,
"backend_http": 8136,
"blockbook_internal": 9036,
"blockbook_public": 9136
},

View File

@ -0,0 +1,14 @@
{
"coin": {
"name": "Ethereum Classic",
"shortcut": "ETC",
"label": "Ethereum Classic",
"alias": "ethereum_classic"
},
"ports": {
"backend_rpc": 8037,
"backend_message_queue": 0,
"blockbook_internal": 9037,
"blockbook_public": 9137
}
}

View File

@ -8,6 +8,7 @@
"ports": {
"backend_rpc": 18036,
"backend_message_queue": 0,
"backend_p2p": 48336,
"blockbook_internal": 19036,
"blockbook_public": 19136
},

View File

@ -0,0 +1,263 @@
//usr/bin/go run $0 $@ ; exit
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
"sort"
"strings"
)
const (
inputDir = "configs/coins"
outputFile = "docs/ports.md"
)
type PortInfo struct {
CoinName string
BlockbookInternalPort uint16
BlockbookPublicPort uint16
BackendRPCPort uint16
BackendServicePorts map[string]uint16
}
type PortInfoSlice []*PortInfo
type Config struct {
Coin struct {
Name string `json:"name"`
}
Ports map[string]uint16 `json:"ports"`
}
func main() {
output := "stdout"
if len(os.Args) > 1 {
if len(os.Args) == 2 && os.Args[1] == "-w" {
output = outputFile
} else {
fmt.Fprintf(os.Stderr, "Usage: %s [-w]\n", filepath.Base(os.Args[0]))
fmt.Fprintf(os.Stderr, " -w write output to %s instead of stdout\n", outputFile)
os.Exit(1)
}
}
slice, err := loadPortInfo(inputDir)
if err != nil {
panic(err)
}
sortPortInfo(slice)
err = writeMarkdown(output, slice)
if err != nil {
panic(err)
}
}
func loadPortInfo(dir string) (PortInfoSlice, error) {
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
items := make(PortInfoSlice, 0, len(files))
for _, fi := range files {
if fi.IsDir() || fi.Name()[0] == '.' {
continue
}
path := filepath.Join(dir, fi.Name())
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("%s: %s", path, err)
}
defer f.Close()
v := Config{}
d := json.NewDecoder(f)
err = d.Decode(&v)
if err != nil {
return nil, fmt.Errorf("%s: json: %s", path, err)
}
item := &PortInfo{CoinName: v.Coin.Name, BackendServicePorts: map[string]uint16{}}
for k, v := range v.Ports {
if v == 0 {
continue
}
switch k {
case "blockbook_internal":
item.BlockbookInternalPort = v
case "blockbook_public":
item.BlockbookPublicPort = v
case "backend_rpc":
item.BackendRPCPort = v
default:
if len(k) > 8 && k[:8] == "backend_" {
item.BackendServicePorts[k[8:]] = v
}
}
}
items = append(items, item)
}
return items, nil
}
func sortPortInfo(slice PortInfoSlice) {
// normalizes values in order to sort zero values at the bottom of the slice
normalize := func(a, b uint16) (uint16, uint16) {
if a == 0 {
a = math.MaxUint16
}
if b == 0 {
b = math.MaxUint16
}
return a, b
}
// sort values by BlockbookPublicPort, then by BackendRPCPort and finally by
// CoinName; zero values are sorted at the bottom of the slice
sort.Slice(slice, func(i, j int) bool {
a, b := normalize(slice[i].BlockbookPublicPort, slice[j].BlockbookPublicPort)
if a < b {
return true
}
if a > b {
return false
}
a, b = normalize(slice[i].BackendRPCPort, slice[j].BackendRPCPort)
if a < b {
return true
}
if a > b {
return false
}
return strings.Compare(slice[i].CoinName, slice[j].CoinName) == -1
})
}
func writeMarkdown(output string, slice PortInfoSlice) error {
var (
buf bytes.Buffer
err error
)
fmt.Fprintf(&buf, "# Registry of ports\n\n")
header := []string{"coin", "blockbook internal port", "blockbook public port", "backend rpc port", "backend service ports (zmq)"}
writeTable(&buf, header, slice)
fmt.Fprintf(&buf, "\n> NOTE: This document is generated from coin definitions in `configs/coins`.\n")
out := os.Stdout
if output != "stdout" {
out, err = os.OpenFile(output, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer out.Close()
}
n, err := out.Write(buf.Bytes())
if err != nil {
return err
}
if n < len(buf.Bytes()) {
return io.ErrShortWrite
}
return nil
}
func writeTable(w io.Writer, header []string, slice PortInfoSlice) {
rows := make([][]string, len(slice))
for i, item := range slice {
row := make([]string, len(header))
row[0] = item.CoinName
if item.BlockbookInternalPort > 0 {
row[1] = fmt.Sprintf("%d", item.BlockbookInternalPort)
}
if item.BlockbookPublicPort > 0 {
row[2] = fmt.Sprintf("%d", item.BlockbookPublicPort)
}
if item.BackendRPCPort > 0 {
row[3] = fmt.Sprintf("%d", item.BackendRPCPort)
}
svcPorts := make([]string, 0, len(item.BackendServicePorts))
for k, v := range item.BackendServicePorts {
var s string
if k == "message_queue" {
s = fmt.Sprintf("%d", v)
} else {
s = fmt.Sprintf("%d %s", v, k)
}
svcPorts = append(svcPorts, s)
}
row[4] = strings.Join(svcPorts, ", ")
rows[i] = row
}
padding := make([]int, len(header))
for column := range header {
padding[column] = len(header[column])
for _, row := range rows {
padding[column] = maxInt(padding[column], len(row[column]))
}
}
content := make([][]string, 0, len(rows)+2)
content = append(content, paddedRow(header, padding))
content = append(content, delim("-", padding))
for _, row := range rows {
content = append(content, paddedRow(row, padding))
}
for _, row := range content {
fmt.Fprintf(w, "|%s|\n", strings.Join(row, "|"))
}
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func paddedRow(row []string, padding []int) []string {
out := make([]string, len(row))
for i := 0; i < len(row); i++ {
format := fmt.Sprintf(" %%-%ds ", padding[i])
out[i] = fmt.Sprintf(format, row[i])
}
return out
}
func delim(str string, padding []int) []string {
out := make([]string, len(padding))
for i := 0; i < len(padding); i++ {
out[i] = strings.Repeat(str, padding[i]+2)
}
return out
}

263
docs/build.md 100644
View File

@ -0,0 +1,263 @@
# Blockbook Build Guide
## Setting up your development environment
Supported environment to develop Blockbook is Linux. Although it is possible build and run Blockbook on macOS
or Windows our build process is not prepared for it. But you can still build Blockbook [manually](#manual-build).
The only dependency required to build Blockbook is Docker. You can see how to install Docker [here](https://docs.docker.com/install/linux/docker-ce/debian/).
Manual build require additional dependencies that are described in appropriate section.
## Build in Docker environment
All build operations run in Docker container in order to keep build environment isolated. Makefile in root of repository
define few targets used for building, testing and packaging of Blockbook. With Docker image definitions and Debian
package templates in *build/docker* and *build/templates* respectively, they are only inputs that make build process.
Docker build images are created at first execution of Makefile and that information is persisted. (Actually there are
created two files in repository .bin-image and .deb-image that are used as tags.) Sometimes it is necessary to
rebuild Docker images, it is possible by executing `make build-images`.
### Building binary
Just run `make` and that is it. Output binary is stored in *build* directory. Note that although Blockbook is Go application
it is dynamically linked with RocksDB dependencies and ZeroMQ. Therefore operating system where Blockbook will be
executed still need that dependencies installed. See [Manual build](#manual-build) instructions below or install
Blockbook via Debian packages.
### Building debug binary
Standard binary contains no debug symbols. Execute `make build-debug` to get binary for debugging.
### Testing
How to execute tests is described in separate document [here](/docs/testing.md).
### Building Debian packages
Blockbook and particular coin back-end are usually deployed together. They are defined in same place as well.
So typical way to build Debian packages is build Blockbook and back-end deb packages by single command. But it is not
mandatory, of course.
> Early releases of Blockbook weren't so friendly for extending. One had to define back-end package, Blockbook package,
> back-end configuration and Blockbook configuration as well. There were many options that were duplicated across
> configuration files and therefore error prone.
>
> Actually all configuration options and also build options for both Blockbook and backend are defined in single JSON
> file and all stuff required during build is generated dynamically.
Makefile targets follow simple pattern, there are few prefixes that define what to build.
* *deb-blockbook-&lt;coin&gt;* Build Blockbook package for given coin.
* *deb-backend-&lt;coin&gt;* Build back-end package for given coin.
* *deb-&lt;coin&gt;* Build both Blockbook and back-end packages for given coin.
* *all-&lt;coin&gt;* Similar to deb-&lt;coin&gt; but clean repository and rebuild Docker image before package build. It is useful
for production deployment.
* *all* Build both Blockbook and back-end packages for all coins.
Which coins are possible to build is defined in *configs/coins*. Particular coin has to have JSON config file there.
For example we want to build some packages for Bitcoin and Bitcoin Testnet.
```bash
# make all-bitcoin deb-backend-bitcoin_testnet
...
# ls build/*.deb
build/backend-bitcoin_0.16.1-satoshilabs-1_amd64.deb build/backend-bitcoin-testnet_0.16.1-satoshilabs-1_amd64.deb build/blockbook-bitcoin_0.0.6_amd64.deb
```
We have built two backend packages for Bitcoin and Testnet and Blockbook package for Bitcoin. Before build have been
performed there was cleaned build directory and rebuilt Docker image.
### Extra variables
There are few variables that can be passed to make in order to modify build process.
In general, build of Blockbook binary require some dependencies. They are downloaded automatically during build process
but if you need to build binary repeatedly it consumes a lot of time. Here comes variable *UPDATE_VENDOR* that if is
unset says that build process uses *vendor* (i.e. dependencies) from your local repository. For example:
`make deb-bitcoin UPDATE_VENDOR=0`. But before the command is executed there must be *vendor* directory populated,
you can do it by calling `dep ensure --vendor-only`. See [Manual build](#manual-build) instructions below.
All build targets allow pass additional parameters to underlying command inside container. It is possible via ARGS
variable. For example if you want run only subset of unit-tests, you will perform it by calling:
`make test ARGS='-run TestBitcoinRPC' UPDATE_VENDOR=0`
Common behaviour of Docker image build is that build steps are cached and next time they are executed much faster.
Although this is a good idea, when something went wrong you will need to override this behaviour somehow. Execute this
command: `make build-images NO_CACHE=true`.
### On naming conventions and versioning
**install and data directories**
Both Blockbook and back-end have separated install and data directories. They use common preffix and are defined in
*configs/environ.json* and all templates use them.
Back-end install directory is */opt/coins/nodes/&lt;coin&gt;*.
Back-end data directory is */opt/coins/data/&lt;coin&gt;/backend*.
Blockbook install directory is */opt/coins/blockbook/&lt;coin&gt;*.
Blockbook data directory is */opt/coins/data/&lt;coin&gt;/blockbook*.
*coin* used above is defined in *coin.alias* in coin definition file.
**package names**
Package names are defined in *backend.package_name* and *blockbook.package_name* in coin definition file. We use
simple pattern *&lt;prefix&gt;-&lt;coin&gt;* to name packages where *prefix* is either *blockbook* or *backend* and
*coin* is made similarly to *coin.alias*. We use convention that coin name uses lowercase characters and dash '-' as
a word delimiter. Testnet versions of coins must have *-testnet* suffix. That differs from *coin.alias* because
underscore has a special meaning in Debian packaging. For example there are packages *backend-bitcoin* and
*blockbook-bitcoin-testnet*.
**user names**
User names are defined in *backend.system_user* and *blockbook.system_user* in coin definition file. We follow common
Linux conventions, user names use lowercase characters and dash '-' as a word delimiter.
Back-end user name use coin name only, including testnet services. For example there is *bitcoin* user for both
*backend-bitcoin* and *backend-bitcoin-testnet* packages.
Blockbook user name has *blockbook-* prefix and coin name (made same as back-end version). For example there is
*blockbook-bitcoin* user for both *blockbook-bitcoin* and *blockbook-bitcoin-testnet* packages.
**back-end versioning**
Since we have to distinguish version of coin distribution and version of our configuration we follow standard Debian
package versioning rules (for details see
[Debian policy](https://www.debian.org/doc/debian-policy/ch-controlfields.html#version)). There is upstream version
and revision both defined in coin definition file in *backend.version* and *backend.package_revision*, respectively.
**blockbook versioning**
Blockbook versioning is much simpler. There is only one version defined in *configs/environ.json*.
### On back-end building
Because we don't keep back-end archives inside out repository we download them during build process. Build steps
are these: download, verify and extract archive, prepare distribution and make package.
All configuration keys described below are in coin definition file in *configs/coins*.
**download archive**
URL from where is archive downloaded is defined in *backend.binary_url*.
**verify archive**
There are three different approaches how is archive verification done. Some projects use PGP sign of archive, some
have signed sha256 sums and some don't care about verification at all. So there is option *backend.verification_type* that
could be *gpg*, *gpg-sha256* or *sha256* and chooses particular method.
*gpg* type require file with digital sign and maintainer's public key imported in Docker build image (see below). Sign
file is downloaded from URL defined in *backend.verification_source*. Than is passed to gpg in order to verify archvie.
*gpg-sha256* type require signed checksum file and maintainer's public key imported in Docker build image (see below).
Checksum file is downloaded from URL defined in *backend.verification_source*. Then is verified by gpg and passed to
sha256sum in order to verify archive.
*sha256* type is used for coins that don't support verification at all. In *backend.verification_source* is defined
hexadecimal string that is compared with output of sha256sum. Although this solution is not secure, it avoid download
errors and other surprises at least.
*gpg* and *gpg-sha256* types require maintainer's public key imported in Docker build image. It is not expected that
maintainer's key will change requently while sing or checksum files are changed every release, so it is ideal to
store maintainer's key within image definition. Public keys are stored in *build/docker/deb/gpg-keys* directory. Docker
image must be rebuilt by calling `make build-images`.
**extract archive**
Extraction command is defined in *backend.extract_command*. Content of archive must be extracted to `./backend` directory.
See bitcoin.json and vertcoin.json for different approaches.
**prepare distribution**
There are two steps in this stage exclude unnecessary files and generate configuration.
Some files are not required for server deployment, some binaries have unnecessary dependencies, so it is good idea to
extract these files from output package. Files to extract are listed in *backend.exclude_files*. Note that paths are
relative to *backend* directory where archive is extracted.
Configuration is described in [config.md](/docs/config.md).
## Manual build
Instructions below are focused on Debian 9 (Stretch). If you want to use another Linux distribution or operating system
like macOS or Windows, please read instructions specific for each project.
Setup go environment:
```
wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz && tar xf go1.10.3.linux-amd64.tar.gz
sudo mv go /opt/go
sudo ln -s /opt/go/bin/go /usr/bin/go
# see `go help gopath` for details
mkdir $HOME/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
```
Install RocksDB: https://github.com/facebook/rocksdb/blob/master/INSTALL.md
and compile the static_lib and tools
```
sudo apt-get update && sudo apt-get install -y \
build-essential git wget pkg-config libzmq3-dev libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev
git clone https://github.com/facebook/rocksdb.git
cd rocksdb
CFLAGS=-fPIC CXXFLAGS=-fPIC make release
```
Setup variables for gorocksdb: https://github.com/tecbot/gorocksdb
```
export CGO_CFLAGS="-I/path/to/rocksdb/include"
export CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4"
```
Install ZeroMQ: https://github.com/zeromq/libzmq
Install go-dep tool:
```
go get github.com/golang/dep/cmd/dep
```
Get blockbook sources, install dependencies, build:
```
cd $GOPATH/src
git clone https://github.com/trezor/blockbook.git
cd blockbook
dep ensure -vendor-only
go build
```
### Example command
Blockbook require full node daemon as its back-end. You are responsible for proper installation. Port numbers and
daemon configuration are defined in *configs/coins* and *build/templates/backend/config* directories. You should use
specific installation process for particular coin you want run (e.g. https://bitcoin.org/en/full-node#other-linux-distributions for Bitcoin).
When you have running back-end daemon you can start Blockbook. It is highly recomended use ports described in [ports.md](/docs/ports.md)
for both Blockbook and back-end daemon. You can use *contrib/scripts/build-blockchaincfg.sh* that will generate
Blockbook's blockchain configuration from our coin definition files.
Example for Bitcoin:
```
contrib/scripts/build-blockchaincfg.sh
./blockbook -sync -blockchaincfg=build/blockchaincfg.json -internal=:9030 -public=:9130 -certfile=server/testcert -logtostderr
```
This command starts Blockbook with parallel synchronization and providing HTTP and Socket.IO interface, with database
in local directory *data* and established ZeroMQ and RPC connections to back-end daemon specified in configuration
file passed to *-blockchaincfg* option.
Blockbook logs to stderr (option *-logtostderr*) or to directory specified by parameter *-log_dir* . Verbosity of logs can be tuned
by command line parameters *-v* and *-vmodule*, for details see https://godoc.org/github.com/golang/glog.
You can check that Blockbook is running by simple HTTP request: `curl https://localhost:9130`. Returned data is JSON with some
run-time information. If port is closed, Blockbook is syncing data.

97
docs/config.md 100644
View File

@ -0,0 +1,97 @@
# Configuration
Coin definitions are stored in JSON files in *configs/coins* directory. They are single source of Blockbook
configuration, Blockbook and back-end package definition and build metadata. Since Blockbook supports only single
coin index per running instance, every coin (including testnet) must have single definition file.
Because most of coins are fork of Bitcoin and they have similar way to install and configure their daemon, we use
templates to generate package definition and configuration files during build process. It is similar to build Blockbook
package too. Templates are filled with data from coin definition. Although build process generate packages
automatically, there is sometimes necessary see intermediate step. You can generate all files by calling
`go run build/templates/generate.go coin` where *coin* is name of definition file without .json extension. Files are
generated to *build/pkg-defs* directory.
Good examples of coin configuration are
[*configs/coins/bitcoin.json*](configs/coins/bitcoin.json) and
[*configs/coins/ethereum.json*](configs/coins/ethereum.json) for Bitcoin-like coins and different coins, respectively.
## Description of coin definition
* `coin` Base information about coin.
* `name` Name of coin used internally (e.g. "Bcash Testnet").
* `shortcut` Ticker symbol (code) of coin (e.g. "TBCH").
* `label` Name of coin used publicly (e.g. "Bitcoin Cash Testnet").
* `alias` Name of coin used in file system paths and config files. We use convention that name uses lowercase
characters and underscore '_' as a word delimiter. Testnet versions of coins must have *_testnet*
suffix. For example "bcash_testnet".
* `ports` List of ports used by both back-end and Blockbook. Ports defined here are used in configuration templates
and also as source for generated documentation.
* `backend_rpc` Port of back-end RPC that is connected by Blockbook service.
* `backend_message_queue` Port of back-end MQ (if used) that is connected by Blockbook service.
* `backend_*` Additional back-end ports can be documented here. Actually the only purpose is to get them to
port table (prefix is removed and rest of string is used as note).
* `blockbook_internal` Blockbook's internal port that is used for metric collecting, debugging etc.
* `blockbook_public` Blockbook's public port that is used to comunicate with Trezor wallet (via Socket.IO).
* `ipc` Defines how Blockbook connects its back-end service.
* `rpc_url_template` Template that defines URL of back-end RPC service. See note on templates below.
* `rpc_user` User name of back-end RPC service, used by both Blockbook and back-end configuration templates.
* `rpc_pass` Password of back-end RPC service, used by both Blockbook and back-end configuration templates.
* `rpc_timeout` RPC timeout used by Blockbook.
* `message_queue_binding_template` Template that defines URL of back-end's message queue (ZMQ), used by both
Blockbook and back-end configuration template. See note on templates below.
* `backend` Definition of back-end package, configuration and service.
* `package_name` Name of package. See convention note in [build guide](/docs/build.md#on-naming-conventions-and-versioning).
* `package_revision` Revision of package. See convention note in [build guide](/docs/build.md#on-naming-conventions-and-versioning).
* `system_user` User used to run back-end service. See convention note in [build guide](/docs/build.md#on-naming-conventions-and-versioning).
* `version` Upstream version. See convention note in [build guide](/docs/build.md#on-naming-conventions-and-versioning).
* `binary_url` URL of back-end archive.
* `verification_type` Type of back-end archive verification. Possible values are *gpg*, *gpg-sha256*, *sha256*.
* `verification_source` Source of sign/checksum of back-end archive.
* `extract_command` Command to extract back-end archive. It is required to extract content of archive to
*backend* directory.
* `exclude_files` List of files from back-end archive to exclude. Some files are not required for server
deployment, some binaries have unnecessary dependencies, so it is good idea to extract these files from output
package. Note that paths are relative to *backend* directory where archive is extracted.
* `exec_command_template` Template of command to execute back-end node daemon. Every back-end node daemon has its
service that is managed by systemd. Template is evaluated to *ExecStart* option in *Service* section of
service unit. See note on templates below.
* `logrotate_files_template` Template that define log files rotated by logrotate daemon. See note on templates
below.
* `postinst_script_template` Additional steps in postinst script. See [ZCash definition](configs/coins/zcash.json)
for more information.
* `service_type` Type of service. Services that daemonize must have *forking* type and write their PID to
*PIDFile*. Services that don't support daemonization must have *simple* type. See examples above.
* `service_additional_params_template` Additional parameters in service unit. See
[ZCash definition](configs/coins/zcash.json) for more information.
* `protect_memory` Enables *MemoryDenyWriteExecute* option in service unit if *true*.
* `mainnet` Set *false* for testnet back-end.
* `config_file` Name of template of back-end configuration file. Templates are defined in *build/backend/config*.
For Bitcoin-like coins it is not necessary to add extra template, most options can be added via
*additional_params*. For coins that don't require configuration option should be empty (e.g. Ethereum).
* `additional_params` Object of extra parameters that are added to back-end configuration file as key=value pairs.
Exception is *addnode* key that contains list of nodes that is expanded as addnode=item lines.
* `blockbook` Definition of Blockbook package, configuration and service.
* `package_name` Name of package. See convention note in [build guide](/docs/build.md#on-naming-conventions-and-versioning).
* `system_user` User used to run Blockbook service. See convention note in [build guide](/docs/build.md#on-naming-conventions-and-versioning).
* `internal_binding_template` Template for *-internal* parameter. See note on templates below.
* `public_binding_template` Template for *-public* parameter. See note on templates below.
* `explorer_url` URL of blockchain explorer.
* `additional_params` Additional params of exec command (see [Dogecoin definition](configs/coins/dogecoin.json)).
* `block_chain` Configuration of BlockChain type that ensures communication with back-end service. All options
must be tweaked for each individual coin separely.
* `parse` Use binary parser for block decoding if *true* else call verbose back-end RPC method that returns
JSON. Note that verbose method is slow and not every coin support it. However there are coin implementations
that don't support binary parsing (e.g. ZCash).
* `mempool_workers` Number of workers for UTXO mempool.
* `mempool_sub_workers` Number of subworkers for UTXO mempool.
* `block_addresses_to_keep` Number of blocks that are to be kept in blockaddresses column.
* `additional_params` Object of coin-specific params.
* `meta` Common package metadata.
* `package_maintainer` Full name of package maintainer.
* `package_maintainer_email` E-mail of package maintainer.

24
docs/ports.md 100644
View File

@ -0,0 +1,24 @@
# Registry of ports
| coin | blockbook internal port | blockbook public port | backend rpc port | backend service ports (zmq) |
|--------------------------|-------------------------|-----------------------|------------------|-----------------------------|
| Bitcoin | 9030 | 9130 | 8030 | 38330 |
| Bcash | 9031 | 9131 | 8031 | 38331 |
| Zcash | 9032 | 9132 | 8032 | 38332 |
| Dash | 9033 | 9133 | 8033 | 38333 |
| Litecoin | 9034 | 9134 | 8034 | 38334 |
| Bgold | 9035 | 9135 | 8035 | 38335 |
| Ethereum | 9036 | 9136 | 8036 | 38336 p2p, 8136 http |
| Ethereum Classic | 9037 | 9137 | 8037 | |
| Dogecoin | 9038 | 9138 | 8038 | 38338 |
| Namecoin | 9039 | 9139 | 8039 | 38339 |
| Vertcoin | 9040 | 9140 | 8040 | 38340 |
| Testnet | 19030 | 19130 | 18030 | 48330 |
| Bcash Testnet | 19031 | 19131 | 18031 | 48331 |
| Zcash Testnet | 19032 | 19132 | 18032 | 48332 |
| Dash Testnet | 19033 | 19133 | 18033 | 48333 |
| Litecoin Testnet | 19034 | 19134 | 18034 | 48334 |
| Ethereum Testnet Ropsten | 19036 | 19136 | 18036 | 48336 p2p |
| Vertcoin Testnet | 19040 | 19140 | 18040 | 48340 |
> NOTE: This document is generated from coin definitions in `configs/coins`.

44
docs/rocksdb.md 100644
View File

@ -0,0 +1,44 @@
# Data storage in RocksDB
Blockbook stores data the key-value store RocksDB. Data are stored in binary form to save space.
The data are separated to different column families:
- **default**
at the moment not used, will store statistical data etc.
- **height** - maps *block height* to *block hash*
*Block heigh* stored as array of 4 bytes (big endian uint32)
*Block hash* stored as array of 32 bytes
Example - the first four blocks (all data hex encoded)
```
0x00000000 : 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943
0x00000001 : 0x00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206
0x00000002 : 0x000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820
0x00000003 : 0x000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10
```
- **outputs** - maps *output script+block height* to *array of outpoints*
*Output script (ScriptPubKey)+block height* stored as variable length array of bytes for output script + 4 bytes (big endian uint32) block height
*array of outpoints* stored as array of 32 bytes for transaction id + variable length outpoint index for each outpoint
Example - (all data hex encoded)
```
0x001400efeb484a24a1c1240eafacef8566e734da429c000e2df6 : 0x1697966cbd76c75eb9fc736dfa3ba0bc045999bab1e8b10082bc0ba546b0178302
0xa9143e3d6abe282d92a28cb791697ba001d733cefdc7870012c4b1 : 0x7246e79f97b5f82e7f51e291d533964028ec90be0634af8a8ef7d5a903c7f6d301
```
- **inputs** - maps *transaction outpoint* to *input transaction* that spends it
*Transaction outpoint* stored as array of 32 bytes for transaction id + variable length outpoint index
*Input transaction* stored as array of 32 bytes for transaction id + variable length input index
Example - (all data hex encoded)
```
0x7246e79f97b5f82e7f51e291d533964028ec90be0634af8a8ef7d5a903c7f6d300 : 0x0a7aa90ea0269c79f844c516805e4cac594adb8830e56fca894b66aab19136a428
0x7246e79f97b5f82e7f51e291d533964028ec90be0634af8a8ef7d5a903c7f6d301 : 0x4303a9fcfe6026b4d33ba488df6443c9a99bca7b7fcb7c6f6cd65cea24a749b700
```