TIEA1000 Tietotekniikan ajankohtaisia teemoja, Lohkoketjuteknologiat ja sovellutukset

Harjoitustyö

Tietoa harjoitustyöstä löytyy täältä.

Santeri Tanin luennot

TIEA100: Lohkoketjuteknologiat ja sovellutukset - luento 1

TIEA100: Lohkoketjuteknologiat ja sovellutukset - luento 2

Yhteystietoja

Professori Veikko Haraan (040-5003460) voi ottaa yhteyttä mihin tahansa lohkoketjuun liittyvän asian suhteen. Henri Heinoselta voi myös lohkoketjuista, luennoista ja harjoitustyöstä.

Linkkejä

Lohkoketjut lyhyesti

Lohkoketju (Blockchain) on desentralisoitu (decentralized) vertaisverkkoon (peer-to-peer network) hajautettu (distributed) tietokanta (database) - kasvava lista tietueita/lohkoja, jotka liittyvät toisiinsa ja jotka turvataan kryptografian avulla. Lohkossa on tyypillisesti tiivistemuotoinen osoitin (hash pointer) linkkinä edelliseen lohkoon, aikaleima (timestamp) ja transaktiotietoa. Lohkoketjuissa olevaa tietoa on vaikeaa muuttaa.

Bitcoinin lohkoketjusta kaikki alkoi

Bitcoin-protokollan ideaa esiteltiin vuonna 2008 pseudonyymi Satoshi Nakamoton toimesta ja Bitcoin-verkko käynnistyi tammikuussa 2009, jolloin syntyi Bitcoinin alkulohko (Genesis block) ja ensimmäiset bitcoin-rahakkeet/-poletit (tokens).

Bitcoin on siis ensimmäisiä lohkoketjusovellutuksia. Se sopii hyvin rahasiirtoihin, mutta lohkoketjuille löytyy paljon muitakin käyttötarkoituksia.

Bitcoinin lohkoketjuun syntyy uusia lohkoja noin 10 minuutin välein. Jokaisessa lohkossa on siis edellisen lohkon ylätunnisteen (block header) tiiviste (hash). Tämä mahdollistaa sen, että lohkossa olevia tietoja ei voida muuttaa ilman, että tiiviste samalla muuttuisi. Uusia lohkoja luovat louhijat, jotka verifioivat ja kokoavat bitcoin-transaktioita lohkoihin. Uuden lohkon lohkoketjuun lisännyt louhija saa palkinnoksi sillä hetkellä vallitsevan lohkopalkkion (block reward) verran bitcoineja. Alussa tämä palkkio oli 50 BTC, neljä vuotta myöhemmin 25 BTC, ja tällä hetkellä palkkio on enää 12,5 BTC. Lisäksi louhijat saavat löytämänsä lohkon kuhunkin transaktioon liitetyn siirtokulun (transaction fee). Tämä motivoi louhijoita valitsemaan sellaiset bitcoin-transaktiot, joissa on mukana riittävän suuri siirtokulu. Tällä hetkellä Bitcoinin lohkoon mahtuu yhden megatavun verran transaktiodataa.

Bitcoin-louhinnalla on siis kaksi tarkoitusta: varmentaa bitcoin-transaktiot ja jakaa uutta bitcoin-rahaketta järjestelmään.

Miksi bitcoin on hyvä raha(ke)?

Bitcoin on hyvä raha ainakin siksi, että se toteuttaa hyvin useimmat rahalta vaadittavat ominaisuudet, joita ovat niukkuus (scarcity), kestävyys (durability), jaettavuus (divisibility), todennettavuus (verifiability), siirrettävyys (portability) ja vaihdettavuus (fungibility).

Niukkuus

Bitcoineja syntyy matemaattisen mallin mukaisesti melkein 21 miljoonaa kappaletta. Tämä raja on tarkoitus saavuttaa noin vuonna 2140. Suurin osa bitcoineista on louhittu jo 2010-luvun aikana, joten niitä on todella niukasti.

Kestävyys

Bitcoin digitaalisena rahakkeena kestää tietysti ajanhammasta paremmin kuin fyysinen raha

Jaettavuus

Bitcoinin voin nykyisellään jakaa pienimmillään sadasmiljoonasosaansa; sillä on oma nimikin: satoshi. Desimaaleja on siis käytössä kahdeksan kappaletta. Näitä voidaan tarvittaessa lisätä myöhemmin, jos ne eivät riitä. Jos bitcoinin arvo nousisi miljoonaan euroon, tällöin yksi satoshi olisi arvoltaan 0,01 euroa eli eurosentin. Näin korkealla kurssilla, olisi varmastikin jo tarpeen lisätä desimaaleja.

Bitcoin toimii kuin käteinen. Edellisissä siirroissa saadut "kolikot" lähetetään eteenpäin, ja tarvittaessa saadaan takaisin vaihtorahaa lompakkoon (yleensä) automaattisesti luotuun vaihtorahaosoitteeseen (change address).

Todennettavuus

Tiivistefunktiot toimivat siten, että niiden laskeminen vaatii paljon laskentatehoa, mutta todentaminen onnistuu helposti.

Siirrettävyys

Bitcoineja voi käyttää kaikkialla, missä on Internet-yhteys. Bitcoineja voi vastaanottaa, vaikkei olisi Internet-yhteyttäkään. Bitcoin-osoitteita voi generoida tietokoneella, joka ei koskaan ole yhteydessä Internetiin. Bitcoin-osoitteen voi antaa kaverille vaikkapa kirjeitse tai puhelimitse.

Vaihdettavuus

Bitcoin sisältää koko transkatiohistoriansa, joten esimerkiksi rikollisessa toiminnassa mukana olleita bitcoineja ei välttämättä hyväksytä kaikkialla. Siispä kaikki bitcoinit eivät olekaan yhtälailla vaihtokelpoisia keskenään.

Kryptografiset tiivisteet

Yksi lohkoketjuihin (blockchain) ja kryptovaluuttoihin (cryptocurrency) liittyvä tärkeä teknologia ovat kryptografiset tiivisteet (cryptographic hash), joita tarvitaan aina, kun louhitaan (mine) tai lähetetään ja vastaanotetaan transaktioita (transactions). Tiivistefunktioiden tarkoituksena on ottaa minkä tahansa kokoinen viipale dataa sisäänsä ja antaa ulos tietynkokoinen, sattumanvaraiselta näyttävä, mutta täysin uudelleenlaskettava luku. Jos siis täysin sama merkkijono syötetään samaan tiivistefunktioon, tulee ulos joka kerta täysin sama luku.

Kokeillaan paria esimerkkimerkkijonoa Linuxin komentorivillä.

henri@henri-VirtualBox:~$ echo "Heippa, maailma!"
Heippa, maailma!
henri@henri-VirtualBox:~$ echo -n "Heippa, maailma!"
Heippa, maailma!henri@henri-VirtualBox:~$

Mitä eroa näillä oli? Ero oli puuttuva rivinvaihto, joka toteutettiin echo-komennon "-n"-parametrillä. Katsotaan seuraavaksi, mitkä ovat näiden kahden samankaltaisen, mutta silti erilaisen, merkkijonon SHA256-tiivisteet.

henri@henri-VirtualBox:~$ echo "Heippa, maailma!" | sha256sum
0ed7ba2aebf392f35eebf519875f400001e2b42a6a712c9e5680b8d1a45a05eb  -
henri@henri-VirtualBox:~$ echo -n "Heippa, maailma!" | sha256sum
4ce0c1199198c847a895e00d2d57f908789f07e946d916dae0b9d19e06c9c089  -

Havaitaan, että pelkkä rivinvaihdon puuttuminen merkkijonosta muuttaa SHA256-tiivistefunktion tulosteen aivan erilaiseksi! Sama ilmiö tapahtuu, kun vaihdetaan huutomerkki pisteeksi.

henri@henri-VirtualBox:~$ echo -n "Heippa, maailma." | sha256sum
c2d5d860bf96df3a7041c09c171d7388d2859b89909f69e83f32b9611bbf5c16  -

SHA256-tiivistefunktio antaa ulos 256-bittisen luvun. Koska 256 binäärinumeron esittäminen vie paljon tilaa, käytetään yleensä numerojärjestelmää, jossa enemmän kuin kaksi numeroa. Tietotekniikassa binääriesityksen ohella käytetään paljon heksadesimaaliesitystä, koska neljä bittiä riittää kaikkien heksadesimaalijärjestelmän numeroiden esittämiseen! Esimerkiksi, 1510=F16=11112. Tyypillisesti siis SHA256:n antama luku ilmaistaan 256 bitin asemesta 32 heksalla.

Bitcoinin lohkon ylätunniste

Bitcoinin lohkon ylätunnisteessa (Bitcoin block header) on 80 tavun verran dataa kuudessa erilaisessa kentässä oheisen taulukon mukaisesti.

TavujaNimiKuvaus
4VersioLohkon versionumero kertoo, mitä lohkovarmennussääntöjä seurata.
32Edellisen lohkon ylätunnisteen tiivisteEdellisen lohkon ylätunniste SHA256d-tiivisteenä sisäisessä tavujärjestyksessä. Tämä varmistaa, että edellistä lohkoa ei voida muuttaa ilman, että myös tämän lohkon ylätunniste muuttuu.
32Merkle-juuren tiivisteSHA256d-tiiviste sisäisessä tavujärjestyksessä, joka on saatu kaikista tämän lohkon transaktioiden tiivisteistä. Tämä varmistaa, että mitään transaktiota ei voida muuttaa ilman, että tiivistekin muuttuu.
4AikaArvio Unix-ajasta, jolloin ylätunnistetta alettiin tiivistää louhijan mielestä. Tämän pitää olla suurempi tai yhtä suuri kuin edellisten 11 lohkon mediaaniaika.
4nBittiäKohdekynnys, jota tämän lohkon ylätunnisteen tiiviste tulee olla pienempi tai yhtä suuri kuin.
4NonceSatunnainen luku, jota louhijat muuttavat louhimisen aikana.

blockchain.info-sivusto näyttää teknistä tietoa Bitcoin-lohkoista.

Luodaan tiedosto louhin.py.

from struct import pack
from hashlib import sha256
from codecs import decode
from binascii import hexlify

def sha256d(data):
  return sha256(sha256(data).digest()).digest()

def internal2rpc(hash):
  return hexlify(hash[::1])

version = pack("<L", 0x01)

previous_header_hash = decode("0000000000000000000000000000000000000000000000000000000000000000", 'hex')

merkle_root = decode("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", 'hex')[::-1]

date = pack("<L", 1231006505)

nbits = pack("<L", 0x1d00ffff)

nbits_calc = hexlify(nbits[::-1])

base = 256

exponent = int(nbits_calc[0:2], 16) - 3
significand = int(nbits_calc[2:8], 16)

target = significand * ( base ** exponent )

nonce = 0x7c2bac10
while nonce < 0x7c2bac1e:
  print("nonce: ", format(nonce, 'x'), " = ", nonce)
  header = (
    version
    + previous_header_hash
    + merkle_root
    + date
    + nbits
    + pack("<L", nonce)
  )

  header_hash = sha256d(header)

  if int(hexlify(header_hash[::-1]), 16) < target:
    print("Nonce      Header Hash")
    print(nonce, internal2rpc(header_hash))
    break

  nonce += 1

Koska tämä on keinotekoinen esimerkki, nonce on jo valmiiksi laitettu lähelle lopullista arvoaan eli tarvitaan vain muutaman tiivisteen laskeminen.

(G:\Users\henriheinonen\Anaconda2) G:\lohkoketjut>python louhin.py
('nonce: ', '7c2bac10', ' = ', 2083236880)
('nonce: ', '7c2bac11', ' = ', 2083236881)
('nonce: ', '7c2bac12', ' = ', 2083236882)
('nonce: ', '7c2bac13', ' = ', 2083236883)
('nonce: ', '7c2bac14', ' = ', 2083236884)
('nonce: ', '7c2bac15', ' = ', 2083236885)
('nonce: ', '7c2bac16', ' = ', 2083236886)
('nonce: ', '7c2bac17', ' = ', 2083236887)
('nonce: ', '7c2bac18', ' = ', 2083236888)
('nonce: ', '7c2bac19', ' = ', 2083236889)
('nonce: ', '7c2bac1a', ' = ', 2083236890)
('nonce: ', '7c2bac1b', ' = ', 2083236891)
('nonce: ', '7c2bac1c', ' = ', 2083236892)
('nonce: ', '7c2bac1d', ' = ', 2083236893)
Nonce      Header Hash
(2083236893, '6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000')

Yksityinen avain, WIF-muoto, julkinen avain, (bitcoin-)osoite

Avataan tämän luentomonistesivun Web-konsoli (Mozilla Firefoxissa: Ctrl + Vaihto + K). Konsoliin voi syöttää JavaScript-komentoja, jotka hyödyntävät bitcore-nimistä kirjastoa. Tämä kirjasto ja sen apukirjastot on ladattu HTML-sivun <head> ja </head> -tägien välissä.

<script src="bower_components/bitcore-lib/bitcore-lib.min.js"></script>
<script src="bower_components/bitcore-mnemonic/bitcore-mnemonic.min.js"></script>

Lisäksi <body> ja </body> -tägien välissä ajetaan seuraava JavaScript-ohjelma:

<script>
var bitcore = require('bitcore-lib');
var Mnemonic = require('bitcore-mnemonic');
</script>

Yksityinen avain (private key) on yleensä 256-bittinen luku eli se koostuu 256 binäärinumerosta. Bitcoin-maailmassa yksityinen avain on satunnainen kokonaisluku välillä [1, n-1], missä n = (FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141)16. (16 * 4 heksadesimaalia, joista jokaisen esittämiseen tarvitaan 4 bittiä: 16 * 4 * 4 bittiä = 256 bittiä.) Yksityinen avain mahdollistaa bitcoinien käyttämisen transaktioissa (eli lähettämisen eteenpäin). Yksityisen avaimen ja julkisen avaimen (public key) välillä on matemaattinen suhde. Julkisesta avaimesta luodaan vielä tiiviimpi bitcoin-osoite, joka sisältää myös tarkistussumman virhelyöntien havaitsemiseksi.

Luodaan yksityinen avain bitcore-kirjaston PrivateKey()-funktion avulla ja tallennetaan se muuttujaan privateKey. Lopuksi vielä tulostetaan muuttujan sisältö konsoliin.

var privateKey = new bitcore.PrivateKey();
console.log("yksityinen avain heksadesimaalimuodossa: " + privateKey.toString()); // ef0dddb11f82c123bf968302b48b9c1e1eddaeeeebd8cc1be634ec7af7183d25

Havaitaan, että yksityinen avain on muodossa, jossa on 64 heksadesimaalia. Koska yksi heksadesimaali vastaa neljää bittiä, on generoitu avain tosiaan 256-bittinen.

Kun yksityisiä avaimia tuodaan (importing) tai lakaistaan (sweeping), käytetään yleensä lyhyempää formaattia nimeltään lompakkotuontiformaatti (Wallet Import Format, WIF). Tästä formaatissa on se etu, että avain/osoite vie vähemmän tilaa ruudulta/paperilta, ja lisäksi siihen on sisäänrakennettu tarkistussumma, jonka avulla havaitaan mahdolliset lyöntivirheet. Heksadesimaalimuotoisessa avaimessa ei ole tätä mahdollisuutta.

Viedään generoitu yksityisavain nyt WIF-muotoon ja tallennetaan se muuttujaan exported. Tulostetaan lopuksi muuttujan sisältö konsoliin.

var exported = privateKey.toWIF();
console.log("yksityinen avain WIF-muodossa: " + exported); // L5EQD1SgEndGUCrYkAxKf1mU4wDULP8QGSPTGmQe2gK3PwmrJKmo

Havaitaan, että WIF-muotoinen yksityisavain alkaa joko K:lla tai L:llä. Tämä on yksityinen avain ns. pakatussa (compressed) muodossa. Yksityinen avain voi olla myös pakkaamattomassa (uncompressed) muodossa.

Yksityisestä avaimesta generoidaan julkinen avain näin.

var publicKey = privateKey.publicKey;
console.log("julkinen avain heksadesimaalimuodossa: " + publicKey.toString()); // 035eadf600daa0b2d02342f4f425c3291d31d4cc01747ee003b10a1d750d37bfcc

Julkista avainta voitaisiin käyttää Bitcoin-osoitteena, mutta siihen sisältyy riskejä, koska siinä ei ole tarkistussummaa, joka havaitsee virhelyönnit. Julkisesta avaimesta luodaan tarkistussumman sisältävä Bitcoin-osoite, joka on myös pituudeltaan selvästi lyhyempi kuin heksadesimaalimuotoinen avain, koska Base58-merkintätapa sisältää 58 merkkiä, kun taas heksadesimaalimerkkejä on vain 16. Lisäksi Bitcoin-osoite paljastaa, onko kyseessä Bitcoin-pääverkon (mainnet, livenet) osoite (alkaa yleensä numerolla 1 tai 3) vai testiverkon osoite (alkaa yleensä kirjaimella m tai n).

var address = publicKey.toAddress();
console.log("julkinen avain Base58-muodossa eli Bitcoin-osoitteena: " + address.toString()); // 1JEwvc8bJx7Tjp6XLjsMicZXECakFx9DEJ

Testiverkon Bitcoin-osoite voidaan luoda samasta julkisesta avaimesta antamalla toAddess-funktiolle parametriksi bitcore.Networks.testnet.

var address2 = publicKey.toAddress(bitcore.Networks.testnet);
console.log("sama julkinen avain Base58-muodossa eli Bitcoin-osoitteena (Bitcoinin testiverkolle): " + address2.toString()); // mzgaMvufWcn3BhyGXpJ3xMeGf43qETdEbq

Entä, jos halutaan luoda avain jostakin helposti muistettavasta salasanasta/salafraasista? Se onnistuu ohisella menetelmällä.

var hashBuffer = new bitcore.deps.Buffer('Heippa, maailma!');
console.log(hashBuffer.toString()); // Heippa, maailma!

var hash = bitcore.crypto.Hash.sha256(hashBuffer);
var bn = bitcore.crypto.BN.fromBuffer(hash);
console.log(bn.toString()); // 34772882961215334166312040706666980173937394587214071999772413302345558507657

var privateKey2 = new bitcore.PrivateKey(bn);
console.log(privateKey2.toString());
console.log(bitcore.PrivateKey.isValid(privateKey2));

Viestin/transaktion allekirjoittaminen

Jos Alice haluaa maksaa Bobille bitcoinin, hänen pitää lähettää viesti (transaktio) Bitcoin-verkolle, jotta kaikki voivat päivittää tilikirjan ajantasalle. On kuitenkin voitava varmistua siitä, että oikea Alice todella oli transaktio takana. Tähän viestiin tarvitaan kolme asiaa

var hashBuffer = new bitcore.deps.Buffer('Heippa, maailma!');
var hash = bitcore.crypto.Hash.sha256(hashBuffer);
var bn = bitcore.crypto.BN.fromBuffer(hash);
var yksityinenAvainAlice = new bitcore.PrivateKey(bn);
var julkinenAvainAlice = yksityinenAvainAlice.publicKey;
var osoiteAlice = julkinenAvainAlice.toAddress();
var viesti = "Alice maksaa 1 bitcoinin Bobille."
var allekirjoitusAlice = bitcore.Message(viesti).sign(yksityinenAvainAlice);
var verifiointi = bitcore.Message(viesti).verify(osoiteAlice, allekirjoitusAlice);

Bitcoin Core, Bitcoin Knots

Bitcoin-lohkoketjuja pääsee käyttämään mm. Bitcoin Core ja Bitcoin Knots -ohjelmistoilla. Näissä esimerkeissä käytetään Bitcoin Knotsin vanhaa versiota regtest-moodissa, jolla on hyvä saada tuntumaan Bitcoinin komentorivityökaluihin ilman, että on vaarassa menettää oikeita bitcoineja. Regtest-moodissa luodaan oma yksityinen lohkoketju, jolloin ei tarvitse hakea edes muutaman gigatavun kokoista Bitcoin testnet -lohkoketjua.

Pura tiedosto bitcoin-0.12.0.knots20160226.rc1-linux64.tar.gz johonkin kansioon 64-bittisessä Linuxissa. Luo tiedosto $HOME/.bitcoin/bitcoin.conf ja lisää sinne oheiset rivit.

regtest=1
rpcuser=testi
rpcpassword=testi

Aja vielä komento

chmod 0600 $HOME/.bitcoin/bitcoin.conf
joka muuttaa tiedoston käyttöoikeuksia siten, että vain käyttäjä itse voi lukea sitä.

Monen kehittäjän mielestä regtest-moodi on paras tapa kehittää uusia sovelluksia. Käynnistetään Bitcoin Knots -solmu regtest-moodissa ja demonina oheisella komennolla.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoind -regtest -daemon
Bitcoin server starting

Käytetään RPC:tä, joka generoi 101 lohkoa. Regtest-moodissa vain 150 ensimmäisessä lohkossa on 50 bitcoinin lohkopalkkio. Koska vaaditaan 100 varmistusta, ennen kuin lohkopalkkio voidaan käyttää, generoimme 101 lohkoa oheisella tavalla.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest generate 101
[
  "78715f5820b5f2eaeb24f4d02600efbd52ec7f8623e1d475e7ad2b6b4cad9fbc", 
  "7b7d41a83cddce4e938a3a7ece1659b7e41d91ae44287585315504959d41eca2", 
...,
  "5a3e23c83eafb94f8c2ff90ecb4306fcf455356b65407a0cf20c672b455ac995", 
  "77afdfb31acc9a207dad90d1af153b9c3b6702f4cb0742b1e3e0f5d25a8543af"
]

Katsotaan, olemmeko saaneet 50 bitcoinin lohkopalkkion.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest getbalance
50.00000000

Katsotaan lompakon käyttämättömät ulostulot transaktioille, joilla on vähintään 1 varmennus.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent
[
  {
    "txid": "c75f9863b791fd1dfb5815f0e28e46bb6cc257b48c76f6355104f5378db8b997",
    "vout": 0,
    "address": "mtnP9JTzSRozPE9MhY6nF67BppeQcJmGka",
    "scriptPubKey": "21034ad40bc2a7ffa13a0bfc72ba3d19fa5fb55d05163e90eabe97794633be885432ac",
    "amount": 50.00000000,
    "confirmations": 101,
    "spendable": true
  }
]

Generoidaan uusi bitcoin-osoite ja tallennetaan se ympäristömuuttujaan NEW_ADDRESS.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ NEW_ADDRESS=`./bitcoin-cli -regtest getnewaddress`
henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ echo $NEW_ADDRESS
mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya

Lähetetään 10,00 bitcoinia juuri luotuun bitcoin-osoitteeseen.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest sendtoaddress $NEW_ADDRESS 10.00
a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8

Katsotaan listunspent-komennolla lompakon käyttämättömät transaktioiden ulostulot.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent
[
]

Niitä ei löytynyt, koska oletuksena ei näytetä käyttämättömiä ulostuloja transaktioille, joilla on 0 varmennusta. Annetaan edelliselle komennolle vielä parametriksi 0.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent 0
[
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 0,
    "address": "muPwQz5bLqtVWYyTHTLwR9fAMbLz1PbQ5q",
    "scriptPubKey": "76a914983e1d3e7e2b8199b214bf678451341de20fc3e988ac",
    "amount": 39.99996160,
    "confirmations": 0,
    "spendable": true
  }, 
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 1,
    "address": "mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya",
    "account": "",
    "scriptPubKey": "76a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac",
    "amount": 10.00000000,
    "confirmations": 0,
    "spendable": true
  }
]

Näemme, että uuteen bitcoin-osoitteeseen on mennyt 10 bitcoinia, mutta yhtään varmennusta ei vielä ole kyseiselle transaktiolle. Havaitaan, että vaihtorahat ovat menneet lompakon luomaan vaihtorahaosoitteeseen "muPwQz5bLqtVWYyTHTLwR9fAMbLz1PbQ5q". Havaitaan myös, että 40,00000000 BTC - 39,99996160 BTC = 0,0000384 BTC on mennyt siirtokuluina louhijoille.

Katsotaan uudelleen, mitä listunspent-komento näyttää ilman parametria.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent
[
]

Se näytti vieläkin tyhjää, koska ei ole louhittu uusia lohkoja. Tehdään yksi uusi lohko.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest generate 1
[
  "1e40d53c17271da7f1bc3480c58f58eb90f9266d11e75ec978b7da33cdb0a9c2"
]

Katsotaan taas, mitä listunspent-komento näyttää.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent
[
  {
    "txid": "bf9ae40e0e5f758822fa304a1715f99a766b6c75e6aa506628d5354593116901",
    "vout": 0,
    "address": "mtnP9JTzSRozPE9MhY6nF67BppeQcJmGka",
    "scriptPubKey": "21034ad40bc2a7ffa13a0bfc72ba3d19fa5fb55d05163e90eabe97794633be885432ac",
    "amount": 50.00000000,
    "confirmations": 101,
    "spendable": true
  }, 
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 0,
    "address": "muPwQz5bLqtVWYyTHTLwR9fAMbLz1PbQ5q",
    "scriptPubKey": "76a914983e1d3e7e2b8199b214bf678451341de20fc3e988ac",
    "amount": 39.99996160,
    "confirmations": 1,
    "spendable": true
  }, 
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 1,
    "address": "mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya",
    "account": "",
    "scriptPubKey": "76a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac",
    "amount": 10.00000000,
    "confirmations": 1,
    "spendable": true
  }
]

Havaitsemme, että tekemämme transaktio (a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8) on nyt saanut yhden varmennuksen. Listaan on myös yllättäen ilmaantunut 101 varmennusta omaava transaktio, joka on tuonut lompakkoon 50 uutta bitcoinia. Tämä on yksi niistä 101 lohkosta, jotka alussa luotiin. Nyt on tullut riittävä määrä varmennuksia, jotta lohkopalkkio voitiin lopulta maksaa lompakkoon.

Katsotaan sen bitcoin-osoitteen yksityisavain, johon juuri saapunut lohkopalkkio ilmaantui.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest dumpprivkey mtnP9JTzSRozPE9MhY6nF67BppeQcJmGka
cRVfEAYKFcfSx3HqiEutdWdxWkk1gWSMnTFMrrLaFrmpX1CaEGcQ

Tätä yksityisavainta tarvitaan, kun bitcore-kirjaston avulla JavaScript-konsolissa luomme uuden transaktion, jonka sarjallistamme.

Luodaan yksityisavain, jonka juuri äsken Bitcoin Knotsin lompakosta löysimme.

var yksityisAvain = new bitcore.PrivateKey('cRVfEAYKFcfSx3HqiEutdWdxWkk1gWSMnTFMrrLaFrmpX1CaEGcQ');

Luodaan JSON-tyylinen tietorakenne, johon asetamme ne tiedot, jotka liittyvät Bitcoin Knotsin ilmoittamaan transaktioon (bf9ae40e0e5f758822fa304a1715f99a766b6c75e6aa506628d5354593116901).

var utxo = {
  "txId" : "bf9ae40e0e5f758822fa304a1715f99a766b6c75e6aa506628d5354593116901",
  "outputIndex" : 0,
  "address" : "mtnP9JTzSRozPE9MhY6nF67BppeQcJmGka",
  "script" : "21034ad40bc2a7ffa13a0bfc72ba3d19fa5fb55d05163e90eabe97794633be885432ac",
  "satoshis" : 5000000000
};

Luodaan tästä nyt uusi transaktio, joka lähettää bitcoinit (5 miljardia satoshia) osoitteeseen, jossa jo ennestään on tasan 10 bitcoinia.

var transaktio = new bitcore.Transaction()
  .from(utxo)
  .to('mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya', 5000000000)
  .sign(yksityisAvain);

Transaktio pitää vielä sarjallistaa serialize-funktiolla.

transaktio.serialize();

Nyt saimmekin mielenkiintoisen virheilmoituksen, jonka mukaan (siirto)kulu on liian pieni! Siirtokulu on liian pieni!

Luodaan uusi transaktio, jossa asetetaan siirtokuluiksi 5430 satoshia! Vähennetään samalla vastaavalla määrällä varsinaiseen kohteeseen menevien satoshien määrää.

var transaktio = new bitcore.Transaction()
  .from(utxo)
  .fee(5430)
  .to('mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya', 4999994570)
  .sign(yksityisAvain);

Kokeillaan sarjallistaa tämä uusi transaktio, jossa on siirtokulu mukana.

transaktio.serialize(); // "0100000001016911934535d5286650aae6756c6b769af915174a30fa2288755f0e0ee49abf000000004948304502210080e91008f7ec0e301fe3c55e0088417d502f4b0a229397bc389e2aa19f83ba3c02202959e478ccf2c0f515e5b23cf9fdd3585cf9e8d039b800553ca665b53615146501ffffffff01cadc052a010000001976a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac00000000"

Tällä kertaa ei tullut virheilmoitusta, vaan erittäin pitkä heksadesimaali, joka on siis raakaversio transaktiosta. Siirretään nyt sendrawtransaction-komennolla 50 bitcoinia toiseen osoitteeseen tämän pitkän heksadesimaalin avulla.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest sendrawtransaction 0100000001016911934535d5286650aae6756c6b769af915174a30fa2288755f0e0ee49abf000000004948304502210080e91008f7ec0e301fe3c55e0088417d502f4b0a229397bc389e2aa19f83ba3c02202959e478ccf2c0f515e5b23cf9fdd3585cf9e8d039b800553ca665b53615146501ffffffff01cadc052a010000001976a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac00000000
500a35cec11b6f310d316e0766629a20fb44603ca5d2e49f04c1e781499962fd

RPC-komento listunspent ilman parametria näyttää vain käyttämättömät ulostulot transaktioille, joilla on ainakin yksi varmennus.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent
[
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 0,
    "address": "muPwQz5bLqtVWYyTHTLwR9fAMbLz1PbQ5q",
    "scriptPubKey": "76a914983e1d3e7e2b8199b214bf678451341de20fc3e988ac",
    "amount": 39.99996160,
    "confirmations": 1,
    "spendable": true
  }, 
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 1,
    "address": "mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya",
    "account": "",
    "scriptPubKey": "76a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac",
    "amount": 10.00000000,
    "confirmations": 1,
    "spendable": true
  }
]

Generoidaan taas yksi lohko lisää.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest generate 1
[
  "2d4036f383005b33c98ea23212e92805bef1e95897e125a8394048f7ee90b135"
]

Katsotaan, millaisia käyttämättämiä ulostuloja varmennuksia omaaville transaktioille nyt näkyy.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest listunspent
[
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 0,
    "address": "muPwQz5bLqtVWYyTHTLwR9fAMbLz1PbQ5q",
    "scriptPubKey": "76a914983e1d3e7e2b8199b214bf678451341de20fc3e988ac",
    "amount": 39.99996160,
    "confirmations": 2,
    "spendable": true
  }, 
  {
    "txid": "a5986ed9ae1bdba9ca7b9786e5260b8dff95719ab0cea70aa130ff2c7cfd1aa8",
    "vout": 1,
    "address": "mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya",
    "account": "",
    "scriptPubKey": "76a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac",
    "amount": 10.00000000,
    "confirmations": 2,
    "spendable": true
  }, 
  {
    "txid": "68b213699aaf2149200af6f8a7241404b5937af15268660b074d49ce4a989cd2",
    "vout": 0,
    "address": "mtnP9JTzSRozPE9MhY6nF67BppeQcJmGka",
    "scriptPubKey": "21034ad40bc2a7ffa13a0bfc72ba3d19fa5fb55d05163e90eabe97794633be885432ac",
    "amount": 50.00000000,
    "confirmations": 101,
    "spendable": true
  }, 
  {
    "txid": "500a35cec11b6f310d316e0766629a20fb44603ca5d2e49f04c1e781499962fd",
    "vout": 0,
    "address": "mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya",
    "account": "",
    "scriptPubKey": "76a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac",
    "amount": 49.99994570,
    "confirmations": 1,
    "spendable": true
  }
]

Onnistui! 49,99994570 bitcoinia on nyt mennyt osoitteeseen mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya. Tällä transaktiolla on 1 varmennus. Lisäksi samalla osoitteella on 2 varmennusta sisältävä transaktio, joka sisältää tasan 10 bitcoinia. Siispä yhteensä kyseisessä osoitteessa on 59,99994570 bitcoinia!

Katsotaan vielä, mitä decoderawtransaction-komento kertoo transaktion raakaversiosta.

henri@henri-VirtualBox:~/bitcoinknots/bitcoin-0.12.0/bin$ ./bitcoin-cli -regtest decoderawtransaction 0100000001016911934535d5286650aae6756c6b769af915174a30fa2288755f0e0ee49abf000000004948304502210080e91008f7ec0e301fe3c55e0088417d502f4b0a229397bc389e2aa19f83ba3c02202959e478ccf2c0f515e5b23cf9fdd3585cf9e8d039b800553ca665b53615146501ffffffff01cadc052a010000001976a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac00000000
{
  "txid": "500a35cec11b6f310d316e0766629a20fb44603ca5d2e49f04c1e781499962fd",
  "size": 158,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "bf9ae40e0e5f758822fa304a1715f99a766b6c75e6aa506628d5354593116901",
      "vout": 0,
      "scriptSig": {
        "asm": "304502210080e91008f7ec0e301fe3c55e0088417d502f4b0a229397bc389e2aa19f83ba3c02202959e478ccf2c0f515e5b23cf9fdd3585cf9e8d039b800553ca665b536151465[ALL]",
        "hex": "48304502210080e91008f7ec0e301fe3c55e0088417d502f4b0a229397bc389e2aa19f83ba3c02202959e478ccf2c0f515e5b23cf9fdd3585cf9e8d039b800553ca665b53615146501"
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 49.99994570,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 1bef7052aa60c191bb5cc318af55986ba6634c48 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9141bef7052aa60c191bb5cc318af55986ba6634c4888ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mi4fNjJnGxKfhhGsgKMMYRTZoWoPKBLkya"
        ]
      }
    }
  ]
}

Yksi kahdesta -moniallekirjoitus (1-of-2 multisignature)

var yksityinenAvain1 = new bitcore.PrivateKey();
console.log(yksityinenAvain1.toString());
var yksityinenAvain2 = new bitcore.PrivateKey();
console.log(yksityinenAvain2.toString());
var julkinenAvain1 = new bitcore.PublicKey(yksityinenAvain1);
console.log(julkinenAvain1.toString());
var julkinenAvain2 = new bitcore.PublicKey(yksityinenAvain2);
console.log(julkinenAvain2.toString());
var p2shOsoite = new bitcore.Address([julkinenAvain1, julkinenAvain2], 1);
console.log(p2shOsoite.toString()); // 3PLow...

Sanastoa