Dodajemy p2p i kończymy blockchain!
Stało się! Ostatnia część najlepszej serii. Zostało dodać p2p i synchronizację. Warto się zapoznać z https://blog.patys.pl/2018/09/10/p2p-komunikacja-serwerow-nodejs-z-wykorzystaniem-websockets/ Będziemy bazować na tym artykule.
Serwer http
Od tego będzie najprościej zacząć: https://expressjs.com/en/starter/hello-world.html. Na podstawie przykładu dodamy tylko swoje końcówki, czyli:
Root/Home (zależy kto jak nazywa):
app.get('/', (req, res) => {
res.send(\`numberOfBlocks: ${patyscoin.blockchain.length}
difficulty: ${patyscoin.difficulty},\`);
});
Wyświetli nam liczbę bloków i trudność.
Connect:
app.get('/connect/:address', (req, res) => {
p2p.addNewConnection(req.params.address);
res.redirect('/');
});
Posłuży do stworzenia nowego połączenia z innym serwerem.
Block:
app.get('/block/:blockNr', (req, res) =>
res.json(patyscoin.blockchain[req.params.blockNr]));
Wyświetli nam blok o wybranym numerze i zwróci jego json.
Transactions:
app.get('/transactions', (req, res) =>
res.json(patyscoin.transactions));
Pokaże listę oczekujących transakcji w json.
Test:
app.get('/test', (req, res) => {
p2p.broadcast({ type: 'TRANSACTION', data: transaction });
patyscoin.addTransaction(fromJSON(JSON.parse(transaction)));
res.redirect('/');
});
Doda nam testową transakcję do blockchain.
Mine:
app.get('/mine', (req, res) => {
patyscoin.addBlock();
res.redirect('/');
});
Wykopie nowy blok. Robimy to poprzez ręczne uruchomienie kopania w ramach testów. Daje większą kontrolę żeby zobaczyć jak działa nasz blockchain.
Bloki i transakcje
Jak przyjrzymy się powyższym końcówkom potrzebujemy dwie rzeczy. “p2p” oraz “patyscoin”. Drugi z tych elementów już mamy, czyli nasz blockchain stworzony w poprzednich artykułach. Zostaje nam połączenie między serwerami.
Tworząc to napotkałem spory problem. Pewnie źle do tego podszedłem, ale w sumie potrzebowałem p2p i blockchain w każdym pliku odpowiadającym za połączenie i wpadłem w nazwijmy to “require hell” - https://stackoverflow.com/questions/23341883/can-node-modules-require-each-other. p2p i blockchain potrzebowały dołączać się razem i byłem zmuszony zrobić p2pBridge, który połączy nam blockchain oraz p2p.
Tutaj pojawił się problem. Jak zrobić to najprościej. Zdecydowałem się dodać coś w stylu “subscribe pattern”. Jednak poszło mi to dosyć nieudolnie. Próbowałem zrobić to samemu nie sprawdzając dokładnie istniejących rozwiązań i popełniłem spory błąd. Nauczka na przyszłość: zawsze robić research jeśli tworzysz coś co ma zapewne wzorzec. (Tutaj jest jak powinno to mniej więcej wyglądać: https://gist.github.com/learncodeacademy/777349747d8382bfb722). A oto moja implementacja:
const patyscoin = require('./index');
const { fromJSON } = require('./src/transactionBuilder');
const p2p = require('./p2p');
const onNewTransaction = (data) => {
let transaction;
try {
transaction = fromJSON(JSON.parse(data));
} catch(e) {
transaction = fromJSON(data);
}
patyscoin.addTransaction(transaction);
}
const onNewBlock = (data) => {
let block;
try {
block = patyscoin.blockFromJSON(JSON.parse(data));
} catch(e) {
block = patyscoin.blockFromJSON(data);
}
patyscoin.addBlock(block);
}
const broadcastNewTransaction = (transaction) => {
p2p.broadcast({ type: 'TRANSACTION', data: transaction });
}
const broadcastNewBlock = (block) => {
p2p.broadcast({ type: 'BLOCK', data: block });
}
const init = () => {
patyscoin.subscribe(broadcastNewTransaction, broadcastNewBlock);
p2p.subscribe(onNewTransaction, onNewBlock);
}
module.exports = { init };
Jak widać najważniejsza jest funkcja "init". Dodaje do patyscoin oraz p2p wszystkie wymienione wyżej funkcje. Czyli blockchain (patyscoin) może reagować na transakcje i je rozsyłać oraz p2p może dodawać bloki i transakcje jeśli takie przyjdą.
Blockchain
Przejdźmy na początek do blockchain i przyjrzyjmy się funkcji “subscribe”.
subscribe(newTransaction, newBlock) {
this.newTransactionCreated = newTransaction;
this.newBlockCreated = newBlock;
}
Jak widzimy przypisuje funkcje do blockchain. W ten sposób blockchain może reagować, kiedy jest dodana transakcja lub blok i pokazać to światu. Na przykład transakcja:
addTransaction(newTransaction) {
if (this.checkTransaction(newTransaction)) {
this.transactions.push(newTransaction);
if(this.newTransactionCreated) {
this.newTransactionCreated(newTransaction);
}
console.info(`Transaction added: ${newTransaction.id}`)
} else {
console.error(`Cannot add transaction`);
}
}
Lub blok:
addBlock(newBlock = null) {
let block;
if (!newBlock) {
block = this.mineNewBlock();
} else {
block = newBlock;
}
if (this.validateBlock(block)) {
this.blockchain.push(block);
if (this.newBlockCreated) {
this.newBlockCreated(block);
}
} else console.error('invalid block!')
}
Dzięki temu odpalą się odpowiednie funkcje, czyli broadcastNewTransaction oraz broadcastNewBlock
P2P
Teraz czas zobaczyć jak p2p przekazuje informacje. Zasada działania jest taka sama.
/*
{ type: 'TRANSACTION', data: string }
{ type: 'SOCKETS', addresses: array }
*/
ws.on('message', (data) => {
const message = JSON.parse(data);
switch(message.type) {
case 'TRANSACTION':
onNewTransaction && onNewTransaction(message.data);
console.log('New transaction');
break;
case 'BLOCK':
onNewBlock && onNewBlock(message.data);
console.log('New block');
break;
case 'SOCKETS':
const newConnections = message.addresses.filter(add => !addresses.includes(add));
newConnections.forEach((con) => {
addNewConnection(con);
});
break;
default:
break;
}
});
};
Jak widzimy odpowiednia funkcja jest uruchamiana w zależności co zostanie przesłane. Gdy otrzymamy message z typem “TRANSACTION” to uruchomimy funkcję “onNewTransaction” jeśli istnieje.
Spójrzmy wyżej w implementację subscribe. Widzimy, że dane są parsowane i dodawane do blockchain.
Podsumowanie
To jest kawał kodu, z którego nie jestem dumny. Jest brzydki, ma dużo zależności i nie tak się powinno programować. To kod, który działa. Zdecydowałem się go opublikować, bo to jest pierwsza rzecz napisana jaka przyszła mi do głowy. Zasada była robić jak najszybciej i najprościej.
Powoli kończymy serię. Pojawią się tutaj jeszcze dodatki, jako kolejne posty mające na celu usprawnić i uzupełnić całą wiedzę potrzebną do stworzenia najprostszego blockchain. Podsumujmy co zrobiliśmy przez te wszystkie posty:
- Opowiedziałem o mojej fascynacji blockchain. Jak zaczęła się przygoda, która doprowadziła nas tutaj po roku tworzenia od zera.
- Rozpoczęliśmy implementację. Stworzyliśmy najprostsze bloki i strukturę. Był to pierwszy kod.
- Dalej trzeba było poznać matematykę - podstawa na jakiej opiera się ta technologia.
- Gdy tylko skończyliśmy ruszyliśmy zobaczyć zabezpieczenia. Dlaczego możemy zaufać blockchain, pamiętasz?
- Warto się było przyjrzeć istniejącemu produktowi. Jak działa i co go napędza.
- Dalej dodaliśmy kopanie. Proof of work, który zabezpieczył wszystko.
- Dodaliśmy portfel i transakcje. Możemy przesyłać na swoje konta dane.
- Znaleźliśmy rozwiązanie na połączenia pomiędzy serwerami i zrobiliśmy prosty czat.
- Teraz połączyliśmy wszystko w całość i mamy działający blockchain.
Zajęło mi to rok czasu. Nie robiłem tego fulltime ani za pieniądze. Po prostu w wolnych chwilach jako pasja. Żeby nie zatrzymywać się poszerzałem swoją wiedzę o inne przykłady i formy blockchain: https://blog.patys.pl/category/kryptowaluty/
Ruszyłem z informacjami dla początkujących, żeby unormować wiedzę: https://blog.patys.pl/category/krypto/
Wymagało to dużo samozaparcia i motywacji. Zajęło dużo czasu, ale się udało. Skończyłem to i ruszam dalej. Z nowym projektem i pomysłem.
Dam radę!!
Hello @patys! This is a friendly reminder that you have 3000 Partiko Points unclaimed in your Partiko account!
Partiko is a fast and beautiful mobile app for Steem, and it’s the most popular Steem mobile app out there! Download Partiko using the link below and login using SteemConnect to claim your 3000 Partiko points! You can easily convert them into Steem token!
https://partiko.app/referral/partiko
Congratulations @patys! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
You can view your badges on your Steem Board and compare to others on the Steem Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
Congratulations @patys! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!