Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
September 04, 2024, 04:36:17 PM |
|
Blockchain Parser ini lebih kurangnya ditujukan agar bisa membaca detail dari database Bitcoin blockchain. Jika agan menginstall Bitcoin Core, maka pada folder tempat menyimpan data dari Bitcoin blockchain, disana ada folder blocks yang berisi kumpulan file blkxxxxx.dat Nah, berikut ini saya coba test parsing file blkxxxxx.dat dari database Bitcoin blockchain. Script yang saya gunakan ( https://github.com/ragestack/blockchain-parser/blob/master/blockchain-parser.py): # -*- coding: utf-8 -*- # # Blockchain parser # Copyright (c) 2015-2023 Denis Leonov <466611@gmail.com> #
import os import datetime import hashlib
def reverse(input): L = len(input) if (L % 2) != 0: return None else: Res = '' L = L // 2 for i in range(L): T = input[i*2] + input[i*2+1] Res = T + Res T = '' return (Res);
def merkle_root(lst): # https://gist.github.com/anonymous/7eb080a67398f648c1709e41890f8c44 sha256d = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest() hash_pair = lambda x, y: sha256d(x[::-1] + y[::-1])[::-1] if len(lst) == 1: return lst[0] if len(lst) % 2 == 1: lst.append(lst[-1]) return merkle_root([hash_pair(x,y) for x, y in zip(*[iter(lst)]*2)])
def read_bytes(file,n,byte_order = 'L'): data = file.read(n) if byte_order == 'L': data = data[::-1] data = data.hex().upper() return data
def read_varint(file): b = file.read(1) bInt = int(b.hex(),16) c = 0 data = '' if bInt < 253: c = 1 data = b.hex().upper() if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = file.read(1) b = b.hex().upper() data = b + data return data
dirA = './blocks/' # Directory where blk*.dat files are stored #dirA = sys.argv[1] dirB = './result/' # Directory where to save parsing results #dirA = sys.argv[2]
fList = os.listdir(dirA) fList = [x for x in fList if (x.endswith('.dat') and x.startswith('blk'))] fList.sort()
for i in fList: nameSrc = i nameRes = nameSrc.replace('.dat','.txt') resList = [] a = 0 t = dirA + nameSrc resList.append('Start ' + t + ' in ' + str(datetime.datetime.now())) print ('Start ' + t + ' in ' + str(datetime.datetime.now())) f = open(t,'rb') tmpHex = '' fSize = os.path.getsize(t) while f.tell() != fSize: while tmpHex != 'D9B4BEF9': # it is for to skip zeroes in some blk files tmpHex = read_bytes(f,4) resList.append('Magic number = ' + tmpHex) tmpHex = read_bytes(f,4) resList.append('Block size = ' + tmpHex) tmpPos3 = f.tell() tmpHex = read_bytes(f,80,'B') tmpHex = bytes.fromhex(tmpHex) tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = tmpHex[::-1] tmpHex = tmpHex.hex().upper() resList.append('SHA256 hash of the current block hash = ' + tmpHex) f.seek(tmpPos3,0) tmpHex = read_bytes(f,4) resList.append('Version number = ' + tmpHex) tmpHex = read_bytes(f,32) resList.append('SHA256 hash of the previous block hash = ' + tmpHex) tmpHex = read_bytes(f,32) resList.append('MerkleRoot hash = ' + tmpHex) MerkleRoot = tmpHex tmpHex = read_bytes(f,4) resList.append('Time stamp = ' + tmpHex) tmpHex = read_bytes(f,4) resList.append('Difficulty = ' + tmpHex) tmpHex = read_bytes(f,4) resList.append('Random number = ' + tmpHex) tmpHex = read_varint(f) txCount = int(tmpHex,16) resList.append('Transactions count = ' + str(txCount)) resList.append('') tmpHex = ''; RawTX = ''; tx_hashes = [] for k in range(txCount): tmpHex = read_bytes(f,4) resList.append('TX version number = ' + tmpHex) RawTX = reverse(tmpHex) tmpHex = '' Witness = False b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) if bInt == 0: tmpB = '' f.seek(1,1) c = 0 c = f.read(1) bInt = int(c.hex(),16) tmpB = c.hex().upper() Witness = True c = 0 if bInt < 253: c = 1 tmpHex = hex(bInt)[2:].upper().zfill(2) tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex inCount = int(tmpHex,16) resList.append('Inputs count = ' + tmpHex) tmpHex = tmpHex + tmpB RawTX = RawTX + reverse(tmpHex) for m in range(inCount): tmpHex = read_bytes(f,32) resList.append('TX from hash = ' + tmpHex) RawTX = RawTX + reverse(tmpHex) tmpHex = read_bytes(f,4) resList.append('N output = ' + tmpHex) RawTX = RawTX + reverse(tmpHex) tmpHex = '' b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) c = 0 if bInt < 253: c = 1 tmpHex = b.hex().upper() tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex scriptLength = int(tmpHex,16) tmpHex = tmpHex + tmpB RawTX = RawTX + reverse(tmpHex) tmpHex = read_bytes(f,scriptLength,'B') resList.append('Input script = ' + tmpHex) RawTX = RawTX + tmpHex tmpHex = read_bytes(f,4,'B') resList.append('Sequence number = ' + tmpHex) RawTX = RawTX + tmpHex tmpHex = '' b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) c = 0 if bInt < 253: c = 1 tmpHex = b.hex().upper() tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex outputCount = int(tmpHex,16) tmpHex = tmpHex + tmpB resList.append('Outputs count = ' + str(outputCount)) RawTX = RawTX + reverse(tmpHex) for m in range(outputCount): tmpHex = read_bytes(f,8) Value = tmpHex RawTX = RawTX + reverse(tmpHex) tmpHex = '' b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) c = 0 if bInt < 253: c = 1 tmpHex = b.hex().upper() tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex scriptLength = int(tmpHex,16) tmpHex = tmpHex + tmpB RawTX = RawTX + reverse(tmpHex) tmpHex = read_bytes(f,scriptLength,'B') resList.append('Value = ' + Value) resList.append('Output script = ' + tmpHex) RawTX = RawTX + tmpHex tmpHex = '' if Witness == True: for m in range(inCount): tmpHex = read_varint(f) WitnessLength = int(tmpHex,16) for j in range(WitnessLength): tmpHex = read_varint(f) WitnessItemLength = int(tmpHex,16) tmpHex = read_bytes(f,WitnessItemLength) resList.append('Witness ' + str(m) + ' ' + str(j) + ' ' + str(WitnessItemLength) + ' ' + tmpHex) tmpHex = '' Witness = False tmpHex = read_bytes(f,4) resList.append('Lock time = ' + tmpHex) RawTX = RawTX + reverse(tmpHex) tmpHex = RawTX tmpHex = bytes.fromhex(tmpHex) tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = tmpHex[::-1] tmpHex = tmpHex.hex().upper() resList.append('TX hash = ' + tmpHex) tx_hashes.append(tmpHex) resList.append(''); tmpHex = ''; RawTX = '' a += 1 tx_hashes = [bytes.fromhex(h) for h in tx_hashes] tmpHex = merkle_root(tx_hashes).hex().upper() if tmpHex != MerkleRoot: print ('Merkle roots does not match! >',MerkleRoot,tmpHex) f.close() f = open(dirB + nameRes,'w') for j in resList: f.write(j + '\n') f.close()
Berikut ini tahapan prosesnya: Buat folder Folder-1: Directory dimana file blkxxxxx.dat tersimpan (folder ini tidak perlu dibuat, jika agan mau langsung menggunakan data dari folder Bitcoin Core; Tentunya proses parsing membutuhkan waktu cukup lama tergantung seberapa banyak filenya .dat nya)Folder-2: Directory untuk tempat menyimpan file hasil parsing Pada test kali ini, saya meg-copy hanya dua file blkxxxxx.dat dan menyimpannya di folder-1 (blocks); Sementara itu, saya menyimpan hasil parsing di folder-2 (blocks_parsing).   Buka script blockchain-parser.py diatas (saya menggunakan IDLE-Python) Sesuaikan direktori folder-1 di dirA dan folder-2 di dirB. Berikut ini contoh milik saya:  Pada file menu, klik Run -> Run Module (F5) Tunggu prosesnya hingga selesai, berikut ini contohnya:  Seperti nampak di atas, proses parsing antara blk00000.dat dan blk00001.dat pada komputer yang saya gunakan membutuhkan waktu sekitar 2 menit. Berikut ini hasilnya:    file blkxxxxx.dat sudah di parsing menjadi blkxxxxx.txt (bisa dibuka di TextEdit, Notepad, dan aplikasi semisalnya). Untuk file hasil parsing tersebut saya upload di drive: - blk00000.txt ( https://drive.google.com/file/d/1JPzi1HztKXCJx8CLIbOfsnw3VfnNkhue/view?usp=sharing) - blk00001.txt ( https://drive.google.com/file/d/1S7e_Yrc68ss3N8Ecms-jj2zB8cn31KVA/view?usp=sharing) Mudah-mudahan bisa bermanfaat.
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
|
Patikno
|
 |
September 05, 2024, 12:27:55 AM |
|
Kalau kita melakukan parsing dan mendapatkan hasilnya dalam bentuk .txt berarti kita bisa melihat keseluruhan detail kode dari blok genesis hingga blok sekarang begitu ya? dan proses-proses yang ada di dalam .txt berhubungan dengan thread [Edukasi] Proses Transaksi & Block di Jaringan Blockchain Bitcoin yang dibuat @punk.zink sebelumnya? Saya mau coba download .txt yang sudah sampean buat, tapi setelah saya cek pada gambar folder blocks_parsing, total besaran data dari kedua .txt tersebut 58GB, terlalu besar. Selain untuk blockchain Bitcoin, ternyata @Denis Leonov juga mengatakan di Github kalau cara ini juga bisa bekerja di kebanyakan blockchain altcoins, tapi dia tidak mengatakan di blockchain altcoins mana saja.
|
|
|
|
|
|
██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ | ██████████████████████████████████████████████████████████████ ████▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄████████▄▄▄▄▄▄▄███▄▄▄▄▄▄▄▄▄ ███▄██▀▀▀▀▀▀▀▀▀▀▀██▄▄▄▄▄▄▄▄███████▄▄▄██▀▀▀▀▀██▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ █████▄▄▄▄▄▄▄▄▄▄████▀▀▀▀███▀▀▄▄██▀███▀▀▀███████▀▀▀█▀▀▀▀▀▀▀▀▀▀██ ███▀██████▀▀▀▀███████████████████▀██████████▀██▄██▄▄▄▄▄███▄███ ███▄█████▄▄▄▄▄▄▄███████████████▄█████████▀▀██▄████████▀█▄████▀ ██▄█████████████████████████████▄███████████████████▀█▄████▀ █▄████████▀▀▀█████████████████████████████████████▀█▄██████ ▄████████▀██████████████████████████████████████▀███▀▀▀▀▀██▄ ███▄████▀████▀███████████████████████████▀██████████████▄███ ▀████▀▀▀██████▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█▀▀▀▀█████████████▀ █▀▀▀▀█████████████████████████████████████████▀▀▀▀▀▀▀▀▀▀▀▀▀ ██████████████████████████████████████████████████████████████ | ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ | ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ | | | | . SPORTSBOOK[NEW] | ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ | ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ | 100% WELCOME BONUS │ NO KYC │ UP TO 15% CASHBACK | | | [PLAY NOW] | ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ |
|
|
|
Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
September 05, 2024, 01:31:53 AM |
|
Kalau kita melakukan parsing dan mendapatkan hasilnya dalam bentuk .txt berarti kita bisa melihat keseluruhan detail kode dari blok genesis hingga blok sekarang begitu ya?
Lebih kurang demikian; Dan sepemahaman saya mengenai seberapa detail informasi yang didapat tergantung dari script-nya juga. Contoh salah satu block yang saya parsing di atas adalah genesis block (bisa dilihat previous block hash-nya 0).
Bisa dilihat pada contoh screenshot isi dari file hasil parsing di atas, detailnya lebih kurang berisi informasi transaksi, dll. pada block tersebut, yang artinya masih ada kaitannya juga dengan materi-materi yang di buat mas zaki@punk tersebut.
Saya mau coba download .txt yang sudah sampean buat, tapi setelah saya cek pada gambar folder blocks_parsing, total besaran data dari kedua .txt tersebut 58GB, terlalu besar.
Tidak sampai sebesar itu jika merujuk file yang ada di PC saya:  btw, agan bisa saja mem-parsing sendiri dan lihat berapa besaran file-nya tanpa perlu mendownload file yang saya upload di drive.
Selain untuk blockchain Bitcoin, ternyata @Denis Leonov juga mengatakan di Github kalau cara ini juga bisa bekerja di kebanyakan blockchain altcoins, tapi dia tidak mengatakan di blockchain altcoins mana saja.
Ya, saya juga sudah membaca mengenai hal tersebut; Menurut asumsi saya semua jenis altcoins yang berbasis PoW. Jika melihat pada script di atas itu untuk algoritma SHA256* (pada contoh adalah untuk Bitcoin). * Jenis coin lainnya: https://coinmarketcap.com/view/sha-256/
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
Chikito
Copper Member
Legendary
Offline
Activity: 2982
Merit: 2204
♻️ Automatic Exchange
|
 |
September 05, 2024, 03:38:52 AM |
|
Blockchain Parser ini lebih kurangnya ditujukan agar bisa membaca detail dari database Bitcoin blockchain.
Tujuannya tak lain dan bukan membaca kalau ada error dan memvalidasinya. Kalau ditemukan error pada saat parsing di pyton maka program akan otomatis berhenti, tentunya ini berlaku juga saat menjalankan bitcoin core. Contoh lain, Semisal ketika running bitcoin core, tiba-tiba berhenti sendiri, stuck atau not responding, maka menjalankan Blockchain Parser dan mengambil block terakhir (i.e blk04479) bagus untuk melihat apa yang membuat wallet tersebut error. Entah apakah bisa juga membaca file rev?, Karena di data PC saya ini, selain blk, ada juga data rev00000 - rev04479. (datanya sama, antara blk00000 sampai blk04479), with dot dat
|
░░░░▄▄████████████▄ ░▄████████████████▀ ▄████████████████▀▄█▄ ▄███████▀▀░░▄███▀▄████▄ ▄██████▀░░░▄███▀░▀██████▄ ██████▀░░▄████▄░░░▀██████ ██████░░▀▀▀▀░▄▄▄▄░░██████ ██████▄░░░▀████▀░░▄██████ ▀██████▄░▄███▀░░░▄██████▀ ▀████▀▄████░░▄▄███████▀ ▀█▀▄████████████████▀ ▄████████████████▀░ ▀████████████▀▀░░░░ | | CCECASH | | | | ANN THREAD TUTORIAL |
|
|
|
Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
September 06, 2024, 03:31:53 AM |
|
Blockchain Parser ini lebih kurangnya ditujukan agar bisa membaca detail dari database Bitcoin blockchain.
Tujuannya tak lain dan bukan membaca kalau ada error dan memvalidasinya. Kalau ditemukan error pada saat parsing di pyton maka program akan otomatis berhenti, tentunya ini berlaku juga saat menjalankan bitcoin core. Contoh lain, Semisal ketika running bitcoin core, tiba-tiba berhenti sendiri, stuck atau not responding, maka menjalankan Blockchain Parser dan mengambil block terakhir (i.e blk04479) bagus untuk melihat apa yang membuat wallet tersebut error. Alih-alih menggunakan Blockchain Parser, kalau untuk melihat error atau troubleshooting di Bitcoin Core akan lebih mudah dengan melihat file debug.log, terlebih file tersebut bisa langsung di buka di text editor.  The debug.log file is a log file that is very useful for troubleshooting. It does not leak any information about your private keys so your Bitcoin is always safe. To get the debug.log file, open Bitcoin Core and go to Help > Debug Window and go to the Information tab. Near the bottom right hand corner of the window is a button labeled Open with Debug log file. Click this to access the file. Now you can save the file elsewhere or copy the contents to send to someone.
Entah apakah bisa juga membaca file rev?, Karena di data PC saya ini, selain blk, ada juga data rev00000 - rev04479. (datanya sama, antara blk00000 sampai blk04479), with dot dat
Di folder block memang terdiri dari beberapa jenis nama file selain blkxxxxx.dat, yakni revxxxxx.dat dan di sub folder index ada juga tipe file xxxxxx.ldb. Saya sendiri belum mencoba parsing jenis file revxxxxx.dat. Namun, jika melihat deskripsi di sini: https://github.com/ragestack/blockchain-parser/blob/master/README.md, script tersebut ditujukan untuk file blkXXXXX.dat Bisa dilihat juga script yang saya cantumkan di post pertama:
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
Chikito
Copper Member
Legendary
Offline
Activity: 2982
Merit: 2204
♻️ Automatic Exchange
|
 |
September 09, 2024, 12:05:15 AM |
|
Tujuannya tak lain dan bukan membaca kalau ada error dan memvalidasinya. Kalau ditemukan error pada saat parsing di pyton maka program akan otomatis berhenti, tentunya ini berlaku juga saat menjalankan bitcoin core. Contoh lain, Semisal ketika running bitcoin core, tiba-tiba berhenti sendiri, stuck atau not responding, maka menjalankan Blockchain Parser dan mengambil block terakhir (i.e blk04479) bagus untuk melihat apa yang membuat wallet tersebut error.
Alih-alih menggunakan Blockchain Parser, kalau untuk melihat error atau troubleshooting di Bitcoin Core akan lebih mudah dengan melihat file debug.log, terlebih file tersebut bisa langsung di buka di text editor. Oh jadi tujuan parser ini hanya untuk membaca saja, Mungkin ini cocok digunakan sebagai tempat penyimpanan data bagi penggunaan blockchain yang bukan sebagai currency, semisal pemerintah yang menggunakan blockchain untuk menyimpan data penduduk, atau universitas yang menggunakan blockchain untuk menyimpan data-data mahasiswa (alumni atau data-data ijazah gitu). Ini opini saya saja, mungkin ke depan ada penggunaan lain yang jauh lebih besar sehingga tiap mau parsing tiap blkxxxx.dat ini membutuhkan pasword atau passphrase. Mungkin karen file .rev ini hanya sebagai undo-an dan gabungan rekaman yang isinya ada di dalam setiap blok di blkxxx.dat tadi, sehingga tidak lebih penting dari melihat parsing dari blk itu sendiri.
|
░░░░▄▄████████████▄ ░▄████████████████▀ ▄████████████████▀▄█▄ ▄███████▀▀░░▄███▀▄████▄ ▄██████▀░░░▄███▀░▀██████▄ ██████▀░░▄████▄░░░▀██████ ██████░░▀▀▀▀░▄▄▄▄░░██████ ██████▄░░░▀████▀░░▄██████ ▀██████▄░▄███▀░░░▄██████▀ ▀████▀▄████░░▄▄███████▀ ▀█▀▄████████████████▀ ▄████████████████▀░ ▀████████████▀▀░░░░ | | CCECASH | | | | ANN THREAD TUTORIAL |
|
|
|
Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
September 09, 2024, 05:40:23 PM Last edit: September 09, 2024, 06:55:35 PM by Husna QA |
|
Alih-alih menggunakan Blockchain Parser, kalau untuk melihat error atau troubleshooting di Bitcoin Core akan lebih mudah dengan melihat file debug.log, terlebih file tersebut bisa langsung di buka di text editor.
Oh jadi tujuan parser ini hanya untuk membaca saja, Mungkin ini cocok digunakan sebagai tempat penyimpanan data bagi penggunaan blockchain yang bukan sebagai currency, semisal pemerintah yang menggunakan blockchain untuk menyimpan data penduduk, atau universitas yang menggunakan blockchain untuk menyimpan data-data mahasiswa (alumni atau data-data ijazah gitu). Ini opini saya saja, mungkin ke depan ada penggunaan lain yang jauh lebih besar sehingga tiap mau parsing tiap blkxxxx.dat ini membutuhkan pasword atau passphrase. Kalau untuk penyimpanan data di Blockchain mungkin bisa memanfaatkan OP_RETURN*. Tapi kalau yang disimpan berupa data-data penting seperti data penduduk saya kira cukup beresiko karena tentunya bisa dengan mudah di parsing dan diketahui publik. File blkxxxxx.dat yang ada di data Bitcoin Blockchain kan bukan diperuntukkan bagi user tertentu saja, melainkan untuk semua komunitas Bitcoin. Jadi, kecil kemungkinan akan ada fitur untuk mem-password file tersebut.
* Salah satu website yang mengumpulkan list OP_RETURN dari File blkxxxxx.dat : https://bitcoinstrings.com/Contoh: https://bitcoinstrings.com/blk00000.txt
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
Chikito
Copper Member
Legendary
Offline
Activity: 2982
Merit: 2204
♻️ Automatic Exchange
|
 |
September 10, 2024, 02:04:22 AM |
|
Kalau untuk penyimpanan data di Blockchain mungkin bisa memanfaatkan OP_RETURN*. Tapi kalau yang disimpan berupa data-data penting seperti data penduduk saya kira cukup beresiko karena tentunya bisa dengan mudah di parsing dan diketahui publik.
Ya beresiko, tapi kan tidak mudah seperti membaca file begitu saja, harus pakai pyton dan code segala macam. Setidaknya, kalau institusi dapat memanfaatkan blockchain ini untuk pengiriman data secara tidak langsung ke pengguna mungkin tidak perlu berfile-file data untuk mendownload atau menggunakan file pengiriman media lain kayak email, drive, atau cloud, cukup menggunakan blockchain dan tersimpan secara terbuka di jaringan untuk bisa diakses oleh siapa pun. File blkxxxxx.dat yang ada di data Bitcoin Blockchain kan bukan diperuntukkan bagi user tertentu saja, melainkan untuk semua komunitas Bitcoin. Jadi, kecil kemungkinan akan ada fitur untuk mem-password file tersebut.
Memang agak sulit, apa lagi terpublish secara vulgar ke jaringan. Namun bisa saja ke depan ada softfork untuk mewadahi pengguna jika ingin mempassword file tersebut. Saya cukup optimis ke depan blockchain ini digunakan bukan hanya untuk cryptocurrency saja, tapi hal-hal lain yang berguna untuk penyimpanan data selamanya seperti ijazah, data penduduk atau hal rahasia negara tanpa perlu ruang arsip lagi di kantornya.
|
░░░░▄▄████████████▄ ░▄████████████████▀ ▄████████████████▀▄█▄ ▄███████▀▀░░▄███▀▄████▄ ▄██████▀░░░▄███▀░▀██████▄ ██████▀░░▄████▄░░░▀██████ ██████░░▀▀▀▀░▄▄▄▄░░██████ ██████▄░░░▀████▀░░▄██████ ▀██████▄░▄███▀░░░▄██████▀ ▀████▀▄████░░▄▄███████▀ ▀█▀▄████████████████▀ ▄████████████████▀░ ▀████████████▀▀░░░░ | | CCECASH | | | | ANN THREAD TUTORIAL |
|
|
|
Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
September 10, 2024, 11:58:28 PM |
|
-snip- Saya cukup optimis ke depan blockchain ini digunakan bukan hanya untuk cryptocurrency saja, tapi hal-hal lain yang berguna untuk penyimpanan data selamanya seperti ijazah, data penduduk atau hal rahasia negara tanpa perlu ruang arsip lagi di kantornya.
Setahu saya blockchain sudah pula dimanfaatkan untuk selain cryptocurrency (bisa di browsing agar tidak terlalu jauh diskusinya dengan tema thread). Sementara itu, jika memang negara 'mau' menggunakan teknologi blockchain untuk penyimpanan data penduduk dan semisalnya, yang mungkin lebih realistis digunakan adalah Private Blockchain.
btw, berikut ini salah satu link terkait Blockchain parser namun lebih spesifik ke: OP_RETURN Data parser & search https://github.com/2snEM6/Bitcoin-Data-Parserbeberapa dependencies yang diperlukan: - NodeJS - PostgreSQL - Bitcoind
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
coolmib
Member

Offline
Activity: 66
Merit: 14
|
 |
April 21, 2025, 06:39:45 AM |
|
Apakah ada yg memanfaatkan module ini u/ mengextract data R, S, Z dari tiap txid ? atau ada opensource lain yg lbh tepat untuk melakukan hal ini> Mohon dishare jika berkenan..
|
|
|
|
|
Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
April 25, 2025, 03:13:21 PM |
|
Apakah ada yg memanfaatkan module ini u/ mengextract data R, S, Z dari tiap txid ? atau ada opensource lain yg lbh tepat untuk melakukan hal ini> Mohon dishare jika berkenan..
Saya sendiri belum pernah mencobanya. Namun, link berikut mungkin bisa memberikan tambahan informasi untuk agan: https://bitcoin.stackexchange.com/questions/93711/how-do-i-to-get-the-r-s-and-z-values-from-a-raw-transaction-version-2
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
coder0x15
Jr. Member
Offline
Activity: 38
Merit: 27
|
 |
December 19, 2025, 03:31:07 AM |
|
Hi all, I’ve released v2.0.0 of my lightweight blockchain parser script, now called Blockchain Scalpel. The tool parses raw Bitcoin blk*.dat files directly from disk and is intended for research, analysis, and low-level blockchain data processing. What’s new in v2.0.0: - Added support for XOR-key handling for blocksdir *.dat files (if no key is present or it is zero, files are parsed directly)
- Incremental parsing: detects already processed files in the output directory and parses only new block files
- If an incomplete (usually the last) .dat file is detected, the script reports an error and stops to avoid corrupted output
- Updated documentation and release notes
Project repository: https://github.com/ragestack/blockchain-parserThe script remains simple, dependency-free, and easy to modify. Feedback is welcome. — Denis
|
|
|
|
|
Husna QA (OP)
Copper Member
Legendary
Offline
Activity: 2884
Merit: 3238
|
 |
December 20, 2025, 05:57:11 AM |
|
Hi all,
I’ve released v2.0.0 of my lightweight blockchain parser script, now called Blockchain Scalpel.
The tool parses raw Bitcoin blk*.dat files directly from disk and is intended for research, analysis, and low-level blockchain data processing.
What’s new in v2.0.0: -snip-
@Denis Leonov, Thank you for visiting the Indonesian local board and for sharing information about the latest version of Blockchain Parser/Blockchain Scalpel. I previously used an earlier version for testing, as described in the first post of this thread. In earlier versions, I had to manually create folders to store block data and result files. Meanwhile, in this latest version, I see that those folders are now provided automatically (I forgot whether the previous version also created these folders automatically or not, since at that time I directly used the raw files from blockchain-parser.py).  In version 1, I had no difficulty configuring the paths for the input and output folders; -snip- However, in version 2, where can these path locations be configured in the script, as before? #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ blockchain-parser.py
Author: Denis Leonov Project: Blockchain Parser (Blockchain Scalpel) Repository: https://github.com/ragestack/blockchain-parser Version: 2.0.0
License: Blockchain Scalpel License (Source-Available, Non-Commercial) Free for non-commercial use with attribution. Commercial use and any SaaS/cloud/hosted use require a paid license.
Commercial licensing contact: 466611@gmail.com Other author's contact info: https://aaris.ru/DL
Limited warranty: A limited compatibility warranty related to block format changes is provided. See LICENSE and WARRANTY.md for details. """
__version__ = "2.0.0" __author__ = "Denis Leonov"
# SPDX-License-Identifier: LicenseRef-Blockchain-Scalpel
import os, io, sys import datetime import hashlib
def reverse(input): L = len(input) if (L % 2) != 0: return None else: Res = '' L = L // 2 for i in range(L): T = input[i*2] + input[i*2+1] Res = T + Res T = '' return (Res);
def merkle_root(h): d = lambda b:hashlib.sha256(hashlib.sha256(b).digest()).digest() rev = lambda x:x[::-1] h = list(map(rev,h)) while len(h) > 1: if len(h)&1:h += h[-1:] h = [d(h[i]+h[i+1]) for i in range(0,len(h),2)] return rev(h[0])
def read_bytes(file,n,byte_order = 'L'): data = file.read(n) if byte_order == 'L': data = data[::-1] data = data.hex().upper() return data
def read_varint(file): b = file.read(1) bInt = int(b.hex(),16) c = 0 data = '' if bInt < 253: c = 1 data = b.hex().upper() if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = file.read(1) b = b.hex().upper() data = b + data return data
def print_help(script_name): print( f"Usage:\n" f" python {script_name} <dirA> <dirB>\n\n" f"Arguments:\n" f" dirA Directory where blk*.dat files are stored (must exist)\n" f" dirB Output directory for parsing results (must already exist)\n" )
def main(): script_name = os.path.basename(sys.argv[0])
if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"): print_help(script_name) sys.exit(0)
if len(sys.argv) != 3: print("Error: exactly 2 arguments are required.\n", file=sys.stderr) print_help(script_name) sys.exit(1) dirA = os.path.abspath(os.path.expanduser(sys.argv[1])) dirB = os.path.abspath(os.path.expanduser(sys.argv[2]))
if not os.path.isdir(dirA): print(f"Error: input directory does not exist or is not a directory:\n {dirA}", file=sys.stderr) sys.exit(1)
if not os.path.exists(dirB): print( f"Error: output directory does not exist (create it first):\n {dirB}", file=sys.stderr) sys.exit(1)
if not os.path.isdir(dirB): print(f"Error: output path exists but is not a directory:\n {dirB}", file=sys.stderr) sys.exit(1)
if not os.access(dirB, os.W_OK): print(f"Error: output directory is not writable:\n {dirB}", file=sys.stderr) sys.exit(1)
kN = os.path.join(dirA, 'xor.dat')
k_ = b'\x00\x00\x00\x00\x00\x00\x00\x00' lk_ = len(k_) if os.path.isfile(kN): with open(kN, 'rb') as kF: k_ = kF.read(lk_)
fList = [x for x in os.listdir(dirA) if x.endswith('.dat') and x.startswith('blk')] rList = [x for x in os.listdir(dirB) if x.endswith('.txt') and x.startswith('blk')]
rNames = {os.path.splitext(x)[0] for x in rList}
fList = [x for x in fList if os.path.splitext(x)[0] not in rNames] fList.sort()
for i in fList: nameSrc = i nameRes = nameSrc.replace('.dat','.txt') resList = [] a = 0 t = os.path.join(dirA, nameSrc) resList.append('Start ' + t + ' in ' + str(datetime.datetime.now())) print ('Start ' + t + ' in ' + str(datetime.datetime.now())) with open(t,'rb') as f0: b_ = bytearray(f0.read()) if any(k_): for ii in range(len(b_)): b_[ii] ^= k_[ii%lk_] f = io.BytesIO(b_) tmpHex = '' fSize = os.path.getsize(t) while f.tell() != fSize: tmpErr = 0 while tmpHex != 'D9B4BEF9': # it is for to skip zeroes in some blk files tmpHex = read_bytes(f,4) tmpErr += 1 if tmpErr > 2: raise ValueError(f"Invalid data: magic number missing — possible truncated {i} file") resList.append('Magic number = ' + tmpHex) tmpHex = read_bytes(f,4) resList.append('Block size = ' + tmpHex) tmpPos3 = f.tell() tmpHex = read_bytes(f,80,'B') tmpHex = bytes.fromhex(tmpHex) tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = tmpHex[::-1] tmpHex = tmpHex.hex().upper() resList.append('SHA256 hash of the current block hash = ' + tmpHex) f.seek(tmpPos3,0) tmpHex = read_bytes(f,4) resList.append('Version number = ' + tmpHex) tmpHex = read_bytes(f,32) resList.append('SHA256 hash of the previous block hash = ' + tmpHex) tmpHex = read_bytes(f,32) resList.append('MerkleRoot hash = ' + tmpHex) MerkleRoot = tmpHex tmpHex = read_bytes(f,4) resList.append('Time stamp = ' + tmpHex) tmpHex = read_bytes(f,4) resList.append('Difficulty = ' + tmpHex) tmpHex = read_bytes(f,4) resList.append('Random number = ' + tmpHex) tmpHex = read_varint(f) txCount = int(tmpHex,16) resList.append('Transactions count = ' + str(txCount)) resList.append('') tmpHex = ''; RawTX = ''; tx_hashes = [] for k in range(txCount): tmpHex = read_bytes(f,4) resList.append('TX version number = ' + tmpHex) RawTX = reverse(tmpHex) tmpHex = '' Witness = False b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) if bInt == 0: tmpB = '' f.seek(1,1) c = 0 c = f.read(1) bInt = int(c.hex(),16) tmpB = c.hex().upper() Witness = True c = 0 if bInt < 253: c = 1 tmpHex = hex(bInt)[2:].upper().zfill(2) tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex inCount = int(tmpHex,16) resList.append('Inputs count = ' + tmpHex) tmpHex = tmpHex + tmpB RawTX = RawTX + reverse(tmpHex) for m in range(inCount): tmpHex = read_bytes(f,32) resList.append('TX from hash = ' + tmpHex) RawTX = RawTX + reverse(tmpHex) tmpHex = read_bytes(f,4) resList.append('N output = ' + tmpHex) RawTX = RawTX + reverse(tmpHex) tmpHex = '' b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) c = 0 if bInt < 253: c = 1 tmpHex = b.hex().upper() tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex scriptLength = int(tmpHex,16) tmpHex = tmpHex + tmpB RawTX = RawTX + reverse(tmpHex) tmpHex = read_bytes(f,scriptLength,'B') resList.append('Input script = ' + tmpHex) RawTX = RawTX + tmpHex tmpHex = read_bytes(f,4,'B') resList.append('Sequence number = ' + tmpHex) RawTX = RawTX + tmpHex tmpHex = '' b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) c = 0 if bInt < 253: c = 1 tmpHex = b.hex().upper() tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex outputCount = int(tmpHex,16) tmpHex = tmpHex + tmpB resList.append('Outputs count = ' + str(outputCount)) RawTX = RawTX + reverse(tmpHex) for m in range(outputCount): tmpHex = read_bytes(f,8) Value = tmpHex RawTX = RawTX + reverse(tmpHex) tmpHex = '' b = f.read(1) tmpB = b.hex().upper() bInt = int(b.hex(),16) c = 0 if bInt < 253: c = 1 tmpHex = b.hex().upper() tmpB = '' if bInt == 253: c = 3 if bInt == 254: c = 5 if bInt == 255: c = 9 for j in range(1,c): b = f.read(1) b = b.hex().upper() tmpHex = b + tmpHex scriptLength = int(tmpHex,16) tmpHex = tmpHex + tmpB RawTX = RawTX + reverse(tmpHex) tmpHex = read_bytes(f,scriptLength,'B') resList.append('Value = ' + Value) resList.append('Output script = ' + tmpHex) RawTX = RawTX + tmpHex tmpHex = '' if Witness == True: for m in range(inCount): tmpHex = read_varint(f) WitnessLength = int(tmpHex,16) for j in range(WitnessLength): tmpHex = read_varint(f) WitnessItemLength = int(tmpHex,16) tmpHex = read_bytes(f,WitnessItemLength) resList.append('Witness ' + str(m) + ' ' + str(j) + ' ' + str(WitnessItemLength) + ' ' + tmpHex) tmpHex = '' Witness = False tmpHex = read_bytes(f,4) resList.append('Lock time = ' + tmpHex) RawTX = RawTX + reverse(tmpHex) tmpHex = RawTX tmpHex = bytes.fromhex(tmpHex) tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = hashlib.new('sha256', tmpHex).digest() tmpHex = tmpHex[::-1] tmpHex = tmpHex.hex().upper() resList.append('TX hash = ' + tmpHex) tx_hashes.append(tmpHex) resList.append(''); tmpHex = ''; RawTX = '' a += 1 tx_hashes = [bytes.fromhex(h) for h in tx_hashes] tmpHex = merkle_root(tx_hashes).hex().upper() if tmpHex != MerkleRoot: print ('Merkle roots does not match! >',MerkleRoot,tmpHex) f.close() f = open(os.path.join(dirB, nameRes),'w') for j in resList: f.write(j + '\n') f.close()
print ('All done ' + str(datetime.datetime.now()))
if __name__ == "__main__": main()
|
| | Peach BTC bitcoin | │ | Buy and Sell Bitcoin P2P | │ | . .
▄▄███████▄▄ ▄██████████████▄ ▄███████████████████▄ ▄█████████████████████▄ ▄███████████████████████▄ █████████████████████████ █████████████████████████ █████████████████████████ ▀███████████████████████▀ ▀█████████████████████▀ ▀███████████████████▀ ▀███████████████▀ ▀▀███████▀▀
▀▀▀▀███████▀▀▀▀ | | EUROPE | AFRICA LATIN AMERICA | | | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
███████▄█ ███████▀ ██▄▄▄▄▄░▄▄▄▄▄ █████████████▀ ▐███████████▌ ▐███████████▌ █████████████▄ ██████████████ ███▀███▀▀███▀ | . Download on the App Store | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ | ▄▀▀▀ █ █ █ █ █ █ █ █ █ █ █ ▀▄▄▄ |
▄██▄ ██████▄ █████████▄ ████████████▄ ███████████████ ████████████▀ █████████▀ ██████▀ ▀██▀ | . GET IT ON Google Play | ▀▀▀▄ █ █ █ █ █ █ █ █ █ █ █ ▄▄▄▀ |
|
|
|
|