Author

Topic: Kako Se Generiraju Bitcoin Adrese? Matematika Na Kojoj Se Bitcoin Adrese Temelje (Read 156 times)

member
Activity: 112
Merit: 24
Ovo je bilo prvo sto sam napravio kad sam procitao Mastering Bitcoin.  Bravo za trud sto pokusavas prosiriti razumijevanje!
legendary
Activity: 2730
Merit: 7065
TESTIRANJE

Code:
console.log(`This is Bitcoin Address: ${publicAddress}
This is WIF Key: ${WIF}
This is Uncompressed Public Key: ${uncompressedKey}
This is compressed Public Key: ${compressedKey}
Thisi is hexadecimal of Private Key: ${privateKey}`);

Nakon upisivanja koda u datoteku, spremite tu datoteku i pokrenite terminal. U terminalu upišite sljedeću naredbu. Provjerite da li je otvoren folder u terminalu:

node index.js (naziv datoteke može biti drugčiji kod vas)

U terminalu ćete dobiti adresu, WIF, javni ključ, privatni ključ i oni će odgovarati ključevima generiranim od strane bitaddress. Možete probati sa drugim privatnim ključem. U 3. koraku promijenite privatni ključ i to je to.

But let's not forget the fact that this thread is still for learning purpose. Always use the keys generated by wallets like Electrum and MyCelium, unless you are completely sure what are you doing.

Ali nemojmo zaboraviti činjenicu da je ova tema ipak napravljena u svrhu učenja. Uvijek koristite ključeve generirane u novčanicima kao što su Electrum ili MyCelium, osim ako niste potpuno sigurni u to što radite.

Za kompletan kod, posjetite: https://webtricks.website/keyGeneration.html

Da biste vidjeli kako se kod izvšava, posjetite: https://key-generation-by-webby.herokuapp.com/


Ovo je prijevod vrlo korisne teme koju je korisnik webtricks napisao. Temu na engleskom jeziku možete pročitati ovdje:
https://bitcointalksearch.org/topic/how-bitcoin-addresses-are-generated-understand-the-math-behind-bitcoin-5223167

legendary
Activity: 2730
Merit: 7065
4. Korak: Kreiranje Komprimiranog I Nekomprimiranog Javnog Ključa

Code:
const checkKey = key => key.length < 64 ? '0'.repeat(64 - key.length) : key;

const publicKeyX = checkKey(publicKey[0].toString(16));
const publicKeyY = checkKey(publicKey[1].toString(16));

const uncompressedKey = '04'+publicKeyX+publicKeyY;

let compressedKey;
if (publicKey[1]%BigInt(2)===BigInt(1)) {
  compressedKey = '03'+publicKeyX;
} else {
  compressedKey = '02'+publicKeyX;
}

Bingo! Došli smo do prvog cilja. Napravili smo nekomprimirani i komprimirani javni ključ. U kodu iznad smo prije svega napravili checkKey funkciju. Ova funkcija radi jednu zanimljivu stvar. Može se dogoditi da prilikom pretvaranja X i Y koordinata iz brojeva u hexadecimalnu vrijednost rezultirajuća dužina X i Y ne iznosi 64. Kako smo prethodno spomenuli, dužina nekomprimiranog ključa je 130, a prva dva znaka su 04, zatim 64 znaka od X, i slijede 64 znaka od Y. Dakle, da bi popunili prazninu, dodajemo nule ako je vrijednost manja od 64. Na primjer, ako je dužina X 63 znaka, dodaćemo jednu 0 kako bi ona dobila 64 znaka.

Zatim smo definirali hexadecimalnu vrijednost X koordinate kao publicKeyX, a Y kao publicKeyY. Možda ste primjetili da koristimo toString(16) u drugom i trećem redu. Ovaj kod pretvara broj u hex a potom wrapper (format datoteke) od checkkey-a provjerava da li je vrijednost manja od 64, i dodaje 0 ako treba, ako ne onda vraća isti ključ.

Nakon toga smo nekomprimirani ključ definirali kao uncompressedKey, a komprimirani ključ kao 03+X ako je Y neparan, a 02+X ako je Y paran.


5. Korak: Generiranje P2PKH Ključa

Prije nego što pređemo na sami kod, spomenimo proces generiranja P2PKH ključa. Treba primijetiti da nekomprimirani i komprimirani ključevi koje smo generirali u 4. koraku nisu specifični samo za Bitcoin. Postoji nekoliko drugih kompanija, poput Gmaila ili Facebooka, koje koriste Elliptic Curve kriptografiju za kreiranje javnih/privatnih ključeva. Međutim, u ovom postupku ćemo pretvoriti naš javni ključ u odgovarajući format za Bitcoin, tj. P2PKH. Slijedi slikoviti prikaz postupka.



Počinjemo sa nekomprimiranim ključem generiranim u koraku 4. (možemo početi i sa komprimiranim ključem koji će stvoriti različitu P2PKH adresu, ali može se koristiti naizmjenično i pripada istom privatnom ključu). Zatim izvršimo sha256 na nekomprimiranom ključu. Postlje toga slijedi ripemd160 hashiranje. Zatim dodamo 00 ispred prethodnog hash-a. To je naša 21-bajtna binarna adresa. Da bismo generirali sljedeća 4 bajta od binarne adrese moramo izvršiti dvostruko sha256 hashiranje na prva 21. bajta. Zatim uzimamo prva 4 bajta rezultirajućeg hash-a, tj. prvih osam znakova rezultirajućeg hash-a i dodajemo ih na kraju 21 bajta. Napokon dobivamo 25-bajtnu Binarnu adresu koju moramo pretvoriti u Base58. A sada ćemo pogledati završni kod.
 
Code:
const keyHex = Buffer.from(uncompressedKey, 'hex');
const ripedHashedKey = ripemd160(sha256(keyHex));
const mainRipeKeyString = '00'+ripedHashedKey.toString('hex');
const mainRipeKey = Buffer.from(mainRipeKeyString, 'hex');
const doubleHashedKey = sha256(sha256(mainRipeKey)).toString('hex');
const checkSum = doubleHashedKey.substr(0, 8);
const binaryAddress = Buffer.from(mainRipeKeyString+checkSum, 'hex');
const publicAddress = bs58(binaryAddress);

Prethodni kod nije ništa drugo nego istih 8 koraka koje sam detaljno opisao na slici iznad. Uspjeli smo! Uspješno smo generirali Bitcoin adresu. Pređimo sada na posljednji korak gdje ćemo generirati naš WIF privatni ključ iz hexadecimale privatnog ključa.

6. Korak: Generiranje WIF-A Iz Privatnog Ključa

Slično kao i u prethodnom pristupu, objasniću prvo postupak prije nego što zapravo pređem na sami  kod. Generiranje WIF-a iz privatnog ključa je zapravo jednostavnije od prethodnog koraka. Pretvaranje hexa privatnog ključa u WIF zapravo donosi mnoge koristi. Prvo je to što je manji i jednostavniji od običnog hexa. Drugo, ima ugrađen kontrolni zbroj koji provjera da li je privatni ključ validan ili nije. Pogledajmo najprije slikoviti prikaz toga:



Prvi korak je jednostavan, na početku hexadecimalnog formata privatnog ključa dodajemo 80. Imajte na umu da svi ti dodaci koje unosimo u kod zapravo nisu brojevi. Oni su hex kodovi. Na primjer, kada se 80 pretvori u decimalni oblik dobivamo 128. Zatim izvršimo dvostruku rundu sha256 hashiranja. Zatim uzmemo prva 4 bajta rezultirajućeg hexa i dodamo ih na kraju produženog hexa privatnog ključa. Na kraju izvršimo Base58 hashiranje dobivenog rezultata i dobivamo WIF ključ. Vrijeme je za kod:

Code:
const pvtKeyExtended = "80"+privateKey;
const extended = Buffer.from(pvtKeyExtended, 'hex');
const hashedExtended = sha256(sha256(extended)).toString('hex');
const checksum = hashedExtended.substr(0, 8);
const finalHex = pvtKeyExtended+checksum;
const WIF = bs58(Buffer.from(finalHex, 'hex'));

Nema ništa posebno u kodu ni ovaj put. Jednostavno izvršavamo svih šest koraka kako je spomenuto na prethodnoj slici.

Odlično! Napokon smo dovršili cijeli postupak i generirali svoju Bitcoin adresu zajedno sa WIF ključem. Sjajno. A sada trebamo testirati kod.
legendary
Activity: 2730
Merit: 7065
Ovo je prijevod vrlo korisne teme koju je korisnik webtricks napisao. Temu na engleskom jeziku možete pročitati ovdje:
https://bitcointalksearch.org/topic/how-bitcoin-addresses-are-generated-understand-the-math-behind-bitcoin-5223167


Kako Se Generiraju Bitcoin Adrese


Ova teme se odnosi samo na P2PKH adrese, tj. Bitcoin adrese koje počinju sa '1', poznatijim kao Legacy Adrese. U budućnosti ću napisati još jednu temu o tome kako nastaju P2SH ili Bech32 adrese.

U redu! Krenimo od početka. Za pravilno korištenje Bitcoina korisniku trebaju dva ključa: Privatni i Javni Ključ. Javni Ključ je ključ koji se dijeli sa klijentima i sa ostatkom svijeta. Npr: 18J6ai34uzGofUuzbiwhXJzKzdx1efDBqA. Dok je Privatni Ključ, ključ koji se koristi za pristupanje sredstvima koja se nalaze na Javnom Ključu.

Možda već znate ovo sam upravo rekao. Ali jeste li se ikada zapitali kako nastaje ovaj par ključeva? Proanalizirat ćemo dublje ovu temu i napraviti vlastiti kod za generiranje ključeva. Glavni i najvažniji dio Bitcoin adrese je privatni ključ. Pričaćemo prvo o tome:



Privatni Ključ

Jednostavnim riječima, sve može biti privatni ključ ako ispunjava dva uvjeta. Prvi uvjet: ne smije biti 0. Drugi uvjet: vrijednost mora biti niža od vrijednosti N, definirane od strane SECG za secp256k1 curve. Međutim, vrijednost N je veoma velika, i praktički svaki 256-bitni broj može biti validan privatni ključ.

Sada se postavlja pitanje kako nastaje privatni ključ. Kao što sam rekao na početku, sve može biti privatni ključ. Na primjer, ovaj niz: "Ja sam niz za generiranje privatnog ključa" može biti pretvoren u privatni ključ. Sve što trebate učiniti je pretvoriti ovaj niz u 256-bitnu vrijednost, i provjeriti je li vrijednost niža od N.

Međutim, da li je preporučljivo stvarati privatne ključeve na ovaj način? Nije! Popularno je reći da je čovjek najgori kreator nasumično odabranih fraza. Ako koristimo uobičajene nizove ili brojeve poput ovoga gore, moguće je da neko drugi iskoristi potpuno isti niz što bi moglo dovesti do kompromitiranja privatnog ključa. Dakle, bolje je spriječiti nego liječiti i treba se oslanjati samo na one generatore koji će generirati privatni ključ slučajnim odabirom.

Ali javlja se jedan drugi problem. Većina generatora poput Math library Javascripta (Math.random() function) koristi fiksne sheme za generiranje slučajno odabranih brojeva. Dakle, korištenje takvih generatora napravit će više problema nego ispravno generiranih ključeva.

Šta je onda najbolje rješenje? Najbolje je koristiti ključeva generirane u novčanicima, ali ako želite da to samostalno radite, koristite sigurne generatore poput ovog:  strong pseudorandom generator.



Dosta priče o privatnim ključevima, hajmo na bitaddress.org da napravimo adresu. Prvo ćemo generirati adresu na bitaddress.org, a zatim ćemo pokušati kreirati tu istu adresu pomoću vlastitog koda kako bismo razumjeli matematiku koja se krije iza procesa generanja ključeva.

U nastavku se nalazi generirani par ključeva broj 1. Možda ćete primjetiti da postoji više oblika i za javni i privatni ključ. Objasnit ću vam ukratko o tome prije nego što pređem na dio o samom kodu:


1. Javna Adresa

Ovo je P2PKH varijanta javnog ključa. Često upotrebljavan format za slanje/primanje bitcoina. Koriste se višestruki krugovi različitih hash algoritama za kreiranje P2PKH ključa od hex-a javnog ključa. To ćemo kasnije pokušati napraviti u ovoj temi sa odgovarajučim kodom.

2. WIF Privatni Ključ

WIF ili Wallet Import Format (format korišten za unos u novčanik) je format privatnog ključa kojeg novčanici kao što je Electrum koriste za unos (import) privatnog ključa. Ako unesete samo hex format privatnog ključa, Electrum neće otvoriti odgovarajući wallet. Morate pretvoriti privatni ključ u WIF format da biste ga mogli koristiti u novčanicima. Napisat ćemo i kod za pretvaranje privatnog ključa u WIF.

3. Nekomprimirani Javni Ključ

OK! Još uvijek nisam objasnio kako se generira javni ključ. Cijeli proces je zapravo vrlo složen. Uzimamo posebnu tačku koju SECG naziva G i koja se nalazi na secp256k1 curve, tj. Jednoj od elliptic curve. Zatim tu tačku pomnožimo sa privatnim ključem. Rezultat toga nam daje dvije koordinate, jedna je X, a druga Y. Nekomprimirani Javni Ključ nije ništa drugo nego: 04 + X + Y. Dakle, prva dva broja od javnog ključa su 04, što znači da je ključ nekomprimiran. Naredna 64 znaka (32 bajta, budući da svaka 2 znaka hexa čine 1 bajt) su X koordinata, a posljednja 64 znaka (32 bajta) su Y koordinata. Ukupna dužina nekomprimiranog javnog ključa je 130 ili 65 bajta.

4. Komprimirani Javni Ključ

Ako je X koordinata poznata, moguće je pronaći Y koordinatu. Zbog toga obično izbacujemo Y koordinatu iz našeg javnog ključa. Dakle, otklanjaju se posljednja 64 znaka. Kao rezultat toga, komprimirani javni ključ se sastoji od 66 znakova (32 bajta). Prva dva znaka mogu biti 02 ili 03 (umjesto 04, što je slučaj kod Nekomprimiranog Javnog Ključa), a sljedeća 64 znaka (32 bajta) su X koordinata. Ako je vrijednost Y koordinate paran broj, onda se koristi 02. Ako je vrijednost Y koordinate neparan, tada se stavlja 03. Na fotografiji iznad, vrijednost Y koordinate je bila neparna, pa u svom ključu koristimo 03.

5. Hexadecimalni Format Privatnog Ključa

Kao što je ranije spomenuto, privatni ključ mora biti 256-bitni ili 32-bajtni (8 bita = 1 bajt), što kada se pretvori u Hexadecimalni format iznosi 64 znaka. Dakle, bilo koja vrijednost se može pretvoriti u hex i imat će 64 znaka. Ovo je vrlo korisno za naš bitcoin kod jer ćemo koristiti hex format privatnog ključa za generiranje odgovarajućih parova ključeva. Kako sam na početku spomenuo, možemo čak koristiti i fraze poput "Ja sam niz za generiranje privatnog ključa" za generiranje privatnog ključa, a evo u čemu je tajna. Takav niz ćemo prvo pretvoriti u hex, a zatim ćemo koristiti 64 znaka hex-a za kreiranje odgovarajućih ključnih parova.

6. Privatni Ključ 64 U Base64 Obliku

Nije posebno popularan format privatnih ključeva. Ali možemo čak i kodirati/dekodirati naš privatni ključ u Base64 koristeći izvorni oblik pretvaranja.

Dovoljno za sada. Sada ćemo se pozabaviti kodom i generiranjem gore nevedenog ključa.



Budući da sam ljubitelj Javascripta (jer mislim da je to najlakši programski jezik i može se koristiti u full-stack developmentu), za ovaj vodič ću koristit JS u Node.JS okruženju. Ali ako vi radite sa drugim programskim jezikom, možete jednostavno prilagoditi moj JS kod svom kodu. Nije bitno i ukoliko uopće nemate iskustva sa kodiranjem, samo pročitajte tekst i pogledajte slike u nastavku i obećavam da ćete razumjeti kako se kreiraju ključevi.

Prije nego što počnemo trebamo pripremiti instalaciju. Prvi korak je kreiranje novog foldera. Unutar foldera napravite jednu datoteku sa .js ekstenzijom. Datoteku možete nazvati kako hoćete, npr: index.js ili app.js.
Sljedeći korak je preuzimanje node.js na vaš računar. Preuzimanje node.js je jednostavno, slično je preuzimanju bilo kojeg drugog kompjuterskog softvera. Naredni korak je preuzimanje jednog od uređivača kodova, predlažem Visual Studio Code (jednostavno korištenje IDE).

Nakon što ste izvršili sve gore navedene korake, otvorite folder u Visual Studio Code i pokrenite svoj terminal. U Visual Studio Code-u postoji terminal, možete njega koristiti. Ako ne želite, možete koristiti izvorni terminal vašeg Mac ili Windows računara, ali provjerite da ste otvorili folder u terminalu. Nakon što je folder otvoren i u Visual Studio Code-u i u terminalu, unesite sljedeću naredbu u terminal da biste instalirali 2 dodatka za projekat:

Code:
npm init -y
npm i ripemd160 --save
npm i bs58 --save

Izuzev elliptic curve kriptografije, u kodu su nam potrebne tri hashing funkcije, a to su sha256, ripemd160 i base58. sha256 je već prisutna u izvornoj programskoj biblioteci nodejs. Ostalo možemo sami kodirati ili jednostavno unjeti već gotove. Zbog jednostavnosti, prethodno smo instalirali ripemd160 i bs58 npm pakete i njih ćemo koristiti u kodu. Proverio sam izvorni kod oba paketa i potpuno ih je sigurno koristiti za kodiranje.

Sada počinje prava zabava. Pokrenite datoteku i počnite od koda. Kod je kronološki poredan. Kod iz 1. Koraka će biti u vrhu datoteke, a kod iz 2. Koraka nastavit će tamo gdje se završava kod 1. Koraka itd:

1. Korak: Stvaranje hashing funkcija

Code:
const crypto = require('crypto');
const RIPEMD160 = require('ripemd160');
const BS58 = require('bs58');

const sha256 = input => crypto.createHash('sha256').update(input).digest();

const ripemd160 = input => new RIPEMD160().update(input).digest();

const bs58 = input => BS58.encode(input);

Dakle, u prva tri reda koda, unjeli smo kodove za sve tri hashing funkcije za našu novu datoteku. Zatim smo njima dali zadatke. Nije obvezno kreirati te zadatke, ali u tom slučaju moramo konstantno ponavljati i pisati čitav kod kad god želimo nešto hashirati. Na primjer, ako ne napišemo ove tri funkcije, svaki put kada trebamo uraditi sha256 hash nečega moramo napisati: crypto.createHash('sha256').update(something).digest(), ali sa gore navedenim kodom, samo trebamo napisati sha256(something) sljedeći put. Cool? ? Idemo dalje.

2. Korak: Kreiranje Elliptic Curve Funkcije

Code:
const generateECPoints = privateKey => {

    const Pcurve = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F');

    const Gx = BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240');
    const Gy = BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424');

    const G = [Gx, Gy];

    const modInverse = (a, n) => {

        a = (a % n + n) % n

        const dArray = [];
        let b = n;

        while(b) {
        [a, b] = [b, a % b];
        dArray.push({a, b});
        }

        if (a !== BigInt(1)) {
        return null;
        }

        let x = BigInt(1);
        let y = BigInt(0);

        for(let i = dArray.length - 2; i >= 0; --i) {
        [x, y] = [y,  x - y * BigInt(dArray[i].a / dArray[i].b)];
        }

        return (y % n + n) % n;
    }

    const modOf = (a,b) => {
        const r = ((a % b) + b)% b;
        return r;
    }

    const ECAdd = (a,b) => {
        const lamAdd = modOf((b[1] - a[1]) * BigInt(modInverse(b[0] - a[0], Pcurve)), Pcurve);
        const x = modOf((lamAdd*lamAdd - a[0] - b[0]), Pcurve);
        const y = modOf((lamAdd*(a[0] - x) - a[1]), Pcurve);
        return [x, y];
    }

    const ECDouble = a => {
        const lamda = modOf(((BigInt(3)*a[0]*a[0])*(modInverse(BigInt(2)*a[1], Pcurve))), Pcurve);
        const x = modOf((lamda*lamda - BigInt(2)*a[0]), Pcurve);
        const y = modOf((lamda*(a[0] - x) - a[1]), Pcurve);
        return [x, y];
    };

    const ECMultiply = (genPoint, pvtKey) => {
        const scalarBinary = BigInt('0x'+pvtKey).toString(2);
        let GP = genPoint;

        for (let i=1; i < scalarBinary.length; i++) {
            GP = ECDouble(GP)
            if (scalarBinary[i] === '1') {
                GP = ECAdd(GP, genPoint);
            }
        }
        return GP;
    }
   
    return ECMultiply(G, privateKey);
}

Kod kojeg ste upravo vidjeli je moja verzija Elliptic Curve Umnožavanja. Ovo je možda jedino čisto Javascript elliptic curve kodiranje koje možete pronaći na cijelom internetu. Mislim da bi bilo neprikladno da objašnjavam cijeli kod u ovoj temi, jer je glavni cilj ove teme generisanje parova ključeva. Dakle, za sada koristite ovaj gore kod takav kakav jest. Napisat ću zasebnu temu u vezi Elliptic Curve Kriptografije za 3-4 dana i objasniti ovaj isti kod u toj temi. 

3. Korak: Stvaranje X i Y koordinata Javnog Ključa od prethodno navedene kodne funkcije i Privatnog Ključa

Code:
const privateKey = "6EBD5FAB742ED0734B37C63BD2A3CE8797FE4AC63C9A99781F8BEDDF6307094E";
const publicKey = generateECPoints(privateKey);

U ovom koraku smo uzeli hex vrijednost privatnog ključa (5. red sa slike) i stavili ga u generateECPoints funkciju koju smo napravili u 2 koraku. To će nam dati X i Y koordinate Javnog Ključa koje će izgledati ovako:
[26552980488606060638326679080566574626825610331305555186819497546906082384636n, 106820354128014061768597493909158935631153585355120117243602895828064095418195n]

Možete primijetiti n na kraju svake koordinate. Taj znak n znači da se ovdje radi o ekstra velikim brojevima koji su u Javascriptu poznati kao Veliki Integri. Možda ste također primijetiti da te koordinate ne odgovaraju X-u i Y-u prethodno postavljene slike. Zapravo, sada smo generirali brojeve. Sada te brojeve trebamo pretvoriti u hexadecimale da bismo dobili nekomprimirani i komprimirani ključ. Učinit ćemo to u sljedećem koraku.
Jump to: