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:
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
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
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
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.