Blockchain: Préparer son environnement de développement avec MultiChain


Cet article n’a pas pour vocation de présenter une nouvelle fois les concepts de la blockchain, mais plutôt d’aborder cette technologie du point de vue du développeur.

Deux approches s’offrent à vous : utiliser un réseau de nœuds existants (Ethereum, Hyperledger, Azure BaaS) et y greffer vos transactions ou contrats, ou bâtir votre propre réseau. Cette seconde solution s’appliquera nettement mieux à un projet de blockchain privée. Car, rappelons-le, la blockchain est avant tout un système de stockage de données décentralisé, sécurisé et fonctionnant sans organe central de contrôle.

Pour mes propres expérimentations, j’ai choisi la plateforme MultiChain, qui offre le double avantage d’être Open Source et de proposer des API dans de nombreux langages de développement (Node.JS, C#, Python, Go). Son déploiement est également extrêmement simple (xcopy). La communauté est très active et offre un très bon support. La documentation est assez complète. Mais vous devrez maîtriser un minimum de théorie avant de vous lancer dedans.

Même s’il est possible de travailler sur plusieurs machines physiques ou virtuelles, je vais vous expliquer dans cet article comment faire tourner plusieurs nœuds de MultiChain sur un même poste de travail, ce qui facilitera grandement votre travail de développeur.

Installer Multichain 1.0.1

Rien de plus simple :


Dans mon exemple, MultiChain est installé dans le répertoire c:\multichain-1.0.1

Création d’une nouvelle blockchain et du bloc genèse

Ouvrez une nouvelle console et tapez les instructions suivantes :
c:
cd \
cd multichain-1.0.1
md node1
md node2

Comme nous allons faire tourner deux nœuds de la chaîne sur la même machine, les répertoires node1 et node2 contiendrons chacun une copie de la blockchain.

Tapez ensuite :

multichain-util create mychain -datadir=.\node1

MultiChain 1.0.1 Utilities (protocol 10009)
Blockchain parameter set was successfully generated.

You can edit it in C:\multichain-1.0.1\node1\mychain\params.dat before running multichaind for the first time.

To generate blockchain please run "multichaind mychain -daemon".


Nous pouvons maintenant démarrer le process multichaind qui va se mettre à l’écoute des autres nœuds. Comme nous allons faire tourner plusieurs instances, nous allons spécifier deux ports par défaut pour les E/S.

multichaind mychain -daemon -datadir=./node1 -port=7000 -rpcport=7001

MultiChain 1.0.1 Daemon (protocol 10009)

Other nodes can connect to this node using:
multichaind mychain@192.168.129.70:7000

This host has multiple IP addresses, so from some networks:
multichaind mychain@192.168.193.1:7000
multichaind mychain@192.168.93.1:7000

Node started

Le premier nœud de la blockchain est maintenant opérationnel et son bloc initial (genesis) a été créé. Il est temps maintenant de créer un second nœud et d’y dupliquer notre chaîne, baptisée très sobrement « mychain ».

Création et synchronisation d’un second nœud

Ouvrez une seconde console et tapez l’instruction suivante (toujours dans le répertoire c:\multichain-1.0.1). Le répertoire de stockage et les ports sont différents. Nous indiquons tout d’abord le nom de la blockchain et l’adresse du premier nœud (port 7000).

multichaind mychain@192.168.193.1:7000 -port=8000 -rpcport=8001 -datadir=.\node2

MultiChain 1.0.1 Daemon (protocol 10009)

Retrieving blockchain parameters from the seed node 192.168.193.1:7000 ...
Blockchain successfully initialized.

Please ask blockchain admin or user having activate permission to let you connect and/or transact:
multichain-cli mychain grant 1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu connect
multichain-cli mychain grant 1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu connect,send,receive

MultiChain offre la possibilité à l’administrateur (càd le créateur de la chaîne) de donner des droits d’accès aux différentes adresses de la blockchain. Vous pouvez imaginer cette adresse comme étant l’identifiant unique d’un portefeuille (wallet). Pour chaque nœud créé, MultiChain crée un wallet, et donc une adresse. Vous pouvez bien sûr en ajouter d’autres par la suite sur chaque nœud.

Sur le premier nœud, nous allons donc donner les permissions de connexion, envoi et réception à l’adresse du portefeuille créé par défaut dans le second nœud.

Le premier nœud tourne dans la première console que nous avons démarrée. Nous allons donc ouvrir une troisième console et utiliser le client mutichain-cli.exe pour envoyer des commandes au nœud principal.

Gérer les permissions

Tapez l’instruction suivante en spécifiant l’adresse du wallet du second nœud.
multichain-cli mychain -datadir=./node1 -port=7000 -rpcport=7001 grant 1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKu u connect,send,receive

{"method":"grant","params":["1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu","connect,send,receive"],"id":1,"chain_name":"mychain"}

90b0f40331b6e62de12d8133c1e0f99b0c9a3b3180ed6d33d0626451e5885bad

De retour dans la console 2, nous pouvons maintenant démarrer le second nœud. La synchronisation des blocs se fait automatiquement.

multichaind mychain@192.168.193.1:7000 -port=8000 -rpcport=8001 -datadir=.\node2 -daemon

MultiChain 1.0.1 Daemon (protocol 10009)

Retrievying blockchain parameters from the seed node 192.168.193.1:7000 ...

Other nodes can connect to this node using:
multichaind mychain@192.168.129.70:8000

This host has multiple IP addresses, so from some networks:
multichaind mychain@192.168.193.1:8000
multichaind mychain@192.168.93.1:8000

Node started

Simplifions-nous la vie

Préciser à chaque fois les numéros de ports est à la longue fastidieux et source d’erreur. Nous allons donc nous simplifier la vie et modifier les fichiers multichain.conf à la racine des répertoires node1 et node2. Ces fichiers existent mais sont actuellement vide.

Dans node1\multichain.conf, ajoutez les deux lignes suivantes :
port=7000
rpcport=7001

Dans node2\multichain.conf, ajoutez les deux lignes suivantes :
port=8000
rpcport=8001

Dans les consoles 1 et 2, stoppez les deux instances avec CTRL-C. Les deux nœuds peuvent maintenant être démarrés comme suit, sur chaque console :

multichaind mychain -daemon -datadir=./node1
multichaind mychain -daemon -datadir=./node2


Lancer des requêtes sur les nœuds

Nos deux nœuds tournent maintenant dans deux consoles séparées. Nous allons en ouvrir une troisième pour réaliser nos premières transactions. Vous constaterez que toutes les réponses sont au format JSON.

multichain-cli -datadir=./node1 mychain getinfo

{"method":"getinfo","params":[],"id":1,"chain_name":"mychain"}
{
    "version" : "1.0.1",
    "nodeversion" : 10001901,
    "protocolversion" : 10009,
    "chainname" : "mychain",
    "description" : "MultiChain mychain",
    "protocol" : "multichain",
    "port" : 7000,
    "setupblocks" : 60,
    "nodeaddress" : "mychain@192.168.129.70:7000",
    "burnaddress" : "1XXXXXXWjmXXXXXXdxXXXXXXK1XXXXXXTHeDHJ",
    "incomingpaused" : false,
    "miningpaused" : false,
    "walletversion" : 60000,
    "balance" : 0.00000000,
    "walletdbversion" : 2,
    "reindex" : false,
    "blocks" : 70,
    "timeoffset" : 0,
    "connections" : 3,
    "proxy" : "",
    "difficulty" : 0.00000006,
    "testnet" : false,
    "keypoololdest" : 1507268598,
    "keypoolsize" : 2,
    "paytxfee" : 0.00000000,
    "relayfee" : 0.00000000,
    "errors" : ""
}

Tout d’abord, pour effectuer des transferts de valeurs  entre les deux portefeuilles, nous devons en récupérer les adresses :

multichain-cli -datadir=./node1 mychain getaddresses

{"method":"getaddresses","params":[],"id":1,"chain_name":"mychain"}
[
    "13EhAWMHfzTzdxWnjKDRLibzJ9E3t3jfBxWHFD"
]

multichain-cli -datadir=./node2 mychain getaddresses

{"method":"getaddresses","params":[],"id":1,"chain_name":"mychain"}
[
    "1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu"
]

Nous allons maintenant verser 1000 unités sur le premier portefeuille, qui rappelons-le, a été créé sur le premier nœud. Le type d’unité est à votre convenance. Dans la terminologie de MultiChain, on parlera d’asset. Le paramètre 0.01 indique la divisibilité de l’asset. Pour mon exemple, j’ai décidé de travailler en EUR.

multichain-cli -datadir=./node1 mychain issue 13EhAWMHfzTzdxWnjKDRLibzJ9E3t3jfBxWHFD EUR 1000 0.01

{"method":"issue","params":["13EhAWMHfzTzdxWnjKDRLibzJ9E3t3jfBxWHFD","EUR",1000,0.01000000],"id":1,"chain_name":"mychain"}

3c91c34d16e7134cfd78afc4aa35d1f1d3f188f008372dd668d4d8491a6d1c94

Si nous listons les assets sur chacun des nœuds, nous voyons que l’information a bien été répliquée :

multichain-cli -datadir=./node1 mychain listassets

{"method":"listassets","params":[],"id":1,"chain_name":"mychain"}
[
    {
        "name" : "EUR",
        "issuetxid" : "3c91c34d16e7134cfd78afc4aa35d1f1d3f188f008372dd668d4d8491a6d1c94",
        "assetref" : "74-265-37180",
        "multiple" : 100,
        "units" : 0.01000000,
        "open" : false,
        "details" : {
        },
        "issueqty" : 1000.00000000,
        "issueraw" : 100000,
        "subscribed" : false
    }
]

multichain-cli -datadir=./node2 mychain listassets

{"method":"listassets","params":[],"id":1,"chain_name":"mychain"}
[
    {
        "name" : "EUR",
        "issuetxid" : "3c91c34d16e7134cfd78afc4aa35d1f1d3f188f008372dd668d4d8491a6d1c94",
        "assetref" : "74-265-37180",
        "multiple" : 100,
        "units" : 0.01000000,
        "open" : false,
        "details" : {
        },
        "issueqty" : 1000.00000000,
        "issueraw" : 100000,
        "subscribed" : false
    }
]

Par contre, si on demande la balance totale d’EUR dans les deux portefeuilles, on constate qu’elle est bien à 1000 dans le nœud 1 et vide dans le nœud 2 :

multichain-cli -datadir=./node2 mychain gettotalbalances

{"method":"gettotalbalances","params":[],"id":1,"chain_name":"mychain"}

[
]

multichain-cli -datadir=./node1 mychain gettotalbalances

{"method":"gettotalbalances","params":[],"id":1,"chain_name":"mychain"}

[
    {
        "name" : "EUR",
        "assetref" : "74-265-37180",
        "qty" : 1000.00000000
    }
]

Nous allons maintenant transférer fictivement 100 EUR depuis le premier portefeuille vers le second, en utilisant l’adresse de ce dernier:

multichain-cli -datadir=./node1 mychain sendasset 1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu EUR 100

{"method":"sendasset","params":["1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu","EUR",100],"id":1,"chain_name":"mychain"}

c1c0bca6750328f027f303aafecf3e477a27d62a8e9241220ba47d81a3fb9039

Consultons à présent la balance totale des deux portefeuilles. Elle est bien à 900 sur le premier nœud et à 100 sur le second.

multichain-cli -datadir=./node2 mychain gettotalbalances

{"method":"gettotalbalances","params":[],"id":1,"chain_name":"mychain"}

[
    {
        "name" : "EUR",
        "assetref" : "74-265-37180",
        "qty" : 100.00000000
    }
]

multichain-cli -datadir=./node1 mychain gettotalbalances

{"method":"gettotalbalances","params":[],"id":1,"chain_name":"mychain"}

[
    {
        "name" : "EUR",
        "assetref" : "74-265-37180",
        "qty" : 900.00000000
    }
]

Enfin, nous allons consulter l’historique des deux dernières transactions effectuées sur nos portefeuilles. On constate bien la présence d’un débit de 100 EUR, suivi d’un crédit du même montant vers le second portefeuille.

multichain-cli -datadir=./node1 mychain listwallettransactions 2

{"method":"listwallettransactions","params":[2],"id":1,"chain_name":"mychain"}
[
    {
        "balance" : {
            "amount" : 0.00000000,
            "assets" : [
                {
                    "name" : "EUR",
                    "assetref" : "74-265-37180",
                    "qty" : 1000.00000000
                }
            ]
        },
        "myaddresses" : [
            "13EhAWMHfzTzdxWnjKDRLibzJ9E3t3jfBxWHFD"
        ],
        "addresses" : [
        ],
        "permissions" : [
        ],
        "issue" : {
            "name" : "EUR",
            "assetref" : "74-265-37180",
            "multiple" : 100,
            "units" : 0.01000000,
            "open" : false,
            "details" : {
            },
            "qty" : 1000.00000000,
            "raw" : 100000,
            "addresses" : [
                "13EhAWMHfzTzdxWnjKDRLibzJ9E3t3jfBxWHFD"
            ]
        },
        "items" : [
        ],
        "data" : [
        ],
        "confirmations" : 22,
        "blockhash" : "009a5f78f3dd02782fd912e1fed30e055620dd7a400ae55a6b34f0bbefd1663c",
        "blockindex" : 1,
        "blocktime" : 1507280420,
        "txid" : "3c91c34d16e7134cfd78afc4aa35d1f1d3f188f008372dd668d4d8491a6d1c94",
        "valid" : true,
        "time" : 1507280409,
        "timereceived" : 1507280409
    },
    {
        "balance" : {
            "amount" : 0.00000000,
            "assets" : [
                {
                    "name" : "EUR",
                    "assetref" : "74-265-37180",
                    "qty" : -100.00000000
                }
            ]
        },
        "myaddresses" : [
            "13EhAWMHfzTzdxWnjKDRLibzJ9E3t3jfBxWHFD"
        ],
        "addresses" : [
            "1Pc3jKAqhvRU2DxGySadYQLmNvBiohZgUaCKuu"
        ],
        "permissions" : [
        ],
        "items" : [
        ],
        "data" : [
        ],
        "confirmations" : 11,
        "blockhash" : "001b94bc83a2df11d368788f4573aa12a3f2d3f8f8d77cf0ef5a4690a7a92b81",
        "blockindex" : 1,
        "blocktime" : 1507280721,
        "txid" : "c1c0bca6750328f027f303aafecf3e477a27d62a8e9241220ba47d81a3fb9039",
        "valid" : true,
        "time" : 1507280711,
        "timereceived" : 1507280711
    }
]

Voilà pour cette première partie qui résume mes propres expérimentations. Dans un prochain article, nous verrons comment utiliser l'API de MultiChain depuis Node.JS pour réaliser un petit logiciel client.