Skip to content

Commit f26351d

Browse files
committed
Communication with other nodes
1 parent c6fdc31 commit f26351d

File tree

9 files changed

+112
-28
lines changed

9 files changed

+112
-28
lines changed

README.md

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Clean code approach to blockchain technology. Learn blockchain by reading source
1212
- [x] Genesis block
1313
- [x] Storing and validate Blockchain
1414
- [x] Proof of Work with difficulty (missing consensus on the difficulty)
15-
- [ ] Communicating with other nodes & controlling the node (based on ReactPHP)
15+
- [X] Communicating with other nodes & controlling the node (based on ReactPHP)
1616
- [ ] Going serverless with AWS Lambda (experiment)
1717
- [ ] Start working on KondasCoin [akondas/coin](https://github.com/akondas/coin) :rocket: (Transactions, Wallet, Transaction relaying, Maybe some UI)
1818

@@ -40,21 +40,36 @@ bin/node --p2p-port=2020
4040

4141
To control node you can use simple (pseudo) REST API:
4242

43+
**[GET] /blocks**
44+
Response (list of all blocks):
4345
```
44-
GET /blocks
45-
46-
Return list of blocks:
4746
[{"index":0,"hash":"8b31c9ec8c2df21968aca3edd2bda8fc77ed45b0b3bc8bc39fa27d5c795bc829","previousHash":"","createdAt":"2018-02-23 23:59:59","data":"PHP is awesome!","difficulty":0,"nonce":0}]
4847
```
4948

49+
**[POST] /mine**
50+
Request (raw):
51+
```
52+
Data to mine (any string).
53+
```
54+
Response (mined block):
5055
```
51-
POST /mine
52-
"post content is data to mine"
53-
54-
Return mined block:
5556
{"index":1,"hash":"a6eba6325a677802536337dc83268e524ffae5dc7db0950c98ff970846118f80","previousHash":"8b31c9ec8c2df21968aca3edd2bda8fc77ed45b0b3bc8bc39fa27d5c795bc829","createdAt":"2018-03-13 22:37:07","data":"Something goof","difficulty":0,"nonce":0}
5657
```
5758

59+
**[GET] /peers**
60+
Response (list of all connected peers):
61+
```
62+
[{"host":"127.0.0.1","port":3131}]
63+
```
64+
65+
**[POST] /peers/add**
66+
Request (json with peer):
67+
```
68+
{"host":"127.0.0.1", "port":"3131"}
69+
```
70+
Response: 204 (empty)
71+
72+
5873
## Tests
5974

6075
To run test suite:

bin/node

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ $node = new Node(
2525
new Miner(new Blockchain(Block::genesis()), new HashDifficulty\ZeroPrefix()),
2626
$p2pServer
2727
);
28+
$p2pServer->attachNode($node);
2829

2930
(new SocketServer($p2pPort, $loop))->on('connection', $p2pServer);
3031
(new HttpServer(new WebServer($node)))->listen(new SocketServer($httpPort, $loop));

src/Block.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ public function hash(): string
103103
return $this->hash;
104104
}
105105

106+
public function previousHash(): string
107+
{
108+
return $this->previousHash;
109+
}
110+
106111
public function difficulty(): int
107112
{
108113
return $this->difficulty;

src/Blockchain.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,9 @@ public function blocks(): array
6060
{
6161
return $this->blocks;
6262
}
63+
64+
public function size(): int
65+
{
66+
return count($this->blocks);
67+
}
6368
}

src/Miner.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,13 @@ public function blockchain(): Blockchain
5151
{
5252
return $this->blockchain;
5353
}
54+
55+
public function replaceBlockchain(Blockchain $blockchain): void
56+
{
57+
if (! $blockchain->isValid()) {
58+
return;
59+
}
60+
61+
$this->blockchain = $blockchain;
62+
}
5463
}

src/Node.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function blocks(): array
3838
public function mineBlock(string $data): Block
3939
{
4040
$block = $this->miner->mineBlock($data);
41-
$this->p2pServer->broadcast(new Message(Message::TYPE_LAST_BLOCK, json_encode($block)));
41+
$this->p2pServer->broadcast(new Message(Message::BLOCKCHAIN, serialize($this->blockchain()->withLastBlockOnly())));
4242

4343
return $block;
4444
}
@@ -55,4 +55,14 @@ public function connect(string $host, int $port): void
5555
{
5656
$this->p2pServer->connect($host, $port);
5757
}
58+
59+
public function blockchain(): Blockchain
60+
{
61+
return $this->miner->blockchain();
62+
}
63+
64+
public function replaceBlockchain(Blockchain $blockchain): void
65+
{
66+
$this->miner->replaceBlockchain($blockchain);
67+
}
5868
}

src/Node/Message.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@
66

77
final class Message
88
{
9-
public const TYPE_LAST_BLOCK = 'last-block';
9+
public const REQUEST_LATEST = 'request-latest';
10+
11+
public const REQUEST_ALL = 'request-all';
12+
13+
public const BLOCKCHAIN = 'blockchain';
1014

1115
/**
1216
* @var string
1317
*/
1418
private $type;
1519

1620
/**
17-
* @var string
21+
* @var ?string
1822
*/
1923
private $data;
2024

21-
public function __construct(string $type, string $data)
25+
public function __construct(string $type, ?string $data = null)
2226
{
2327
$this->type = $type;
2428
$this->data = $data;
@@ -29,7 +33,7 @@ public function type(): string
2933
return $this->type;
3034
}
3135

32-
public function data(): string
36+
public function data(): ?string
3337
{
3438
return $this->data;
3539
}

src/Node/P2pServer.php

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Blockchain\Node;
66

7+
use Blockchain\Blockchain;
78
use Blockchain\Node;
89
use React\Socket\ConnectionInterface;
910
use React\Socket\Connector;
@@ -33,12 +34,40 @@ public function __construct(Connector $connector)
3334

3435
public function __invoke(ConnectionInterface $connection): void
3536
{
36-
$peer = new Peer($connection);
37-
if ($this->contains($peer)) {
37+
if (isset($this->peers[$connection->getRemoteAddress()])) {
3838
return;
3939
}
4040

41-
//$connection->on('data')
41+
$connection->on('data', function (string $data) use ($connection): void {
42+
$message = unserialize($data, [Message::class]);
43+
switch ($message->type()) {
44+
case Message::REQUEST_LATEST:
45+
$connection->write(serialize(new Message(
46+
Message::BLOCKCHAIN,
47+
serialize($this->node->blockchain()->withLastBlockOnly())
48+
)));
49+
50+
break;
51+
case Message::REQUEST_ALL:
52+
$connection->write(serialize(new Message(
53+
Message::BLOCKCHAIN,
54+
serialize($this->node->blockchain())
55+
)));
56+
57+
break;
58+
case Message::BLOCKCHAIN:
59+
$this->handleBlockchain(unserialize($message->data(), [Blockchain::class]), $connection);
60+
61+
break;
62+
}
63+
});
64+
65+
$connection->on('close', function () use ($connection): void {
66+
unset($this->peers[$connection->getRemoteAddress()]);
67+
});
68+
69+
$this->peers[$connection->getRemoteAddress()] = new Peer($connection);
70+
$this->peers[$connection->getRemoteAddress()]->send(new Message(Message::REQUEST_LATEST));
4271
}
4372

4473
public function attachNode(Node $node): void
@@ -59,24 +88,35 @@ public function connect(string $host, int $port): void
5988

6089
public function broadcast(Message $message): void
6190
{
91+
foreach ($this->peers as $peer) {
92+
$peer->send($message);
93+
}
6294
}
6395

6496
/**
6597
* @return Peer[]
6698
*/
6799
public function peers(): array
68100
{
69-
return $this->peers;
101+
return array_values($this->peers);
70102
}
71103

72-
private function contains(Peer $peer): bool
104+
private function handleBlockchain(Blockchain $blockchain, ConnectionInterface $connection): void
73105
{
74-
foreach ($this->peers as $p) {
75-
if ($p->isEqual($peer)) {
76-
return true;
77-
}
106+
if ($blockchain->size() === 0) {
107+
return;
78108
}
79109

80-
return false;
110+
if ($blockchain->last()->index() <= $this->node->blockchain()->last()->index()) {
111+
return; // received blockchain is no longer than current blockchain, skip
112+
}
113+
114+
if ($blockchain->last()->previousHash() === $this->node->blockchain()->last()->hash()) {
115+
$this->node->blockchain()->add($blockchain->last());
116+
} elseif ($blockchain->size() === 1) {
117+
$connection->write(serialize(new Message(Message::REQUEST_ALL)));
118+
} else {
119+
$this->node->replaceBlockchain($blockchain);
120+
}
81121
}
82122
}

src/Node/Peer.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ public function port(): int
3434
return parse_url($this->connection->getRemoteAddress(), PHP_URL_PORT);
3535
}
3636

37-
public function isEqual(self $peer): bool
38-
{
39-
$this->host() === $peer->host() && $this->port() === $peer->port();
40-
}
41-
4237
/**
4338
* @return mixed[]
4439
*/

0 commit comments

Comments
 (0)