piuk.MyBlockChain.java Source code

Java tutorial

Introduction

Here is the source code for piuk.MyBlockChain.java

Source

/*
 * Copyright 2011-2012 the original author or authors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package piuk;

import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;

import org.json.simple.JSONValue;

import piuk.blockchain.android.Constants;

import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import com.google.bitcoin.core.AbstractWalletEventListener;
import com.google.bitcoin.core.Block;
import com.google.bitcoin.core.BlockChain;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.PeerEventListener;
import com.google.bitcoin.core.ProtocolException;
import com.google.bitcoin.core.Sha256Hash;
import com.google.bitcoin.core.StoredBlock;
import com.google.bitcoin.core.TransactionInput;
import com.google.bitcoin.core.TransactionOutput;
import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.core.WalletEventListener;
import com.google.bitcoin.core.WalletTransaction;
import com.google.bitcoin.store.BlockStoreException;
import com.google.bitcoin.store.MemoryBlockStore;

import de.roderick.weberknecht.WebSocketConnection;
import de.roderick.weberknecht.WebSocketEventHandler;
import de.roderick.weberknecht.WebSocketException;
import de.roderick.weberknecht.WebSocketMessage;

public class MyBlockChain extends BlockChain implements WebSocketEventHandler {
    final String URL = "ws://api.blockchain.info:8335/inv";
    int nfailures = 0;
    WebSocketConnection _websocket;
    MyRemoteWallet remoteWallet;
    StoredBlock latestBlock;
    boolean isConnected = false;
    boolean isRunning = true;

    public MyRemoteWallet getRemoteWallet() {
        return remoteWallet;
    }

    public static class MyBlock extends Block {
        private static final long serialVersionUID = 1L;

        long time;
        Sha256Hash hash;
        int blockIndex;

        public MyBlock(NetworkParameters params) throws ProtocolException {
            super(params);
        }

        @Override
        public long getTimeSeconds() {
            return time;
        }

        @Override
        public Sha256Hash getHash() {
            return hash;
        }
    }

    public Set<PeerEventListener> listeners = new ConcurrentSkipListSet<PeerEventListener>();

    @Override
    public int getBestChainHeight() {
        return getChainHead().getHeight();
    }

    @Override
    public StoredBlock getChainHead() {
        if (latestBlock != null) {
            return latestBlock;
        } else {
            return remoteWallet._multiAddrBlock;
        }
    }

    final private WalletEventListener walletEventListener = new AbstractWalletEventListener() {
        @Override
        public void onChange(Wallet wallet) {
            try {
                if (remoteWallet._multiAddrBlock != null) {

                    if (latestBlock == null || latestBlock.getHeight() < remoteWallet._multiAddrBlock.getHeight()) {
                        latestBlock = remoteWallet._multiAddrBlock;

                        for (PeerEventListener listener : listeners) {
                            listener.onBlocksDownloaded(null, latestBlock.getHeader(), 0);
                        }
                    }
                }

                if (isRunning) {
                    start();
                } else if (isConnected()) {
                    //Disconnect and reconnect
                    //To resubscribe
                    subscribe();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    public MyBlockChain(NetworkParameters params, MyRemoteWallet remoteWallet)
            throws BlockStoreException, WebSocketException, URISyntaxException {
        super(params, remoteWallet.getBitcoinJWallet(), new MemoryBlockStore(params));

        this._websocket = new WebSocketConnection(new URI(URL));

        this._websocket.setEventHandler(this);

        this.remoteWallet = remoteWallet;
    }

    public synchronized void subscribe() {
        try {
            String message = "{\"op\":\"blocks_sub\"}{\"op\":\"wallet_sub\",\"guid\":\"" + remoteWallet.getGUID()
                    + "\"}";

            for (Map<String, Object> key : this.remoteWallet.getKeysMap()) {
                message += "{\"op\":\"addr_sub\", \"addr\":\"" + key.get("addr") + "\"}";
            }

            _websocket.send(message);

        } catch (WebSocketException e) {
            e.printStackTrace();
        }
    }

    public void removePeerEventListener(PeerEventListener listener) {
        listeners.remove(listener);
    }

    public void addPeerEventListener(PeerEventListener listener) {
        listeners.add(listener);
    }

    public void onClose() {
        System.out.println("onClose()");

        this.isConnected = false;

        if (!isRunning)
            return;

        for (PeerEventListener listener : listeners) {
            listener.onPeerConnected(null, 0);
        }

        if (nfailures < 5) {
            try {
                ++nfailures;

                _websocket.connect();
            } catch (WebSocketException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isConnected() {
        return this.isConnected;
    }

    public void stop() {
        System.out.println("start()");

        try {
            this.isRunning = false;

            _websocket.close();
        } catch (WebSocketException e) {
            e.printStackTrace();
        }

        remoteWallet.getBitcoinJWallet().removeEventListener(walletEventListener);
    }

    public void start() {
        this.isRunning = true;

        System.out.println("start()");

        try {
            _websocket.connect();
        } catch (WebSocketException e) {
            e.printStackTrace();
        }

        remoteWallet.getBitcoinJWallet().addEventListener(walletEventListener);
    }

    @SuppressWarnings("unchecked")
    public void onMessage(WebSocketMessage wmessage) {

        System.out.println("OnMessage()");

        try {
            String message = wmessage.getText();

            System.out.println("Websocket() onMessage() " + message);

            Map<String, Object> top = (Map<String, Object>) JSONValue.parse(message);

            if (top == null)
                return;

            String op = (String) top.get("op");

            if (op.equals("block")) {
                Map<String, Object> x = (Map<String, Object>) top.get("x");

                Sha256Hash hash = new Sha256Hash(Hex.decode((String) x.get("hash")));
                int blockIndex = ((Number) x.get("blockIndex")).intValue();
                int blockHeight = ((Number) x.get("height")).intValue();
                long time = ((Number) x.get("time")).longValue();

                MyBlock block = new MyBlock(Constants.NETWORK_PARAMETERS);
                block.hash = hash;
                block.blockIndex = blockIndex;
                block.time = time;

                this.latestBlock = new StoredBlock(block, BigInteger.ZERO, blockHeight);

                List<MyTransaction> transactions = remoteWallet.getMyTransactions();
                List<Number> txIndexes = (List<Number>) x.get("txIndexes");
                for (Number txIndex : txIndexes) {
                    for (MyTransaction tx : transactions) {

                        MyTransactionConfidence confidence = (MyTransactionConfidence) tx.getConfidence();

                        if (tx.txIndex == txIndex.intValue() && confidence.height != blockHeight) {
                            confidence.height = blockHeight;
                            confidence.runListeners();
                        }
                    }
                }

                for (PeerEventListener listener : listeners) {
                    listener.onBlocksDownloaded(null, block, 0);
                }

            } else if (op.equals("utx")) {
                Map<String, Object> x = (Map<String, Object>) top.get("x");

                WalletTransaction tx = MyTransaction.fromJSONDict(x);

                BigInteger result = BigInteger.ZERO;

                BigInteger previousBalance = remoteWallet.getBitcoinJWallet().final_balance;

                for (TransactionInput input : tx.getTransaction().getInputs()) {
                    //if the input is from me subtract the value
                    MyTransactionInput myinput = (MyTransactionInput) input;

                    if (remoteWallet.isAddressMine(input.getFromAddress().toString())) {
                        result = result.subtract(myinput.value);

                        remoteWallet
                                .getBitcoinJWallet().final_balance = remoteWallet.getBitcoinJWallet().final_balance
                                        .subtract(myinput.value);
                        remoteWallet.getBitcoinJWallet().total_sent = remoteWallet.getBitcoinJWallet().total_sent
                                .add(myinput.value);
                    }
                }

                for (TransactionOutput output : tx.getTransaction().getOutputs()) {
                    //if the input is from me subtract the value
                    MyTransactionOutput myoutput = (MyTransactionOutput) output;

                    if (remoteWallet.isAddressMine(myoutput.getToAddress().toString())) {
                        result = result.add(myoutput.getValue());

                        remoteWallet
                                .getBitcoinJWallet().final_balance = remoteWallet.getBitcoinJWallet().final_balance
                                        .add(myoutput.getValue());
                        remoteWallet
                                .getBitcoinJWallet().total_received = remoteWallet.getBitcoinJWallet().total_sent
                                        .add(myoutput.getValue());
                    }
                }

                MyTransaction mytx = (MyTransaction) tx.getTransaction();

                mytx.result = result;

                remoteWallet.getBitcoinJWallet().addWalletTransaction(tx);

                if (result.compareTo(BigInteger.ZERO) >= 0) {
                    System.out.println("On Received");

                    remoteWallet.getBitcoinJWallet().invokeOnCoinsReceived(tx.getTransaction(), previousBalance,
                            remoteWallet.getBitcoinJWallet().final_balance);
                } else {
                    remoteWallet.getBitcoinJWallet().invokeOnCoinsSent(tx.getTransaction(), previousBalance,
                            remoteWallet.getBitcoinJWallet().final_balance);
                }
            } else if (op.equals("on_change")) {
                String newChecksum = (String) top.get("checksum");
                String oldChecksum = remoteWallet.getChecksum();

                System.out.println("On change " + newChecksum + " " + oldChecksum);

                if (!newChecksum.equals(oldChecksum)) {
                    try {
                        String newPayload = MyRemoteWallet.getWalletPayload(remoteWallet.getGUID(),
                                remoteWallet.getSharedKey(), oldChecksum);

                        if (newPayload == null)
                            return;

                        remoteWallet.setPayload(newPayload);

                        remoteWallet.getBitcoinJWallet().invokeOnChange();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void onOpen() {
        System.out.println("onOpen()");

        this.isConnected = true;

        subscribe();

        for (PeerEventListener listener : listeners) {
            listener.onPeerConnected(null, 1);
        }

        nfailures = 0;
    }
}