Skip to content

Commit c6fdc31

Browse files
committed
Work in progress with P2pServer
1 parent 41183e1 commit c6fdc31

File tree

11 files changed

+151
-94
lines changed

11 files changed

+151
-94
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ To start the node:
2424
bin/node
2525
```
2626

27-
Default port is 8080 but you can change it with `--port` param:
27+
Default web server port is 8080 but you can change it with `--http-port` param:
2828

2929
```
30-
bin/node --port=9090
30+
bin/node --http-port=9090
31+
```
32+
33+
Default p2p server port is 3030 but you can change it with `--p2p-port` param:
34+
35+
```
36+
bin/node --p2p-port=2020
3137
```
3238

3339
## API

bin/node

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,33 @@
33

44
use React\EventLoop\Factory;
55
use React\Http\Server as HttpServer;
6-
use React\Socket\Server;
6+
use React\Socket\Server as SocketServer;
7+
use React\Socket\Connector;
78
use Blockchain\Node;
89
use Blockchain\Miner;
910
use Blockchain\Blockchain;
1011
use Blockchain\Block;
1112
use Blockchain\Miner\HashDifficulty;
13+
use Blockchain\WebServer;
1214

1315
require __DIR__.'/../vendor/autoload.php';
1416

15-
$loop = Factory::create();
17+
$params = getopt('', ['http-port::', 'p2p-port::']);
18+
$httpPort = $params['http-port'] ?? 8080;
19+
$p2pPort = $params['p2p-port'] ?? 3030;
1620

17-
$node = new Node(new Miner(new Blockchain(Block::genesis()), new HashDifficulty\ZeroPrefix()));
18-
$webServer = new HttpServer(new Node\WebServer($node));
21+
$loop = Factory::create();
1922

20-
$params = getopt('', ['port::']);
21-
$port = $params['port'] ?? 8080;
23+
$p2pServer = new Node\P2pServer(new Connector($loop));
24+
$node = new Node(
25+
new Miner(new Blockchain(Block::genesis()), new HashDifficulty\ZeroPrefix()),
26+
$p2pServer
27+
);
2228

23-
$socket = new Server($port, $loop);
24-
$webServer->listen($socket);
29+
(new SocketServer($p2pPort, $loop))->on('connection', $p2pServer);
30+
(new HttpServer(new WebServer($node)))->listen(new SocketServer($httpPort, $loop));
2531

26-
echo sprintf("Server running at http://127.0.0.1:%s\n", $port);
32+
echo sprintf("Web server running at http://127.0.0.1:%s\n", $httpPort);
33+
echo sprintf("P2p server running at tcp://127.0.0.1:%s\n", $p2pPort);
2734

2835
$loop->run();

src/Blockchain.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public function last(): Block
4848
return end($this->blocks);
4949
}
5050

51+
public function withLastBlockOnly(): self
52+
{
53+
return new self($this->last());
54+
}
55+
5156
/**
5257
* @return Block[]
5358
*/

src/Node.php

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,26 @@
55
namespace Blockchain;
66

77
use Blockchain\Node\Message;
8+
use Blockchain\Node\P2pServer;
89
use Blockchain\Node\Peer;
910
use Blockchain\Node\Peers;
1011

1112
final class Node
1213
{
1314
/**
14-
* @var Peers
15+
* @var Miner
1516
*/
16-
private $peers;
17+
private $miner;
1718

1819
/**
19-
* @var Miner
20+
* @var P2pServer
2021
*/
21-
private $miner;
22+
private $p2pServer;
2223

23-
public function __construct(Miner $miner)
24+
public function __construct(Miner $miner, P2pServer $p2pServer)
2425
{
2526
$this->miner = $miner;
26-
$this->peers = new Peers();
27+
$this->p2pServer = $p2pServer;
2728
}
2829

2930
/**
@@ -37,7 +38,7 @@ public function blocks(): array
3738
public function mineBlock(string $data): Block
3839
{
3940
$block = $this->miner->mineBlock($data);
40-
$this->peers->broadcast(new Message(Message::TYPE_LAST_BLOCK, json_encode($block)));
41+
$this->p2pServer->broadcast(new Message(Message::TYPE_LAST_BLOCK, json_encode($block)));
4142

4243
return $block;
4344
}
@@ -47,15 +48,11 @@ public function mineBlock(string $data): Block
4748
*/
4849
public function peers(): array
4950
{
50-
return $this->peers->all();
51+
return $this->p2pServer->peers();
5152
}
5253

53-
public function addPeer(Peer $peer): void
54+
public function connect(string $host, int $port): void
5455
{
55-
if ($this->peers->contains($peer)) {
56-
return;
57-
}
58-
59-
$this->peers->add($peer);
56+
$this->p2pServer->connect($host, $port);
6057
}
6158
}

src/Node/P2pServer.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Blockchain\Node;
6+
7+
use Blockchain\Node;
8+
use React\Socket\ConnectionInterface;
9+
use React\Socket\Connector;
10+
use RuntimeException;
11+
12+
class P2pServer
13+
{
14+
/**
15+
* @var Node
16+
*/
17+
private $node;
18+
19+
/**
20+
* @var Connector
21+
*/
22+
private $connector;
23+
24+
/**
25+
* @var Peer[]
26+
*/
27+
private $peers = [];
28+
29+
public function __construct(Connector $connector)
30+
{
31+
$this->connector = $connector;
32+
}
33+
34+
public function __invoke(ConnectionInterface $connection): void
35+
{
36+
$peer = new Peer($connection);
37+
if ($this->contains($peer)) {
38+
return;
39+
}
40+
41+
//$connection->on('data')
42+
}
43+
44+
public function attachNode(Node $node): void
45+
{
46+
if ($this->node !== null) {
47+
throw new RuntimeException('Node already attached to p2pServer');
48+
}
49+
50+
$this->node = $node;
51+
}
52+
53+
public function connect(string $host, int $port): void
54+
{
55+
$this->connector->connect(sprintf('%s:%s', $host, $port))->then(function (ConnectionInterface $connection): void {
56+
$this($connection);
57+
});
58+
}
59+
60+
public function broadcast(Message $message): void
61+
{
62+
}
63+
64+
/**
65+
* @return Peer[]
66+
*/
67+
public function peers(): array
68+
{
69+
return $this->peers;
70+
}
71+
72+
private function contains(Peer $peer): bool
73+
{
74+
foreach ($this->peers as $p) {
75+
if ($p->isEqual($peer)) {
76+
return true;
77+
}
78+
}
79+
80+
return false;
81+
}
82+
}

src/Node/Peer.php

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,38 @@
55
namespace Blockchain\Node;
66

77
use JsonSerializable;
8+
use React\Socket\ConnectionInterface;
89

910
final class Peer implements JsonSerializable
1011
{
1112
/**
12-
* @var string
13+
* @var ConnectionInterface
1314
*/
14-
private $host;
15+
private $connection;
1516

16-
/**
17-
* @var int
18-
*/
19-
private $port;
17+
public function __construct(ConnectionInterface $connection)
18+
{
19+
$this->connection = $connection;
20+
}
2021

21-
public function __construct(string $host, int $port)
22+
public function send(Message $message): void
2223
{
23-
$this->host = $host;
24-
$this->port = $port;
24+
$this->connection->write(serialize($message));
2525
}
2626

2727
public function host(): string
2828
{
29-
return $this->host;
29+
return parse_url($this->connection->getRemoteAddress(), PHP_URL_HOST);
3030
}
3131

3232
public function port(): int
3333
{
34-
return $this->port;
34+
return parse_url($this->connection->getRemoteAddress(), PHP_URL_PORT);
3535
}
3636

3737
public function isEqual(self $peer): bool
3838
{
39-
$this->host === $peer->host && $this->port === $peer->port;
39+
$this->host() === $peer->host() && $this->port() === $peer->port();
4040
}
4141

4242
/**
@@ -45,8 +45,8 @@ public function isEqual(self $peer): bool
4545
public function jsonSerialize(): array
4646
{
4747
return [
48-
'host' => $this->host,
49-
'port' => $this->port,
48+
'host' => $this->host(),
49+
'port' => $this->port(),
5050
];
5151
}
5252
}

src/Node/Peers.php

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/Node/WebServer.php renamed to src/WebServer.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
declare(strict_types=1);
44

5-
namespace Blockchain\Node;
5+
namespace Blockchain;
66

7-
use Blockchain\Node;
8-
use Blockchain\Node\Response\JsonResponse;
7+
use Blockchain\WebServer\Response\JsonResponse;
98
use Psr\Http\Message\ServerRequestInterface;
109
use React\Http\Response;
1110

@@ -32,7 +31,11 @@ public function __invoke(ServerRequestInterface $request): Response
3231
return new JsonResponse($this->node->peers());
3332
case 'POST:peers/add':
3433
$data = json_decode($request->getBody()->getContents(), true);
35-
$this->node->addPeer(new Peer($data['host'], $data['port']));
34+
if (! isset($data['host'], $data['port'])) {
35+
return new Response(400);
36+
}
37+
38+
$this->node->connect($data['host'], (int) $data['port']);
3639

3740
return new Response(204);
3841
default:

src/Node/Response.php renamed to src/WebServer/Response.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Blockchain\Node;
5+
namespace Blockchain\WebServer;
66

77
use Psr\Http\Message\ResponseInterface;
88

src/Node/Response/JsonResponse.php renamed to src/WebServer/Response/JsonResponse.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
declare(strict_types=1);
44

5-
namespace Blockchain\Node\Response;
5+
namespace Blockchain\WebServer\Response;
66

7-
use Blockchain\Node\Response;
7+
use Blockchain\WebServer\Response;
88
use React\Http\Response as HttpResponse;
99

1010
final class JsonResponse extends HttpResponse implements Response

0 commit comments

Comments
 (0)