Ethereum POA en utilisant Geth
Définition du consensus Proof Of Authority (POA, Preuve d’autorité):
Proof-of-authority (PoA) est un algorithme utilisé avec la technologie de blockchain qui fournit des transactions relativement rapides grâce à un mécanisme de consensus basé sur l’identité en tant que pieu (l’identité étant une preuve suffisante pour la validation des transactions).
Dans les réseaux basés sur le POA, les transactions et les blocs sont validés par des comptes approuvés, appelés validateurs. Les validateurs exécutent un logiciel leur permettant de mettre des transactions dans des blocs. Le processus est automatisé et n’exige pas que les validateurs surveillent constamment leurs ordinateurs. Cependant, il est nécessaire de maintenir l’ordinateur (le nœud d’autorité, ou l’ensemble des noeuds d’autorité) pour qu’ils ne soient pas compromis.
Avec les POA, les individus gagnent le droit de devenir des validateurs, donc il y a une incitation à conserver la position qu’ils ont acquis. En attachant une réputation à l’identité, les validateurs sont encouragés à maintenir le processus de transaction, car ils ne souhaitent pas que leur identité soit liée à une réputation négative. [Source: Wikipedia]
Comment monter notre propre chaîne avec le consensus POA ?
Buts de ce tutoriel: Créer deux noeuds, en utilsant Geth, qui vont servire de validateurs
de notre blockchain privée d’Ethereum. Et on va monter un bootnode qui va servir de service de découverte (discovery service). Et le tout formera un réseau d’Ethereum privé en P2P (Peer to Peer).
Avant de continuer la lecture, je tiens à vous inviter à lire la proposition EIP255 qui a fait naître la possibilité d’intégrer la POA avec la blockchain d’Ethereum déjà existante.
Dans ce tutoriel, je vais utiliser Geth v1.7.3
, vous pouvez l’installer en visitant ce lien:
$> geth version
Geth
Version: 1.7.3-stable
Git Commit: 4bb3c89d44e372e6a9ab85a8be0c9345265c763a
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.9.2
Operating System: linux
GOPATH=/home/user/go
GOROOT=/usr/user/go
PS: Si vous utilisez Ubuntu 16.04+ ou une distribution dérivée d’Ubuntu, vous pouvez télécharger Geth
en ajoutant le PPA d’Ethereum [Pour plus d’informations, veuillez visiter ce lien]:
$> sudo apt update
$> sudo add-apt-repository -y ppa:ethereum/ethereum
$> sudo apt update
$> sudo apt-get install ethereum
Maintenant, créons un nouveau espace de travail:
$> cd # Aller au dossier de l'utilisateur
$> mkdir test_poa # Créer un nouveau dossier
$> cd test_poa
$> mkdir node1 node2
Ensuite, on va créer des comptes Ethereum avec Geth
pour les noeuds 1 et 2:
$> geth --datadir node1/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase: # ici on écrit le mot de passe pour le compte n°1 du noeud 1
Address: {2d299ed001270f8c2f02abdda2bda4e5b4cca348} # adresse publique du compte n°1
$> geth --datadir node2/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase: # ici on écrit le mot de passe pour le compte n°2 du noeud 2
Address: {bbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f} # adresse publique du compte n°2
Ces commandes vont créer un nouveau dossier sous chacun des dossiers node1
et node2
nommé keystore
qui va contenir un fichier qui va servir du compte ethereum pour le noeud n°1 et le noeud n°2.
À ce niveau, copions les deux adresses publiques, générées par Geth
, dans un nouveau fichier nommé accounts
:
echo -n "2d299ed001270f8c2f02abdda2bda4e5b4cca348" >> accounts
echo -n "bbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f" >> accounts
Et écrivons les mots de passes de chaque compte de chaque noeud dans un fichier nommé password
sous chaque dossier de chaque noeud:
echo -n "node1 password" > node1/password
echo -n "node2 password" > node2/password
Miantenant, créons le fichier Genesis
qui va servir à initier notre nouvelle blockchain privée (pour plus d’informations sur le Genesis block
veuillez visiter ce lien)
Pour ne pas compliquer les choses, Geth
vient avec un exécutable nommé puppeth
qui facilite la création des fichier Genesis
qui vont initier les nouvelles blockchain privées.
PS: Si vous ne trouvez pas l’exécutable puppeth
, vous pouvez l’installer via le PPA d’ethereum qu’on a vu au début de ce tutoriel:
$> sudo apt update
$> sudo apt install puppeth
À ce niveau, créons le fichier Genesis
:
$> puppeth
+-----------------------------------------------------------+
| Welcome to puppeth, your Ethereum private network manager |
| |
| This tool lets you create a new Ethereum network down to |
| the genesis block, bootnodes, miners and ethstats servers |
| without the hassle that it would normally entail. |
| |
| Puppeth uses SSH to dial in to remote servers, and builds |
| its network components out of Docker containers using the |
| docker-compose toolset. |
+-----------------------------------------------------------+
Please specify a network name to administer (no spaces or hyphens, please)
> NexusNetwork # Nom de notre nouveau Network (nouvelle blockchain) (PS: Pas d'espaces !)
Sweet, you can set this via --network=NexusNetwork next time!
INFO [05-29|14:06:10] Administering Ethereum network name=NexusNetwork
WARN [05-29|14:06:10] No previous configurations found path=/home/user/.puppeth/NexusNetwork
What would you like to do? (default = stats)
1. Show network stats
2. Configure new genesis
3. Track new remote server
4. Deploy network components
> 2 # choisir n° 2
Which consensus engine to use? (default = clique)
1. Ethash - proof-of-work
2. Clique - proof-of-authority
> 2 # choisir le consensus POA qui est le n°2
How many seconds should blocks take? (default = 15)
> 3 # le temps entre chaque bloc
Which accounts are allowed to seal? (mandatory at least one) # seal = validateur
> 0x2d299ed001270f8c2f02abdda2bda4e5b4cca348 # adresse publique du compte du noeud n°1
> 0xbbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f # adresse publique du compte du noeud n°2
> 0x
Which accounts should be pre-funded? (advisable at least one) # Ajouter des fonds aux adresses
> 0x2d299ed001270f8c2f02abdda2bda4e5b4cca348 # adresse publique du compte du noeud n°1
> 0xbbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f # adresse publique du compte du noeud n°2
> 0x
Specify your chain/network ID if you want an explicit one (default = random)
> 9999 # ID de notre réseau
INFO [05-29|14:07:30] Configured new genesis block
What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components
> 2 # choisir 2
1. Modify existing fork rules
2. Export genesis configuration
3. Remove genesis configuration
> 2 # choisir 2 pour exporter le fichier Genesis nommé NexusNetwork.json
Which file to save the genesis into? (default = NexusNetwork.json)
>
INFO [05-29|14:08:09] Exported existing genesis block
What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components
> ^C
L’arboraissance de notre projet sera comme suit:
tree -L 2
.
├── accounts
├── NexusNetwork.json
├── node1
│ ├── keystore
│ └── password
└── node2
├── keystore
└── password
4 directories, 4 files
Et le contenu du fichier NexusNetwork.json
est:
$> cat NexusNetwork.json
{
"config": {
"chainId": 9999,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"byzantiumBlock": 4,
"clique": {
"period": 3,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x5b0d504f",
"extraData": "0x00000000000000000000000000000000000000000000000000000000000000002d299ed001270f8c2f02abdda2bda4e5b4cca348bbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0000000000000000000000000000000000000000": {
"balance": "0x1"
},
"0000000000000000000000000000000000000001": {
"balance": "0x1"
...
"bbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
Ceci dit, initions nos noeuds avec le nouveau fichier Genesis
:
$> geth --datadir node1/ init NexusNetwork.json
INFO [05-29|14:10:14] Allocated cache and file handles database=/home/user/test_poa/node1/geth/chaindata cache=16 handles=16
INFO [05-29|14:10:14] Writing custom genesis block
INFO [05-29|14:10:14] Successfully wrote genesis state database=chaindata hash=78e1c1…31bcdd
INFO [05-29|14:10:14] Allocated cache and file handles database=/home/user/test_poa/node1/geth/lightchaindata cache=16 handles=16
INFO [05-29|14:10:14] Writing custom genesis block
INFO [05-29|14:10:14] Successfully wrote genesis state database=lightchaindata hash=78e1c1…31bcdd
$>geth --datadir node2/ init NexusNetwork.json
INFO [05-29|14:10:20] Allocated cache and file handles database=/home/user/test_poa/node2/geth/chaindata cache=16 handles=16
INFO [05-29|14:10:20] Writing custom genesis block
INFO [05-29|14:10:20] Successfully wrote genesis state database=chaindata hash=78e1c1…31bcdd
INFO [05-29|14:10:20] Allocated cache and file handles database=/home/user/test_poa/node2/geth/lightchaindata cache=16 handles=16
INFO [05-29|14:10:20] Writing custom genesis block
INFO [05-29|14:10:20] Successfully wrote genesis state database=lightchaindata
Puis, créons un boot.key
en utilisant bootnode
un exécutable trouvé dans le projet Geth
et si cet exécutable est manquant, vous pouvez le télécharger via les dépôts du PPA d’Ethereum ajouté au début de l’article:
$> sudo apt update
$> sudo apt install bootnode
Et la génération du boot.key
:
$> bootnode -genkey boot.key
$> cat boot.key
7dd0375ae3f0e92cc151829e98289ccde4b9c2cb2317bd15283d6b930f47bfc4
Et la nouvelle arboraissance du projet sera:
tree -L 2
.
├── accounts
├── boot.key
├── NexusNetwork.json
├── node1
│ ├── geth
│ ├── keystore
│ └── password
└── node2
├── geth
├── keystore
└── password
6 directories, 5 files
Maintenant, dans une terminale, exécutons notre bootnode:
bootnode -nodekey boot.key -verbosity 9 -addr :30310
INFO [05-29|14:16:50] UDP listener up self=enode://393fafe1972bb56d3743a7d0d5590c113c357348815f10b8d970ffa3919b71be734abf8ebeff2eb2921eee305ba4c2fefbbd79ef3a69e4a78750491da3824d78@[::]:30310
Et dans une autre terminale, exécutons le noeud n°1:
$> geth --datadir node1/ --syncmode 'full' --port 30311 --rpc --rpcaddr 'localhost' --rpcport 8501 --rpcapi 'personal,net,db,eth,web3,txpool,miner' --bootnodes 'enode://393fafe1972bb56d3743a7d0d5590c113c357348815f10b8d970ffa3919b71be734abf8ebeff2eb2921eee305ba4c2fefbbd79ef3a69e4a78750491da3824d78@127.0.0.1:30310' -networkid 9999 --gasprice '1' --unlock '2d299ed001270f8c2f02abdda2bda4e5b4cca348' --password node1/password --mine
Et dans une autre terminale exécutons le noeud n°2:
$> geth --datadir node1/ --syncmode 'full' --port 30311 --rpc --rpcaddr 'localhost' --rpcport 8501 --rpcapi 'personal,db,eth,web3,txpool,miner' --bootnodes 'enode://393fafe1972bb56d3743a7d0d5590c113c357348815f10b8d970ffa3919b71be734abf8ebeff2eb2921eee305ba4c2fefbbd79ef3a69e4a78750491da3824d78@127.0.0.1:30310' -networkid 9999 --gasprice '1' --unlock '2d299ed001270f8c2f02abdda2bda4e5b4cca348' --password node1/password --mine
En ouvrant la terminale qu’on a utilisé pour exécuter notre bootnode, on va constater que le deux noeuds sont activées vu que les adresses 127.0.0.1:30311
et 127.0.0.1:30312
sont activées:
TRACE[05-29|14:28:25] >> NEIGHBORS/v4 addr=127.0.0.1:30311 err=nil
TRACE[05-29|14:28:25] << FINDNODE/v4 addr=127.0.0.1:30311 err=nil
TRACE[05-29|14:28:25] >> PING/v4 addr=127.0.0.1:30312 err=nil
TRACE[05-29|14:28:25] << PONG/v4 addr=127.0.0.1:30312 err=nil
Et en ouvrant une autre terminale pour voir la nouvelle arboraissance du projet quand les noeuds et le bootnode sont activés, on va remarquer la génération de deux nouveaux fichiers geth.ipc
qui vont servire à activer la communication IPC
avec notre nouvelle blockchain:
tree -L 2
.
├── accounts
├── boot.key
├── NexusNetwork.json
├── node1
│ ├── geth
│ ├── geth.ipc
│ ├── keystore
│ └── password
└── node2
├── geth
├── geth.ipc
├── keystore
└── password
6 directories, 7 files
Essayons d’activer la console JavaScript de Geth en utilisant IPC
comme moyen de communication:
geth attach node1/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.2
coinbase: 0x2d299ed001270f8c2f02abdda2bda4e5b4cca348
at block: 80 (Tue, 29 May 2018 14:32:08 CET)
datadir: /home/user/test_poa/node1
modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> # Console activée avec succès !
De même essayons d’activer la console JavaScript de Geth en utilsant le protocole RPC/HTTP
comme moyen de communication:
geth attach 'http://localhost:8501'
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.2
coinbase: 0x2d299ed001270f8c2f02abdda2bda4e5b4cca348
at block: 104 (Tue, 29 May 2018 14:33:20 CET)
modules: eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> # Console activée avec succès !
PS: On peut noter que Geth nous dise qu’il a ouvert le port 8501 pour la communication RPC/HTTP
et le endpoint IPC
pour une interaction avec ses fonctionnalités. Par exemple dans le noeud n°1 en lançant les commandes en dessus on peut constater ce message:
INFO [05-29|14:24:21] IPC endpoint opened: /home/user/test_poa/node1/geth.ipc
INFO [05-29|14:24:21] HTTP endpoint opened: http://localhost:8501
Maintenant, essayons de communiquer avec notre nouvelle blockchain en utilisant la console JavaScript de Geth:
$> geth attach 'http://localhost:8501'
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.2
coinbase: 0x2d299ed001270f8c2f02abdda2bda4e5b4cca348
at block: 136 (Tue, 29 May 2018 14:35:29 CET)
modules: eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> net.version
"9999" # version de notre réseau
> eth.blockNumber
153 # le dernier block trouvé
> eth.coinbase
"0x2d299ed001270f8c2f02abdda2bda4e5b4cca348" # adresse du compte par défaut
> eth.sendTransaction({'from': eth.coinbase, 'to': 'bbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f', 'value': web3.toWei(1, 'ether')})
"0x9c896d9e26d9671d4dfe56861e05b01b18380a48905de77234b5569e554fa349" # tx hash
> eth.getTransactionReceipt("0x9c896d9e26d9671d4dfe56861e05b01b18380a48905de77234b5569e554fa349")
{
blockHash: "0x7de8cc81dc7d5a9839b63f5ce47b05c5fddc6347f8df30fd4f8357217a1b562b",
blockNumber: 189,
contractAddress: null,
cumulativeGasUsed: 21000,
from: "0x2d299ed001270f8c2f02abdda2bda4e5b4cca348",
gasUsed: 21000,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: "0xbbb6abec9f9b6d0e7d3ff18bcb68ce6ac931478f",
transactionHash: "0x9c896d9e26d9671d4dfe56861e05b01b18380a48905de77234b5569e554fa349",
transactionIndex: 0
}
> exit
De la même façon, essayons de comminiquer avec notre nouvelle blockchain en utilisant Python et web3(v4.2.1)
:
PS: Veuillez visiter ce lien pour activer le middleware qui va faciliter la communication avec notre nouvelle blockchain avec le consensus POA.
>>> from web3 import Web3, IPCProvider
>>> w3 = Web3(IPCProvider('/home/user/test_poa/node1/geth.ipc'))
>>> from web3.middleware import geth_poa_middleware
>>> w3.middleware_stack.inject(geth_poa_middleware, layer=0)
>>> w3.version.node
'Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.2'
>>> w3.eth.coinbase
'0x2d299ed001270F8C2f02abDDa2Bda4E5B4CCa348'
>>> w3.eth.gasPrice
1 # en wei
>>> w3.eth.getBalance("0x2d299ed001270F8C2f02abDDa2Bda4E5B4CCa348")
904625697166532776746648320380374280103671755200316906557262375061821304312 # en wei
>>> w3.fromWei(w3.eth.getBalance("0x2d299ed001270F8C2f02abDDa2Bda4E5B4CCa348"), 'ether')
Decimal('904625697166532776746648320380374280103671755200316906557.262375061821304312') # en ether
Au final: On a pu créer une blockchain privée d’ethereum avec le consensus POA qui peut nous être utile pour le cas où on cherche à créer un réseau blockchain sans GAS/sans frais de transaction
ou avec des frais moins élevés ou bien en montant des noeuds spécifiques qui seront les seuls participants de confiance du réseau et bien sûr l’un des atouts du POA c’est qu’il n’utilise pas des réssources énormes pour la validation des blocs (à l’instar du consensus POW, Proof Of Work).
Ceci dit, ce type de réseau est utile pour les tests en local, comme pour des solutions pour des organismes privés qui cherchent à monter une solution blockchain en limitant l’accès à des membres bien définis.
Commentaires