# Geth

Geth (go-ethereum) - программное обеспечение узла сети Ethereum. Имеет все функции для взаимодействия с сетью ethereum, но не подходит для использования обычными пользователями в силу сложности cli интерфейса, и отсутствия графического интерфейса.

В этой лабораторной работе мы создадим свою частную сеть. Для этого: 
- создадим структуру генезис блока
- сгенерируем тестовые аккаунты для узла geth
- инициируем цепочку блоков
- запустим узел
- свяжем несколько узлов
- начнём майнинг

Для начала, необходимо определиться, где будем хранить рабочие файлы geth.<br>
По умолчанию, это директория `~/.ethereum/`.<br>
Но если вдруг её захочется сменить, то при каждом запуске geth понадобится указывать путь к его папке при помощи аргумента
`--datadir `*`путь_к_директории`*.<br>
Например `geth --http --http.corsdomain "*" `**`--datadir ~/.screed/eth/`**

Создаём директорию `~/.ethereum`, если она отсутствует
```
$ mkdir ~/.ethereum
```

### Генезис блок

Генезис блоком является блок, который находится в самом начале цепочки, и имеет id 0.<br>
Его содержимое определяется файлом genesis.json, и определяет параметры создаваемой цепочки. Перейдём в директорию `~/.ethereum/`
```
$ cd ~/.ethereum
```
Создадим файл genesis.json
```
$ vim genesis.json
```
Его содержимое должно быть следующим:
```json
{
    "config": {
        "chainId": 1547,
        "homesteadBlock": 0,
        "eip150Block": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
    "difficulty": "400",
    "gasLimit": "21000",
    "alloc": {
        "0x135d49110A61c950882b3Efe0510A380c5525966": { "balance": "3000" },
        "0xb567f849887fac1534d4da5f2a0db9ce467f15d9": { "balance": "2000" },
        "0x6B287f15F8c6CA65eC36bF48b90E40ab839cA17d": { "balance": "5000" }
    }
}
```
Значения здесь означают:
- `chainId`- id создаваемой цепочки. Предназначен для разграничения цепочек, чтобы они не мешались между собой
- `homesteadBlock`- ***~~данные удалены~~***
- `eip---Block`- активация различных модификаций блокчейна, заложенных в geth
- `difficulty`- стартовая сложность майнинга, которая в будущем будет подстраиваться под мощность сети
- `gasLimit`- ***~~данные удалены~~***
- `alloc`- выдача адресам базового баланса

Обратите внимание, что для объединения нескольких узлов в сеть, они должны иметь **одинаковый** генезис блок.

### Создание аккаунтов

Создавать аккаунты можно разными способами. Можно воспользоваться генератором аккаунтов geth, но в таком случае будет проблематично импортировать их в другой кошелёк.

#### Способ geth

> <span style="color:#f94144">Инструкцию данного блока выполнять не нужно. Если не интересно, переходите к блоку "наш способ".</span>

```
$ geth account new
```
После этого будет дважды запрошен пароль для шифрования ключа, если на аккаунте не будет ничего ценного, то пароль можно оставить пустым.
```
INFO [08-25|20:29:27.829] Maximum peer count                       ETH=50 LES=0 total=50
Your new account is locked with a password. Please give a password. Do not forget this password.
Password: 
Repeat password: 

Your new key was generated

Public address of the key:   0xD365e349b79c240019FdaeFFfDF034D859e545cf
Path of the secret key file: /home/kaya613/.ethereum/keystore/UTC--2020-08-25T17-29-32.899061288Z--d365e349b79c240019fdaefffdf034d859e545cf

- You can share your public address with anyone. Others need it to interact with you.
- You must NEVER share the secret key with anyone! The key controls access to your funds!
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
```
В выводе той команды можно найти путь к файлу с ключом `~/.ethereum/keystore/`*`ключ-нейм`*

Конечно, можно воспользоваться таким способом, если мы не хотим иметь доступ к этим адресам из другого кошелька. И мы даже можем совершать транзакции из geth, но это будет довольно неудобно.
Вот пример команды консоли geth, для перевода ETH
```
> eth.sendTransaction({from: eth.coinbase, to:'0x4892BDEB1364943A20ED068434f27a05fF02F102', value: web3.toWei(0.05, "ether"), gas:21000})
```
И это мы ещё опустили тот факт, что для совершения транзакций нужно будет запустить geth с небезопасным параметром, и далее разблокировать кошелёк.

Поэтому будет лучше, если для этого мы воспользуемся другим кошельком.

#### Наш способ

Поскольку мы собираемся импортировать ключи в другие кошельки, будем создавать "сырые" приватные ключи, и уже потом вставлять их в geth.

Находясь в директории `~/.ethereum` создадим директорию для ключей
```
$ mkdir priv
```
Теперь создадим три ключа, и запишем их в файлы `0`, `1`, `2`
```
$ dd if=/dev/random bs=16M count=1 | sha256sum | cut -d " " -f 1 > priv/0
$ dd if=/dev/random bs=16M count=1 | sha256sum | cut -d " " -f 1 > priv/1
$ dd if=/dev/random bs=16M count=1 | sha256sum | cut -d " " -f 1 > priv/2
```
Здесь мы при помощи команды `dd` получаем 16mb случайных данных, при помощи символа `|` перенаправляем эти данные команде хеширования `sha256sum`, обрезаем лишний вывод при помощи `cut`. Далее используя символ `>` записываем результат в файлы 0, 1 и 2 в директории priv.<br>
В ethereum, приватный ключ является результатом работы `sha256sum`, поэтому мы его и использовали.

Добавим чуть больше безопасности (совсем чуть), и запретим другим пользователям читать наши приватные ключи. Для этого изменим права доступа к тем файлам.
```
$ chmod 600 priv/0
$ chmod 600 priv/1
$ chmod 600 priv/2
```
От злоумышленника, имеющего физ. доступ к компьютеру это не спасёт, однако при удалённом доступе может создать ему некоторые сложности. А в идеале, такие вещи нужно шифровать.<br>
Но поскольку на этих аккаунтах не будет ничего ценного, безопасностью можно полностью пренебречь.

Осталось импортировать ключи в geth. Для этого выполним команды:
```
geth account import priv/0
geth account import priv/1
geth account import priv/2
```
Как и в способе создания ключей через geth, при каждом импорте будет запрошен новый пароль, и подтверждение. Оставим пустым.

### Инициация цепочки блоков

Уже почти можно инициировать цепочку (сделать из genesis.json реальный блок). Но прежде чем мы это сделаем нужно убедиться, что у нас нет старой цепочки.
Для этого посмотрим, существует ли старая цепочка. Находясь в директории `~/.ethereum`, вводим команду просмотра содержимого директории
```
$ ls -l
```
```
-rw-r--r-- 1 kaya613 users  460 авг 24 18:40 genesis.json
drwx------ 7 kaya613 users 4,0K авг 24 20:31 geth
-rw------- 1 kaya613 users 3,1K авг 24 20:30 history
drwx------ 2 kaya613 users 4,0K авг 25 21:24 keystore
drwxr-xr-x 2 kaya613 users 4,0K авг 24 18:39 priv
drwxr-xr-x 2 kaya613 users 4,0K авг 23 20:25 tmp
```
Если у вас, как в моём случае существует папка geth, то необходимо удалить её
```
$ rm -rf geth
```
Теперь можно создавать цепочку
```
$ geth init genesis.json
```
`init`- команда geth, для создания генезис блока из json файла.

В итоге мы должны получить похожий вывод
```
INFO [08-25|22:35:30.707] Maximum peer count                       ETH=50 LES=0 total=50
INFO [08-25|22:35:30.718] Set global gas cap                       cap=25000000
INFO [08-25|22:35:30.718] Allocated cache and file handles         database=/home/kaya613/.ethereum/geth/chaindata cache=16.00MiB handles=16
INFO [08-25|22:35:30.724] Writing custom genesis block 
INFO [08-25|22:35:30.726] Persisted trie from memory database      nodes=4 size=567.00B time=1.86686ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [08-25|22:35:30.727] Successfully wrote genesis state         database=chaindata                                hash="1a58ab…a47810"
INFO [08-25|22:35:30.727] Allocated cache and file handles         database=/home/kaya613/.ethereum/geth/lightchaindata cache=16.00MiB handles=16
INFO [08-25|22:35:30.732] Writing custom genesis block 
INFO [08-25|22:35:30.733] Persisted trie from memory database      nodes=4 size=567.00B time="581.32µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [08-25|22:35:30.733] Successfully wrote genesis state         database=lightchaindata                           hash="1a58ab…a47810"
```
Цепочка создана, а это значит, что можно запускать узел. Нам потребуется два терминала, один для запуска узла, а другой для управления узлом. Второй терминал можно открыть в том же окне, нажав комбинацию `Win + a` / `Win + z` / `Win + x`, `с подходящей стороны`, `снизу` и `справа` соответственно.

В одном выполним команду
```
$ geth --networkid 1547 --http --http.corsdomain "*"
```
- `networkid`- id сети. Нужно указать тот же, что мы писали в генезис блоке
- `http`- указываем, что с нашим узлом смогут взаимодействовать клиенты по протоколу http-rpc
- `http.corsdomain`- кто сможет взаимодействовать с нашим узлом. Мы разрешили всем, хоть и говорят, что это небезопасно

##### неиспользованные аргументы, которые приняли значение по умолчанию:
- `http.addr`- на каком адресе geth будет слушать подключения клиентов. Значение по умолчанию `127.0.0.1` означает, что смогут подключаться только клиенты, запущенные на этом же компьютере
- `http.port`- на каком порту geth будет слушать подключения клиентов. Значение по умолчанию `8545`

После запуска узла, необходимо получить управление над ним. Для этого перейдём в другой терминал, и выполним команду
```
$ geth attach
```
И вот, мы в консоли geth.<br>
Обе эти команды не будут завершаться самостоятельно, а значит, во время их работы мы не сможем пользоваться самим терминалом. Чтобы остановить выполнение какой либо из команд, нужно находясь в нужном терминале нажать `Ctrl + c`.

### Связь нескольких узлов в сеть

Для связи двух узлов нужно узнать `enode` адрес узла, заменить в нём ip на актуальный, и добавить этот адрес на другом узле.<br>
Чтобы узнать enode нашего узла, выполним команду в консоли geth
```
> admin.nodeInfo.enode
```
Получим строку такого вида
```
"enode://7c982c91f53cb3f5d4bab1dc37cdf50272828c1068c421b94e5ada05e509c82a87ed93ee56bd51a7877bccc8e64e7836db8d67e88043cb7a55a4ccd72b98262d@127.0.0.1:30303"
```
Возможно, после порта 30303 будет что-то ещё. Это нужно будет стереть.<br>
Теперь узнаем правильный ip адрес. Для этого в терминале (не в консоли `geth attach`, и не в процессе узла geth, а в новом терминале) выполним команду
```
$ ip a
```
Получим подобный вывод
<pre style="font-size:.85em">
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 4c:cc:6a:d1:f6:0a brd ff:ff:ff:ff:ff:ff
    inet 172.21.13.7/24 brd 172.21.13.255 scope global dynamic enp1s0
       valid_lft 74075sec preferred_lft 74075sec
    inet6 fe80::ba0d:c40b:9c69:f4fd/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: <span style="color:#f9c74f">tun0</span>: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 65535 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet6 300:dc31:7c01:3161::1/128 scope global 
       valid_lft forever preferred_lft forever
    inet6 <span style="color:#f94144">200:dc31:7c01:3161:5a0:aa65:b32d:b95c</span>/7 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::7e9d:1fb2:fc09:10d6/64 scope link stable-privacy 
       valid_lft forever preferred_lft forever
</pre>
Здесь нужно будет найти интерфейс tun0 (скорее всего он будет называться так), и скопировать из него адрес вида `2xx:x:x:x:x:x:x:x`. В моём случае это `200:dc31:7c01:3161:5a0:aa65:b32d:b95c`.<br>
Теперь заменим ip из enode на свой. Не забудьте обернуть ip в `[ ]`
<pre style="font-size:.85em">
"enode://7c982c91f53cb3f5<span style="color:#90be6d">...</span>@<span style="color:#f9c74f">127.0.0.1</span>:30303"
"enode://7c982c91f53cb3f5<span style="color:#90be6d">...</span>@<span style="color:#43aa8b">[200:dc31:7c01:3161:5a0:aa65:b32d:b95c]</span>:30303"
</pre>
Кстати, порт `30303`, в отличии от порта `8545`, служит для связи узлов ethereum между собой.

Теперь нужно передать enode на другой узел, и выполнить **на том узле** в консоли geth attach команду со значением enode
```
> admin.addPeer("enode://7c982c91f53cb3f5d4bab1dc37cdf50272828c1068c421b94e5ada05e509c82a87ed93ee56bd51a7877bccc8e64e7836db8d67e88043cb7a55a4ccd72b98262d@[200:dc31:7c01:3161:5a0:aa65:b32d:b95c]:30303")
```
Если всё верно, то на выходе должно быть `true`. Если перезапустить geth, то пиры придётся добавлять заново.

Проверим, появился ли узел в списке пиров (на каждом узле)
```
> admin.peers
```
Должен быть получен подобный блок (их может быть несколько)
<pre style="font-size:.85em">
{
    caps: ["eth/63", "eth/64", "eth/65"],
    enode: "enode://6a3b9106929d19992fd5f4be7bb81c1ce3929eeeb66d71e85852bbff5db927e63a5bd68d14583c74d32f39a56a2dfc61e21d51ebb60899c2d3ea17244088c24f@[200:ab20:623a:6:7953:11ae:be0f:ad55]:30303",
    id: "bd3e2eab04865a82a35fb7dd49a394b706694b603b1040e558936fa95ce0f782",
    name: "Geth/v1.9.19-stable-3e064192/linux-amd64/go1.14.7",
    network: {
      inbound: false,
      localAddress: "[300:dc31:7c01:3161::1]:33874",
      <span style="color:#f9c74f">remoteAddress: "[200:ab20:623a:6:7953:11ae:be0f:ad55]:30303",</span>
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 400,
        head: "0x1a58ab5528894b7884d04c95b476bbdc473942a02b8dcf7fbcff221971a47810",
        version: 65
      }
    }
}
</pre>
Здесь в выделенной строке можно найти ip ноды, к которой мы подключились, а строкой выше написан наш ip.
Если вы нашли ip ноды, к которой подключались, значит подключение прошло успешно. После связи пиров, блоки между ними будут синхронизироваться.

### Майнинг

**Майнинг**- получение транзакций из сети, добавление их в блок, и создание блока.

Чтобы начать майнинг, нужно в консоли geth выполнить команду
```
> miner.start()
```
В скобках можно указать кол-во ядер процессора, которые будут использоваться для майнинга.

После начала майнинга, скорее всего начнётся создание файла размером 1 ГБ. Это займёт несколько минут, и только потом пойдёт майнинг. Прервать создание этого файла без остановки узла невозможно.<br>
Когда этот файл будет создан, начнётся майнинг, и он будет отображаться в терминале с процессом geth
```
INFO [08-26|22:05:08.835] 🔗 block reached canonical chain          number=1 hash="af15c9…7f88bc"
INFO [08-26|22:05:08.835] 🔨 mined potential block                  number=8 hash="a67bfd…8bf662"
INFO [08-26|22:05:08.837] Commit new mining work                   number=9 sealhash="1b7ab8…d4db28" uncles=0 txs=0 gas=0 fees=0 elapsed="144.757µs"
INFO [08-26|22:05:11.464] Successfully sealed new block            number=9 sealhash="1b7ab8…d4db28" hash="83b6c1…1a0f38" elapsed=2.627s
```
Чтобы остановить майнинг, можно выполнить команду
```
> miner.stop()
```
