Skip to content

Commit

Permalink
Routerクラス(P2Pネットワーク)
Browse files Browse the repository at this point in the history
  • Loading branch information
mizumotok committed Apr 26, 2018
1 parent 1e12c90 commit 768c762
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 19 deletions.
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -27,6 +27,7 @@
"crypto-js": "^3.1.9-1",
"elliptic": "^6.4.0",
"express": "^4.16.3",
"uuid": "^3.2.1"
"uuid": "^3.2.1",
"ws": "^5.1.1"
}
}
18 changes: 18 additions & 0 deletions src/__tests__/blockchain/blockchain.test.js
Expand Up @@ -107,4 +107,22 @@ describe('Blockchain', () => {
chain.push(justRightBlock);
expect(Blockchain.calcDifficultyTarget(chain)).toBe(difficultyTarget);
});

it('replaceChain test', () => {
blockchain.addBlock(newBlock);
const genesis = Block.genesis();

// 短いチェーンは無視
blockchain.replaceChain([genesis]);
expect(blockchain.chain).toHaveLength(2);

// 有効でないチェーンは無視
blockchain.replaceChain([genesis, new Block(0, 'xxx', 256, 0, [])]);
expect(blockchain.chain[1]).toBe(newBlock);

// 長くて有効なチェーンは置き換える
const newBlock2 = new Block(new Date(), newBlock.hash(), 256, 0, [], MINING_DURATION);
blockchain.replaceChain([genesis, newBlock, newBlock2]);
expect(blockchain.chain).toHaveLength(3);
});
});
7 changes: 5 additions & 2 deletions src/__tests__/blockchain/transaction.test.js
Expand Up @@ -4,15 +4,18 @@ import SHA256 from 'crypto-js/sha256';
import Blockchain, { Transaction } from '../../blockchain';
import Wallet from '../../wallet';
import Miner from '../../miner';
import Router from '../../router';

import { MINING_REWARD } from '../../config';

describe('Transaction', () => {
let wallet: Wallet;
let miner: Miner;
beforeEach(() => {
const blockchain = new Blockchain();
wallet = new Wallet(blockchain);
miner = new Miner(blockchain, wallet.publicKey);
const router = new Router(blockchain, false);
wallet = new Wallet(blockchain, router);
miner = new Miner(blockchain, wallet.publicKey, router);
});

it('createOutputs test', () => {
Expand Down
6 changes: 4 additions & 2 deletions src/__tests__/miner/index.test.js
Expand Up @@ -3,6 +3,7 @@
import Blockchain, { Transaction } from '../../blockchain';
import Miner from '../../miner';
import Wallet from '../../wallet';
import Router from '../../router';

describe('Miner', () => {
let blockchain;
Expand All @@ -11,8 +12,9 @@ describe('Miner', () => {

beforeEach(() => {
blockchain = new Blockchain();
wallet = new Wallet(blockchain);
miner = new Miner(blockchain, wallet.publicKey);
const router = new Router(blockchain, false);
wallet = new Wallet(blockchain, router);
miner = new Miner(blockchain, wallet.publicKey, router);
});

it('mine test', () => {
Expand Down
6 changes: 4 additions & 2 deletions src/__tests__/wallet/index.test.js
Expand Up @@ -4,6 +4,7 @@ import { ec as EC } from 'elliptic';
import Blockchain, { Transaction } from '../../blockchain';
import Miner from '../../miner';
import Wallet from '../../wallet';
import Router from '../../router';
import { MINING_REWARD } from '../../config';

const ec = new EC('secp256k1');
Expand All @@ -15,8 +16,9 @@ describe('Miner', () => {

beforeEach(() => {
blockchain = new Blockchain();
wallet = new Wallet(blockchain);
miner = new Miner(blockchain, wallet.publicKey);
const router = new Router(blockchain, false);
wallet = new Wallet(blockchain, router);
miner = new Miner(blockchain, wallet.publicKey, router);
});

it('balance test', () => {
Expand Down
15 changes: 15 additions & 0 deletions src/blockchain/blockchain.js
Expand Up @@ -65,6 +65,21 @@ class Blockchain {
return true;
});
}

replaceChain(newChain: Array<Block>) {
if (newChain.length < this.chain.length) {
console.log('チェーンが短いため無視します。');
return;
}

if (!Blockchain.isValidChain(newChain)) {
console.log('チェーンが有効でないため無視します。');
return;
}

this.chain = newChain;
console.log('ブロックチェーンを更新しました。');
}
}

export default Blockchain;
4 changes: 4 additions & 0 deletions src/config.js
Expand Up @@ -2,10 +2,14 @@ const DIFFICULTY_TARGET = 240;
const MINING_DURATION = 5000;
const MINING_REWARD = 50;
const HTTP_PORT = process.env.HTTP_PORT || 3001;
const P2P_PORT = process.env.P2P_PORT || 5001;
const PEERS = process.env.PEERS ? process.env.PEERS.split(',') : [];

module.exports = {
DIFFICULTY_TARGET,
MINING_DURATION,
MINING_REWARD,
HTTP_PORT,
P2P_PORT,
PEERS,
};
12 changes: 5 additions & 7 deletions src/index.js
Expand Up @@ -3,6 +3,7 @@
import express from 'express';
import bodyParser from 'body-parser';
import Blockchain from './blockchain';
import Router from './router';
import Wallet from './wallet';
import Miner from './miner';
import { HTTP_PORT } from './config';
Expand All @@ -12,9 +13,9 @@ app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(`${__dirname}/public`));

const bc = new Blockchain();
const wallet = new Wallet(bc);
const miner = new Miner(bc, wallet.publicKey);

const router = new Router(bc);
const wallet = new Wallet(bc, router);
const miner = new Miner(bc, wallet.publicKey, router);

app.get('/blocks', (req, res) => {
const r = bc.chain.map((b, i) => {
Expand All @@ -32,10 +33,7 @@ app.get('/transactions', (req, res) => {

app.post('/transact', (req, res) => {
const { recipient, amount } = req.body;
const tx = wallet.createTransaction(recipient, Number(amount));
if (tx) {
miner.pushTransaction(tx);
}
wallet.createTransaction(recipient, Number(amount));
res.redirect('/transactions');
});

Expand Down
7 changes: 6 additions & 1 deletion src/miner/index.js
@@ -1,16 +1,20 @@
// @flow

import Blockchain, { Block, Transaction } from '../blockchain';
import Router from '../router';

class Miner {
transactionPool: Array<Transaction>;
blockchain: Blockchain;
rewardAddress: string;
router: Router;

constructor(blockchain: Blockchain, rewardAddress: string) {
constructor(blockchain: Blockchain, rewardAddress: string, router: Router) {
this.transactionPool = [];
this.blockchain = blockchain;
this.rewardAddress = rewardAddress;
this.router = router;
this.router.subscribe(this);
}

mine() {
Expand Down Expand Up @@ -39,6 +43,7 @@ class Miner {

this.blockchain.addBlock(block);
this.clearTransactions();
this.router.mineDone();
}

pushTransaction(tx: Transaction) {
Expand Down
58 changes: 58 additions & 0 deletions src/router/index.js
@@ -0,0 +1,58 @@
// @flow

import WebSocket from 'ws';
import Blockchain, { Block, Transaction } from '../blockchain';
import Miner from '../miner';
import P2pServer from './p2p_server';
import MESSAGE_TYPES from './message_types';
import type { MessageData } from './message_types';

class Router {
blockchain: Blockchain;
miner: Miner;
sockets: Array<WebSocket>;
p2pServer: P2pServer;

constructor(blockchain: Blockchain, p2pEnabled: bool = true) {
this.blockchain = blockchain;
this.p2pServer = new P2pServer(this.blockchain, this.messageHandler.bind(this), p2pEnabled);
}

subscribe(miner: Miner) {
this.miner = miner;
}

pushTransaction(tx: Transaction) {
if (this.miner) {
this.miner.pushTransaction(tx);
}
this.p2pServer.broadcastTransaction(tx);
}

mineDone() {
this.p2pServer.broadcastMined();
}

messageHandler(data: MessageData) {
switch (data.type) {
case MESSAGE_TYPES.BLOCK_CHAIN:
this.blockchain.replaceChain(data.payload.map(o => Block.fromJSON(o)));
break;
case MESSAGE_TYPES.TRANSACTION:
if (this.miner) {
this.miner.pushTransaction(Transaction.fromJSON(data.payload));
}
break;
case MESSAGE_TYPES.MINED:
this.blockchain.replaceChain(data.payload.map(o => Block.fromJSON(o)));
if (this.miner) {
this.miner.clearTransactions();
}
break;
default:
break;
}
}
}

export default Router;
13 changes: 13 additions & 0 deletions src/router/message_types.js
@@ -0,0 +1,13 @@
const MESSAGE_TYPES = {
BLOCK_CHAIN: 'BLOCK_CHAIN',
TRANSACTION: 'TRANSACTION',
MINED: 'MINED',
};

type MessageData = {
type: string,
payload: any,
};

export default MESSAGE_TYPES;
export type { MessageData };
78 changes: 78 additions & 0 deletions src/router/p2p_server.js
@@ -0,0 +1,78 @@
// @flow

import WebSocket from 'ws';
import Blockchain, { Transaction } from '../blockchain';
import MESSAGE_TYPES from './message_types';
import type { MessageData } from './message_types';
import { P2P_PORT, PEERS } from '../config';

class P2pServer {
blockchain: Blockchain;
messageHandler: Function;
sockets: Array<WebSocket>;

constructor(blockchain: Blockchain, messageHandler: Function, p2pEnabled: bool) {
this.sockets = [];
this.blockchain = blockchain;
this.messageHandler = messageHandler;
if (p2pEnabled) {
this.listen();
}
}

listen() {
try {
const server = new WebSocket.Server({ port: P2P_PORT });
server.on('connection', socket => this.connect2Socket(socket));
this.connect2Peers();
console.log(`P2P Listening on ${P2P_PORT}`);
} catch (e) {
console.log('P2Pサーバの起動に失敗しました。');
}
}

connect2Peers() {
PEERS.forEach((peer) => {
const socket = new WebSocket(peer);
socket.on('open', () => this.connect2Socket(socket));
});
}

connect2Socket(socket: WebSocket) {
this.sockets.push(socket);
console.log('Socket connected');
socket.on('message', (message) => {
console.log(`received from peer: ${message}`);
const data: MessageData = JSON.parse(message);
this.messageHandler(data);
});
this.sendBlockchain(socket);
}

sendBlockchain(socket: WebSocket) {
P2pServer.sendData(socket, {
type: MESSAGE_TYPES.BLOCK_CHAIN,
payload: this.blockchain.chain.map(b => b.toJSON()),
});
}

broadcastTransaction(transaction: Transaction) {
this.sockets.forEach(socket => P2pServer.sendData(socket, {
type: MESSAGE_TYPES.TRANSACTION,
payload: transaction.toJSON(),
}));
}

broadcastMined() {
this.sockets.forEach(socket => P2pServer.sendData(socket, {
type: MESSAGE_TYPES.MINED,
payload: this.blockchain.chain.map(b => b.toJSON()),
}));
}

static sendData(socket: WebSocket, data: MessageData) {
socket.send(JSON.stringify(data));
}
}

export default P2pServer;
12 changes: 8 additions & 4 deletions src/wallet/index.js
Expand Up @@ -3,26 +3,30 @@
import uuidv1 from 'uuid/v1';
import { ec as EC } from 'elliptic';
import Blockchain, { Transaction } from '../blockchain';
import Router from '../router';

const ec = new EC('secp256k1');

class Wallet {
blockchain: Blockchain;
keyPair: any;
publicKey: string;
router: Router;

constructor(blockchain: Blockchain) {
constructor(blockchain: Blockchain, router: Router) {
this.blockchain = blockchain;
this.keyPair = ec.genKeyPair({ entropy: uuidv1() });
this.publicKey = this.keyPair.getPublic().encode('hex');
this.router = router;
}

createTransaction(recipient: string, amount: number): Transaction {
createTransaction(recipient: string, amount: number) {
if (amount > this.balance()) {
console.log('残高不足です。');
return null;
return;
}
return Transaction.createTransaction(this, recipient, amount);
const tx = Transaction.createTransaction(this, recipient, amount);
this.router.pushTransaction(tx);
}

balance() :number {
Expand Down
6 changes: 6 additions & 0 deletions yarn.lock
Expand Up @@ -4808,6 +4808,12 @@ ws@^4.0.0:
async-limiter "~1.0.0"
safe-buffer "~5.1.0"

ws@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-5.1.1.tgz#1d43704689711ac1942fd2f283e38f825c4b8b95"
dependencies:
async-limiter "~1.0.0"

xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
Expand Down

0 comments on commit 768c762

Please sign in to comment.