Po veľmi dlhom čase som znova popracoval na tutoriále. Už dlhšie som uvažoval, že napíšem niečo málo o IPFS ale nechcel som spracovávať jednoduché tutoriály z official webu, ale premýšľal som nad niečím zaujímavým. Prvotný nápad bola implementácia Etherea a IPFS, ale povedal som si, že ešte by náhodou niekto tvrdil, že som zaťažený len na Ethereum, a tak mi napadlo, že preskúmam bližšie projekt Stellar. Výsledok experimentovania je tento tutoriál, kde sa pozrieme na základný koncept implementácie technológie Stellar a komunikácie so Stellar blockchainom v kombinácii s využitím technológie IPFS.
Skôr než sa dostaneme ku konkrétnemu kódu, mali by sme si vysvetliť, o čom tieto technológie sú a predostrieť si ich základy a princípy.

IPFS – Inter-Planetary File System

Hneď na official webe sa dočítame o IPFS ako o peer-to-peer hypermedia protokole, ktorý robí web rýchlejší, bezpečnejší a viac otvorený. V skratke, vďaka tejto technológii sú naše dáta/súbory uložené permanentne, ich zálohovaním na rôzne počítače alebo zariadenia po celom svete.
Povedzme, že ako novinár potrebujem každý deň so svojimi kolegami zdieľať rôzne dokumenty či videá, ktoré bude následne denne sledovať množstvo ľudí po celom svete. V súčasnosti, pokiaľ pre rôzne dôvody nechceme využiť služby ako Youtube, je to docela drahá záležitosť. Potrebujeme dobré pripojenie, veľké množstvo, množstvo voľného miesta a myslieť na to, že video je prístupné len dovtedy, pokiaľ beží server a nie je zahltený veľkým množstvom návštev. Čo ak by ale video, namiesto toho, aby bolo uložené na konkrétnom zariadení, by mohlo byť distribuované po zariadeniach po celom svete a namiesto jeho sťahovania zo servera z opačného konca sveta, by sa stiahlo z najbližšieho uzla na sieti alebo najbližšieho IPFS servera s tým, že zvýšená návštevnosť neovplyvní jeho rýchlosť a ani snaha o jeho stiahnutie či zablokovanie neovplyvní prístup ľudí k týmto dátam. Práve toto je krása decentralizovaného, pernamentného internetu. Samozrejme, pokiaľ si zachováme príčetnosť, v tomto momente každému z nás svieti v hlave kontrolka, že v nesprávnych rukách alebo so zlými zámermi cybercrime a cyberbullying naberajú úplne nový level. Aby som nepredlžoval zbytočne tento tutoriál, ako funguje IPFS si vysvetlíme inokedy, prípadne si preštudujte whitepaper. IPFS by si zaslúžilo samostatný blogpost, keďže je dosť o čom hovoriť.

Official Web: https://ipfs.io/

Stellar

O Stellar som sa zmieňoval už skôr v mojom článku o konsenzuálnych algoritmoch. Podľa uvítania na offcial webe je Stellar platforma, ktorá spája banky, platobné systémy a ľudí. Môžeme si ju predstaviť ako infraštruktúru pre okamžité platby, takmer bez nákladov na transakcie. Pre zaujímavosť, Stellar ma ako zabudovanú feature priamo v platforme exchange mien/tokenov s možnosťou ich zamieňania priamo pri transakcii.

Rýchlosť
Čo mňa zaujalo na tejto platforme najviac, je jej rýchlosť. Stellar je signifikantne lacnejšia a rýchlejšia platforma než akákoľvek iná platforma, ako napríklad Ethereum. Keď pri Ethereu v mnohých prípadoch čakáme na potvrdenie transakcie niekoľko minút, v prípade Stellat to trvá max. 5 sekúnd. Nerád propagujem niečo, čo si sám neviem prepočítať, ale myslím, že stojí za zmienku, že na veľa fórach, diskusiách a aj na oficiálnych stránkach existuje tvrdenie, že spraviť 100 000 transakcií na Stellar stojí len 1 cent.

Smart Contracty
Čo sa týka bezpečnosti, Stellar má zámerne limitovaný systém pre smart contracty. Zámerne preto, aby sa predišlo potenciálne exploitovateľnému kódu. V porovnaní s Ethereom, Ethereum smart contracty, resp. jazyk Solidy je turing-complete. To znamená, že dokáže reprezentovať akúkoľvek inštrukciu, ktorú počítač dokáže spracovať, inými slovami, teoreticky naprogramujeme, čo len chceme.
V prípade Stellar Smart Contract (SSC), sú vyjadrené ako zloženie transakcií, ktoré sú spojené a vykonávané s použitím rôznych “obmedzení” alebo pravidiel:
Multisignature – Aké kľúče sú potrebné na autorizáciu určitej operácie? Aké strany sa musia dohodnúť na okolnostiach, aby vykonali určité kroky?
Batching/Atomicity – Aké operácie sa musia vyskytnúť spolu alebo naopak zlyhať? Čo sa musí stať, aby transakcia prešla alebo zlyhala?
Sequence – V akom poradí by sa mali transakcie spracovať? Aké sú obmedzenia a závislosti?
Time Bounds – Kedy môže byť transakcia spracovaná?

Protokol konsenzu – Federated Byzantine Agreement
V prípade konsenzuálneho algoritmu, ktorý zabezpečuje nemennosť, konzistenciu a dôveryhodnosť dát, Stellar má implmentovaný distribuovaný konsenzus.
Tento protokol funguje na základe využívania “quorov”, čo sú nody, ktoré majú za úlohu dosahovať medzi sebou konsenzus výmenou istých signatúr. Tým sú transakcie veľmi rýchle a s minimálnym, bezpečnostným poplatkom 0.00001 lumenov, čím sa zabraňuje DoS útokom na sieti. Tento protokol je často označovaný aj za výsledok evolúcie technológie blockchain.

IPFS & Stellar v praxy

Aby som nemal len teoretické poznatky týchto techológii, rozhodol som sa s nimi trocha experimentovať. Dal som si za cieľ uložiť dáta na IPFS, ktorá mi vráti hash kód ako unikátny prístupový kód k mojim dátam, ktorý ako potvrdenie vlastníctva tohto kódu, reps. potvrdenie vlastníctva dát uložených na IPFS uložím priamo na moju Stellar peňaženku alebo účet (account). Týmto procesom bude hash kód v mojom vlastníctve a len dáta prístupné skrz môj Stellar účet môžem považovať za dôveryhodné s tým, že každým updatom týchto dát, sa prepíše aj hash uložený na mojom Stellar účte.

Pre implementáciu som znova zvolil môj obľúbený ReactJS. Jeho používanie som už raz popisoval v tutoriáli pre Ethereum smart contracty, kde sme ich napájali na front-end pomocou knižnice Web3.

Začneme inštaláciou základného balíčka:

npm install -g create-react-app
Následne inštaláciou základného skeletonu appky:
create-react-app stellar-app
V tomto momente potrebujeme doinštalovať Stellar a IPFS knižnice pre komunikáciu s platformami:
npm i stellar-sdk

npm i ipfs-api

Náš súbor package.json by mal vyzerať asi takto:

{
  "name": “stellar-app",
  "version": "0.1.0",
  "dependencies": {
    "ipfs-api": "^22.0.0",
    "react": "^16.4.0",
    "react-dom": "^16.4.0",
    "react-form": "^3.5.5",
    "react-scripts": "1.1.4",
    "stellar-sdk": "^0.8.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}
Spustíme appku príkazom:
npm start

Ako ďalší krok, potrebujeme si vytvoriť nový účet na Stellar testnete. Niečo ako obdoba Ropstenu na Ethereu. Po vytvorení je vám na účet pripísaných testovacích 10 000 lumenov(XLM).

Poďme ale konkrétne na kód, ktorý celý ako vždy, nájdete na mojom githube. Začneme základným setnutím si konštánt:

//Definované spojenie s IPFS API
const ipfs = ipfsAPI({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' });
//Spojenie s testovacím blockchainom Stellaru
const serverUrl = 'https://horizon-testnet.stellar.org';// TESTNET
const server = new StellarSdk.Server(serverUrl);
//Vytvoríme si objekt account kde zadáme adresu svojho Stellar account a jeho private key (secret)
var account = {
    public:'GDJ2QE5DGI2P4LPVUHELCAHTM4LHC2RR7BAPMLMB4ZBO3ZXRXNA4VABG',
    secret:'--secret--'
};

V reálnej prevádzke by samozrejme, secret, nemohlo byť zadané takto do kódu v javascripte ale ideálne by sa načítalo z config súboru server environmentu alebo whatever už uznáte za bezpečné. Ideálný prípad je využiť “Metapay” čo je ekvivalent Metamasku pre Stellar.

Povedzme, že sme si pripravili inputy kde užívateľ zadáva dáta. Teraz potrebujeme tieto informácie pri potvrdení užívateľom spracovať, uložiť na IPFS a odpoveď z IPFS vo forme hashu odoslať na náš Stellar account kde sa uloží.

handleSubmit(event) {
	//Nechceme, aby sa nám submitom po každý raz stránka refreshla, to by nám bol react na prd
	event.preventDefault();

	//Buffer je NODEJS modul, ktorý umožňuje manipulovať priamo s binárkou dát
	const buffer = Buffer.from(this.state.value);

	//Pridáme spracované dáta do metódy .add ktorá nám vo fallback funkcii vráti práve požadovaný hash
	ipfs.files.add(buffer, (err, ipfsHash) => {

		//Hash odkaz na dáta uložené na IPFS si uložíme do objektu, ktorý je určený
		//ako vstup pre metódu dát priamo na Stellar účte
		var transactData = {name:'ipfs', value:ipfsHash[0].path};

		//Pripojíme sa k testnetu
		StellarSdk.Network.useTestNetwork();

		//Načítame si náš účet pomocou našej adresy
		server.loadAccount(account.public)
			.then(function(account) {

			//V tomto momente máme prístup k účtu a môžeme zavolať vytvorenie novej transakcie
			var transaction = new StellarSdk.TransactionBuilder(account)
			//Pridáme operáciu k transakcii
			//Túto operáciu bude predstavovať operácia manageData, ktorá nám spracuje objekt transactData
			//ktorý nesie so sebou práve náš hash z IPFS
			.addOperation(
				//Zakomentovaná časť je moja testovacia transakcia s lumenmi 
				/*
                                StellarSdk.Operation.payment({
                                destination: receiverPublicKey,
                                // The term native asset refers to lumens
                                asset: StellarSdk.Asset.native(),
                                // Specify 350.1234567 lumens. Lumens are divisible to seven digits past
                                // the decimal. They are represented in JS Stellar SDK in string format
                                // to avoid errors from the use of the JavaScript Number data structure.
                                amount: '0.00001',
                            }),
                            */
				StellarSdk.Operation.manageData(transactData)
			)
			//Pôvodný zámer bol ukladať IPFS hash do jednotlivých transakcií ako MEMO 
			//Nápad ale padol na tom že memo_text uloží string max o 28 znakoch
			//.addMemo(
			//StellarSdk.Memo.text(ipfsHash[0].path)
			//)
			.build();

			//Už nám stačí len podpísať našu transakciu a hotovo
			transaction.sign(sourceKeypair);

			//Klinet submitne našu operáciu 
			server.submitTransaction(transaction)
				.then(function(transactionResult) {
				console.log(JSON.stringify(transactionResult, null, 2));
				console.log('Success! View the transaction at: ');
				console.log(transactionResult._links.transaction.href);
			})
				.catch(function(err) {
				console.log('An error has occured:');
				console.log(err);
			});
		})
			.catch(function(e) {
			console.error(e);
		});

		//Overíme si či nám transakcia prešla
		this._confirmPayment(this.state.txMemo); 
	});

}

V tomto momente, keď sú data submitnuté v transakcii, potrebujeme informáciu o tom, že transakcia naozaj prešla a dáta môže potvrdiť aj pre IPFS aby ich pernamentne “pripol” na sieť.

//To že sme uploadli dáta IPFS a získali hash ešte nieje záruka toho že dáta budú 
//pernamentne zapísané. Práve metódou .add() hovoríme našej nóde aby po čase tieto dáta 
//nevymazala ako odpad, ako prevenciu pred ukladaním zbytočných dát. Dáta uploadnute 
//z nášho servera na IPFS sú uložené zatiaľ na našej nóde, dokiaľ si ich nestiahne
//iná nóda a tým ich rozšíri. Inak po vypnutí servera alebo tejto našej nódy ostatné 
//nódy stratia prístup k dátam a nestihnú si ich stiahnúť k sebe.
_pinIpfsListing(ipfsHash) {
	ipfs.pin.add(ipfsHash)
}

_confirmPayment(ipfsHash) {
	server.transactions().forAccount(account.public).cursor('now').stream({
		onmessage: (transaction) => {
			if(transaction.memo == ipfsHash) {
				// Yes, it made it on the blockchain!
				transaction.operations().then((ops) => {
					var payment = ops._embedded.records[0];
					if(parseInt(parseFloat(payment.amount)) < 1) {
						console.error('Payment insufficient. Post not saved!');
					} else {
						this._pinIpfsListing(ipfsHash);
					}
				}).catch((error) => {
					error.target.close(); // Close stream
					console.error('Payment Error: ', error);
					alert('Error confirming payment. Try again later');
				});
			}
		},
		onerror: (error) => {
			error.target.close(); // Close stream
			console.error('Streaming Error: ', error);
		}
	});
}

Máme dáta uložené na IPFS, hash máme uložený na svojom účte, ako ale tieto dáta získame späť a zobrazíme ich na webe?

//Keďže Stellar blockchain dátu sú transparentné spolu aj so všetkými účtami, 
//vieme si tieto dáta zobraziť pomocou jednoduchého zavolania API, kde nám stačí 
//len server url, naša adresa a key ktorým sme pomenovali dáta pri vyskladávaní
//transakcie pri submite dát.
_getData(act, key) {
	let xxx = this;
	var url = serverUrl + '/accounts/' + act.public + '/data/' + key;
	this._webget(url, function(json){
		if(json.status==404){ console.log('Error getting data for key '+key); }
		else {
			console.log(key+' : '+atob(json.value)); /* atob() base64 */
			xxx._getIPFS(atob(json.value));
		}
	});
}

// Simple XmlHttpRequest
_webget(url, callback) {
	var http = new XMLHttpRequest();
	http.open("GET", url, true);
	http.onreadystatechange = function() {
		if(http.readyState==4) {
			console.log('Response: '+http.responseText);
			var json = null;
			try {
				json = JSON.parse(http.responseText);
			} catch(ex) {
				console.log("JSON ERROR", ex.message);
				json = { error: true, message: ex.message };
			}
			callback(json);
		}
	};
	http.send();
}

Keďže nám API vrátila náš IPFS hash, v tomto momente si pomocou neho dákažeme získať dáta z IPFS a zobraziť ich na front-ende.


//Ako vstupný parameter potrebujeme náš IPFS hash
_getIPFS(getIpfsListing) {
	let xxx = this;
	ipfs.files.get(getIpfsListing, function (err, files) {
		files.forEach((file) => {
			//Vrátené dáta si potrebujeme convertovať na string
			const post = file.content.toString('utf8');
			//Setneme si ich ako state, ktorý si už môžeme v kľude 
			//vypísať na front-end alebo spracovávať podľa potreby. 
			xxx.setState({post:post})
		})
	})
}

Na front-ende to môže vyzerať aj takto:

Snímka obrazovky 2018-05-31 o 21.12.36

Pokiaľ disponujete dostatkom fantázie, určite teraz primýšľate čo všetko sa dá takto vytvoriť a čomu všetkému to môže byť prospešné.

Link na môj Github odkiaľ si môžeš stiahnúť zdrojáky.

Happy coding! Happy hacking!

Ak ťa táto téma zaujala, určite pozri moje ďalšie tutoriály! –> SEM

#1 Ethereum DApp Tutorial: Intro do Smart Contractov

 

0