Bitcoin per principianti, parte 3: API BitCoinJ

Per gli sviluppatori Java, BitCoinJ è un punto di ingresso per lo sviluppo di applicazioni che interagiscono con la rete Bitcoin. In questo articolo finale di una serie in tre parti, Dirk Merkel ti aiuta a configurare BitCoinJ in un ambiente di sviluppo Eclipse, quindi esamina diversi brevi esercizi che ti familiarizzeranno con questa implementazione leggera del protocollo di transazione Bitcoin.

Le puntate precedenti di questa serie in tre parti hanno introdotto il framework concettuale e tecnologico di Bitcoin, una valuta virtuale e una rete peer-to-peer. Questo articolo, un'introduzione tutorial all'API BitCoinJ, presuppone che tu abbia familiarità con gli indirizzi, le transazioni, i blocchi e la catena di blocchi Bitcoin.

BitCoinJ è un'implementazione Java open source del protocollo Bitcoin. In quanto tale, è uno strumento utile da avere se si desidera scrivere applicazioni Java che interagiscono con la rete Bitcoin. Per esplorare l'API BitCoinJ, costruiremo varie applicazioni di esempio che illustrano i passaggi di programmazione necessari per costruire applicazioni Bitcoin più complesse in Java. Dopo aver utilizzato Maven per creare e impostare un progetto nell'IDE Eclipse, faremo pratica con la creazione di un indirizzo Bitcoin, l'archiviazione in un portafoglio e il salvataggio del portafoglio su disco. Stabiliremo quindi una connessione alla rete di test Bitcoin e recupereremo il suo blocco di genesi. Infine, collegheremo insieme il nostro codice di esempio finora inviando alcuni Bitcoin a un indirizzo sulla rete di test.

Informazioni su BitCoinJ

BitCoinJ è un'implementazione Java del protocollo Bitcoin. Scritto da Mike Hearn, BitCoinJ non è un'implementazione completa del client Bitcoin originale, ma una versione più leggera e accessibile. Sebbene sia abbastanza solido da cui imparare, BitCoinJ è ancora in fase di sviluppo (attualmente alla v.0.3) e non dovrebbe essere utilizzato per spostare un gran numero di Bitcoin.

Inizia con BitCoinJ

BitCoinJ è ospitato da Google Code in un repository Subversion e può essere estratto in modo anonimo. Una volta controllato il tronco del progetto BitCoinJ, sarai in grado di tenerlo facilmente aggiornato. Tuttavia, non sarai in grado di confermare alcuna modifica.

Puoi utilizzare il client Subversion integrato nel tuo IDE preferito o semplicemente controllare il progetto dalla riga di comando, come ho fatto io:

Una volta ottenuto il codice, lo compilerai con Maven, il sistema di compilazione di BitCoinJ. Maven adotta un approccio basato sul ciclo di vita dei progetti di costruzione ed è altamente estensibile con molti plug-in principali e di terze parti. Ciò che Maven fa estremamente bene è gestire le dipendenze. Se guardi il file Maven pom.xml nella directory principale di BitCoinJ, vedrai che utilizza solo una manciata di dipendenze; questi includono JUnit e EasyMock per i test di unità, SLF4J per la registrazione e le API Bouncy Castle Crypto per operazioni crittografiche come hashing e firma.

Dalla riga di comando, esegui mvn clean packagee Maven recupererà queste e altre dipendenze, compilerà il progetto, eseguirà la suite di unit test e impacchetterà il codice compilato in un file JAR di istantanea. Come mostrato nella Figura 2, Maven esegue prima il ciclo di vita pulito per eliminare eventuali artefatti dalle build precedenti. Quindi esegue le fasi del ciclo di vita predefinito fino alla fase del pacchetto inclusa.

Maven ha qualche asso nella manica in più. Innanzitutto, l'esecuzione mvn site:sitecrea la documentazione di BitCoinJ, comprese le pagine sulle dipendenze, il monitoraggio dei problemi, le mailing list, la licenza, il team di sviluppo, il repository dei sorgenti e altri. Queste pagine tendono ad essere informative ma di base. L'esecuzione mvn javadoc:javadocgenera la documentazione del progetto, che tornerà utile quando inizieremo a esercitare l'API BitCoinJ.

La documentazione rivela che l'API è divisa in quattro pacchetti:

  • Discovery si occupa di discovery / comunicazione di rete peer-to-peer.
  • Store contiene strutture di dati per l'archiviazione di blocchi e la catena di blocchi.
  • Gli esempi includono una manciata di semplici applicazioni basate su BitCoinJ (queste hanno ispirato i miei esempi per questo articolo).
  • Core contiene la maggior parte delle classi e delle funzionalità di BitCoinJ, incluse le classi per comunicare con i nodi peer, scaricare la catena di blocchi e inviare e ricevere transazioni.

Imposta il progetto di esempio in Eclipse

Svilupperemo il codice di esempio per questo articolo in Eclipse, utilizzando Maven per gestire BitCoinJ come dipendenza. Fortunatamente, BitCoinJ ha un ambiente di integrazione continua che crea il progetto, raccoglie e segnala vari artefatti e deposita un'istantanea JAR nel repository Maven basato su Nexus del progetto.

La Figura 3 mostra la finestra di dialogo di creazione del progetto Eclipse che risulta dalla creazione di un nuovo progetto Maven e dalla selezione dell'archetipo "quickstart", che genera un progetto Maven di base. Il mio codice per questo progetto risiede in un pacchetto denominato com.waferthin.bitcoinj, che produce un 0.0.1-SNAPSHOT con la build Maven.

Fare clic su Fine indica alla procedura guidata di creare il progetto, il che significa inserire una classe principale "Hello World" nella directory del progetto, denominata src/main/java/com/waferthin/bitcoinjnel mio caso.

Infine, dobbiamo dire a Maven che il progetto dipende dall'istantanea di BitCoinJ, come mostrato nel Listato 1. Ho modificato il file pom.xml generato dalla procedura guidata di Maven per dichiarare la posizione e il nome del repository Nexus di BitCoinJ (righe dalla 18 alla 28) e impostato la versione da cui dipendere per la compilazione (righe da 39 a 45):

Listato 1. Maven pom.xm per il progetto BitCoinJ

001| 002| 4.0.0 003| 004| com.waferthin.bitcoinj.explored 005| bitcoinj-explored 006| 0.0.1-SNAPSHOT 007| jar 008| 009| bitcoinj-explored 010| //maven.apache.org 011| 012|  013| UTF-8 014|  015| 016|  017|  018|  019| bitcoinj-release 020|  021| 022|//nexus.bitcoinj.org/content/repositories/releases 023|  024|  025| bitcoinj-snapshot 026|  027| //nexus.bitcoinj.org/content/repositories/snapshots 028|  029|  030| 031|  032|  033| junit 034| junit 035| 3.8.1 036| test 037|  038| 039|  040|  041| com.google 042| bitcoinj 043| 0.3-SNAPSHOT 044| compile 045|  046|  047|

È tutto quello che c'è da fare. Nella sezione successiva importeremo le classi BitCoinJ nel nostro codice e costruiremo un progetto BitCoinJ con Maven, il tutto senza dover copiare il file JAR effettivo.

Creazione di un indirizzo Bitcoin

Per inviare o ricevere Bitcoin, è necessario un indirizzo. Gli indirizzi derivano dalla parte pubblica di una coppia di chiavi crittografiche pubblico-privata (vedere "Bitcoin per principianti, Parte 2: Bitcoin come tecnologia e rete"). Il tipo di crittografia utilizzato da Bitcoin è chiamato crittografia a curva ellittica (ECC). La crittografia a chiave pubblica che molti di noi conoscono si basa sulla difficoltà di trovare i fattori primi di grandi numeri interi. Al contrario, ECC si basa sulla difficoltà di trovare il logaritmo discreto di una curva ellittica. (Spiegare questo in modo più dettagliato non solo ci porterebbe nella tana del coniglio dell'algebra superiore, ma supererebbe anche rapidamente i miei calcoli universitari. Fortunatamente, non abbiamo bisogno di saperne di più per utilizzare la ECKeyclasse di BitCoinJ per rappresentare e generare la chiave coppie.)

Nella riga 20 del Listato 2, creiamo una nuova coppia di chiavi della curva ellittica istanziando un oggetto di tipo ECKey. Nota che il toString()metodo predefinito della classe viene sovrascritto per restituire la chiave pubblica e privata in notazione esadecimale, che viene utilizzata nella riga 23.

Listato 2. Creazione di una coppia di chiavi della curva ellittica con ECKey

001|package com.waferthin.bitcoinj; 002| 003|import com.google.bitcoin.core.ECKey; 004|import com.google.bitcoin.core.NetworkParameters; 005|import com.google.bitcoin.core.Address; 006| 007|public class CreateAddress  008

You might recall that the public part of a Bitcoin key pair should be an address. But the public part of the key generated by the above code will initially look nothing like the addresses the Bitcoin client displays in its UI. The address form we're used to seeing in a Bitcoin transaction is derived by repeated hash operations to the public key. This form includes a flag that indicates which of the two Bitcoin networks the key belongs to -- Bitcoin's production network or its test network. (See the Bitcoin wiki page for a more detailed description of the algorithmic creation of Bitcoin key pairs.)

Differentiating Bitcoin networks

Currently there are two Bitcoin networks, one for production and one that is used for development. Both networks have their own genesis block and subsequent block chain. Later in this article, we'll use the Bitcoin testnet to execute a Bitcoin transaction. For now, you only need to know that the networks are differentiated by pre-pending a single byte to the input to one of the cryptographic hashes in the ECC algorithm: 0x6f indicates the production network and 0x00 the test one.

We don't need to apply the sequence of cryptographic hashes ourselves because the ECKey class provides the same functionality with the toAddress() method. After invoking that method and passing in the type of network via a NetworkParameters object (see line 26 in Listing 2), the toAddress() method returns an Address object. That object's toString() method will yield a true Bitcoin address. After compiling and executing the class I get the following address for Bitcoin's test network:

mpJ9UDd4qtNhMiGefK8NM1V5PMq9jMb7ck

Testnet addresses typically start with m or n, whereas production addresses start with 1. Try executing the same code on your own machine and you will get a different, unique address.

Wallets and keys

If you participate in the Bitcoin economy, you likely keep all of your riches in your wallet. The wallet is nothing more than a local data file that contains serialized objects representing all of your Bitcoin transactions and a cache of unused addresses. The sum of your incoming and outgoing transaction amounts is the amount of Bitcoins in your wallet. In this section we'll use BitCoinJ's Wallet object to create a wallet data file, populate it with five addresses, and save it to disk.

The Wallet class implements the Serializable interface to enable us to persist it to disk or some other more permanent storage medium. Specifically, methods loadFromFile(File) and the corresponding saveToFile(File) read and write wallet files. We'll be using loadFromFile(File) to write a newly created wallet object to a file.

Note that BitCoinJ wallet files are not compatible with wallet files created by the official Bitcoin client.

Creating and storing keys

La Walletclasse ha un membro pubblico di nome keychainche è una ArrayListdi tipo ECKey, che viene utilizzato per memorizzare tutte le coppie di chiavi CE nel portafoglio. Il addKey(ECKey)metodo viene utilizzato per aggiungere coppie di chiavi, ma attualmente non esiste alcun metodo per rimuoverle. Questo ha senso perché non dovrebbe essere facile per utenti o programmi eliminare le chiavi private: è necessaria una chiave privata per accedere ai fondi inviati tramite la corrispondente chiave pubblica. Senza una coppia di chiavi nel portafoglio o con un backup da qualche parte, i fondi inviati andrebbero persi per sempre.