Full Code of phishman3579/Bitcoin for AI

master 4ed6be50a2a6 cached
33 files
159.6 KB
34.3k tokens
266 symbols
1 requests
Download .txt
Repository: phishman3579/Bitcoin
Branch: master
Commit: 4ed6be50a2a6
Files: 33
Total size: 159.6 KB

Directory structure:
gitextract_1ggmfz6a/

├── .classpath
├── .gitignore
├── .project
├── LICENSE
├── README.md
└── src/
    └── com/
        └── jwetherell/
            └── bitcoin/
                ├── Blockchain.java
                ├── Peer.java
                ├── ProofOfWork.java
                ├── Wallet.java
                ├── common/
                │   ├── Constants.java
                │   ├── HashUtils.java
                │   └── KeyUtils.java
                ├── data_model/
                │   ├── Block.java
                │   ├── Data.java
                │   └── Transaction.java
                ├── interfaces/
                │   ├── MessageListener.java
                │   ├── Receiver.java
                │   └── Sender.java
                ├── networking/
                │   ├── Multicast.java
                │   ├── TCP.java
                │   └── UDP.java
                └── test/
                    ├── AllTest.java
                    ├── BlockChainTest.java
                    ├── BlockTest.java
                    ├── DataTest.java
                    ├── EncodeDecodeTest.java
                    ├── MulticastTest.java
                    ├── PeerTest.java
                    ├── ProofOfWorkTest.java
                    ├── TCPTest.java
                    ├── TransactionTest.java
                    ├── UDPTest.java
                    └── WalletTest.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .classpath
================================================
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
	<classpathentry kind="src" path="src"/>
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
	<classpathentry kind="output" path="bin"/>
</classpath>


================================================
FILE: .gitignore
================================================
/bin/


================================================
FILE: .project
================================================
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
	<name>Bitcoin</name>
	<comment></comment>
	<projects>
	</projects>
	<buildSpec>
		<buildCommand>
			<name>org.eclipse.jdt.core.javabuilder</name>
			<arguments>
			</arguments>
		</buildCommand>
	</buildSpec>
	<natures>
		<nature>org.eclipse.jdt.core.javanature</nature>
	</natures>
</projectDescription>


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



================================================
FILE: README.md
================================================
# Bitcoin
An example Bitcoin implementation which can be used to learn about Bitcoin/Blockchain. This implementations is for educational use only.

# Overview.

## Wallet

The Wallet is how peers interact with the Bitcoin peer-to-peer network. The Wallet generates a public key and a private key which it uses to sign each Transaction. The pulic key is the send-to address used by the Bitcoin network. Each Wallet has the ability to send coins from your account to another account and it also has the ability to confirm Transactions (except it's own) which it receives from the Bitcoin peer-to-peer network.

```
    Wallet {
      sendCoin(entity, value); // Creates a new Transaction
      handleTransaction(Transaction); // Receives a unconfirmed Transaction
      handleConfirmation(Transaction); // Receives a confirmed Transaction and adds to blockchain
    }
```

## Transaction

Transactions are just a collection of input transactions, output transactions, a value, and a signature. 

```
    Transaction {
        byte[] header;
        Transaction[] inputs;
        Transaction[] outputs;
        long value;
        byte[] signature;
    }
```

See the [Transaction Class](https://github.com/phishman3579/Bitcoin/blob/master/src/com/jwetherell/bitcoin/data_model/Transaction.java) for reference.

#### The Wallet also has a number of Transaction rules:

* Once a Transaction has been used as an input, it cannot be used again. 
* All inputs on a Transaction have to be completely consumed on a transaction.

Note: To send a Bitcoin transaction, you have to already own a Bitcoin. Getting an initial Bitcoin is usually done by trading something for a number of Bitcoins. One caveat of, having to own a Bitcoin to make a transaction, is the first transaction. The first transaction is called the genesis transaction, it is the only transaction which does not need input transactions.

### An example Transaction

If Justin wants to send 6 coins to George:

Ledger:

|  Justin's unused Transactions  |  George's unused Transaction  |
|  ----------------------------- | ----------------------------- | 
| Transaction #1 : 5 Coins       |                               |
| Transaction #2 : 3 Coins       |                               |
| Transaction #3 : 7 Coins       |                               |

```
    Aggregate Transaction #4 {
      byte[]        header      "6 coins for George and 2 coins to Justin"
      Transaction[] input       { Transaction #1, Transaction #2 }
      Transaction[] output      { Transaction #5, Transaction #6 }
      int           value       0
      byte[]        signature   "Justin's signature based on the Header"
    }
```
Note: The 'value' on the Aggregate Transaction (#4) is a reward for anyone who confirms the Transaction. The higher the reward, the better chance the Transaction will be processed quicker.

```
    Transaction #5 {
      byte[]        header      "2 coins to Justin"
      Transaction[] input       { Transaction #1, Transaction #2 }
      Transaction[] output      { }
      int           value       2
      byte[]        signature   "Justin's signature based on the Header"
    }

    Transaction #6 {
      byte[]        header      "6 coins for George"
      Transaction[] input       { Transaction #1, Transaction #2 }
      Transaction[] output      { }
      int           value       6
      byte[]        signature   "Justin's signature based on the Header"
    }
```

The Aggregate Transaction (#4) will remove Transaction #1 and #2 from Justin's unused Transactions. Since the total of all inputs is 8 coins, which is 2 more than what Justin wants to send to George, the output will contain a Transaction which sends 2 coins back to Justin.

The Wallet will use it's private key to sign the Header of the Aggregate Transactions (#4) and it will also sign each of the output Transactions (#5 & #6). It will then send Transaction #4 to the Bitcoin network for confirmation. 

Each peer on the Bitcoin network will receive the Transaction and try to confirm it. 

To confirm a Transaction, a Peer will:
* Check the Signature of Transaction against the public key of the sender. 

If it passes:
* Send the confirmed Transaction to the Bitcoin network.

## Block

The confirmed Transaction (#4) is added to a pool of confirmed Transactions. Peers (also called Miners) will gather confirmed Transactions from the pool and put them into a Block. A Block contains a number of confirmed Transactions, the Miner's signature, and a couple of other fields used for "Proof of work" processing.

```
    Block {
      Transaction[]     transactions
      int               nonce
      int               zeros
      byte[]            previousHash
      byte[]            nextHash
      byte[]            signature
    }
```

See the [Block Class](https://github.com/phishman3579/Bitcoin/blob/master/src/com/jwetherell/bitcoin/data_model/Block.java) for reference.

Miners will create a single 'block hash' from all the confirmed Transactions in the Block. They will then go through the process of "Proof of work". The goal of the "Proof of work" is to create a hash which begins with a random number of zeros (see the 'zeros' field). "Proof of work" is designed to be processor intensive which adds randomness to the time it takes to process a Block. A Miner will take the 'block hash' and append a random integer (called a 'nonce') to it. It will then create a new hash from 'block hash + nonce' and see if it satisfies the "Proof of work", this process will repeat until it finds a 'nonce' which satisfies the "Proof of work"

See the [Proof of work](https://github.com/phishman3579/Bitcoin/blob/master/src/com/jwetherell/bitcoin/ProofOfWork.java) for reference.

Once a Miner finds a 'nonce' which satisfies the "Proof of work", it will:
* Create another hash (see 'nextHash') using the Blockchain's current hash (see 'previousHash') and the 'block hash' 
* Send the Block to the Bitcoin network.

```
    Block #1 {
      Transaction[]     transactions    { Transaction #4 }
      int               nonce           453;
      int               zeros           3;
      byte[]            previousHash    "Blockchain hash #1";
      byte[]            nextHash        "Blockchain hash #2";
      byte[]            signature       "Miner's signature";
    }
```
Peers on the Bitcoin network will receive the Block and start confirming it. 

To confirm the Block, A Peer will:
* Make sure the 'nonce' satisfies the "Proof of work"
* Check the Block's signature 
* Check the signature of each Trasaction in the Block.

If everything passes:
* Add the block to it's Blockchain.
* Send the confirmed Block to the Bitcoin network

## Blockchain

The Blockchain is a simple structure which contains a list of confirmed Blocks, a list of Transactions in chronological order, a list of unused Transactions, and the current hash.

Note: all transactions in the same block are said to have happened at the same time.

```
    Blockchain {
        List<Block>         blockchain
        List<Transactions>  transactions
        List<Transaction>   unused
        byte[]              currentHash
    }
```

See the [Blockchain](https://github.com/phishman3579/Bitcoin/blob/master/src/com/jwetherell/bitcoin/BlockChain.java) for reference.


When the Peer adds the Block to the Blockchain, the Blockchain will:
* Check to see if the 'previousHash' from the Block matches it's 'currentHash', 
* Check to see if the input Transactions from all the Transactions in the Block are 'unused'

If everything passes:
* The Block is added to the 'blockChain'
* The Transaction is added to the 'transactions' list
* All 'input' transactions are removed from the 'unused' list
* All the 'output' transactions are added to the 'unused' list
* The 'currentHash' is updated to 'nextHash' from the current Block.

```
    Blockchain {
        List<Block>         blockchain      { Block #0 }
        List<Transactions>  transactions    { Transaction #0 }
        List<Transaction>   unused          { Transaction #1, Transaction #2, Transaction #3 }
        byte[]              currentHash     "Blockchain hash #1"
    }
```

Updated Blockchain.

```
    Blockchain {
        List<Block>         blockchain      { Block #0, Block #1 };
        List<Transactions>  transactions    { Transaction #0, Transaction #4 }
        List<Transaction>   unused          { Transaction #3, Transaction #5, Transaction #6 }
        byte[]              currentHash     "Blockchain hash #2"
    }
```

Ledger:

|  Justin's unused Transactions  |  George's unused Transaction  |
|  ----------------------------- | ----------------------------- | 
| Transaction #3 : 7 Coins       | Transaction #6 : 6 Coins      |
| Transaction #5 : 2 Coins       |                               |
|                                |                               |

Based off of [1](http://www.michaelnielsen.org/ddi/how-the-bitcoin-protocol-actually-works/) and [2](http://www.imponderablethings.com/2013/07/how-bitcoin-works-under-hood.html)

Also see the [original paper](https://bitcoin.org/bitcoin.pdf)


================================================
FILE: src/com/jwetherell/bitcoin/Blockchain.java
================================================
package com.jwetherell.bitcoin;

import java.nio.ByteBuffer;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import com.jwetherell.bitcoin.common.Constants;
import com.jwetherell.bitcoin.common.HashUtils;
import com.jwetherell.bitcoin.data_model.Block;
import com.jwetherell.bitcoin.data_model.Transaction;

public class Blockchain {

    public static final String              NO_ONE              = "no one";
    public static final Signature           NO_ONE_SIGNATURE;
    public static final byte[]              NO_ONE_PUB_KEY;
    static {
        try {
            final KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA", "SUN");
            final SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
            gen.initialize(512, random);

            final KeyPair pair = gen.generateKeyPair();
            final PrivateKey privateKey = pair.getPrivate();
            NO_ONE_SIGNATURE = Signature.getInstance("SHA1withDSA", "SUN");
            NO_ONE_SIGNATURE.initSign(privateKey);

            final PublicKey publicKey = pair.getPublic();
            NO_ONE_PUB_KEY = publicKey.getEncoded();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static final String              GENESIS_NAME        = "genesis";

    protected static final boolean          DEBUG               = Boolean.getBoolean("debug");

    private static final byte[]             INITIAL_HASH        = new byte[0];

    private static final Transaction        GENESIS_TRANS;
    private static final Block              GENESIS_BLOCK;
    static {
        // To start the blockchain, we have an initial transaction which has no inputs and one output which
        // is given to the genesis entity. This is the only transaction which has no inputs.
        final Transaction[] empty = new Transaction[0];
        final Transaction[] output = new Transaction[1];
        final String outputMsg = "Genesis gets 50 coins.";
        output[0] = Transaction.newSignedTransaction(NO_ONE_SIGNATURE, NO_ONE, GENESIS_NAME, outputMsg, 50, empty, empty);

        final String msg = "Genesis transfer.";
        GENESIS_TRANS = Transaction.newSignedTransaction(NO_ONE_SIGNATURE, NO_ONE, GENESIS_NAME, msg, 0, empty, output);

        final ByteBuffer buffer = ByteBuffer.allocate(GENESIS_TRANS.getBufferLength());
        GENESIS_TRANS.toBuffer(buffer);
        buffer.flip();

        final byte[] bytes = buffer.array();
        final byte[] nextHash = Blockchain.getNextHash(Blockchain.INITIAL_HASH, bytes);

        final Transaction[] trans = new Transaction[]{ GENESIS_TRANS };
        GENESIS_BLOCK = new Block(NO_ONE, Blockchain.INITIAL_HASH, nextHash, trans, 0);
        GENESIS_BLOCK.confirmed = true;
    }

    private final List<Block>               blockchain          = new CopyOnWriteArrayList<Block>();
    private final List<Transaction>         transactions        = new CopyOnWriteArrayList<Transaction>();
    private final List<Transaction>         unused              = new CopyOnWriteArrayList<Transaction>();

    private final String                    owner;

    private volatile byte[]                 latestHash          = INITIAL_HASH;

    public Blockchain(String owner) {
        this.owner = owner;
        // transfer initial coins to genesis entity
        this.addBlock(GENESIS_NAME, GENESIS_BLOCK);
    }

    public int getLength() {
        return blockchain.size();
    }

    public Block getBlock(int blockNumber) {
        if (blockNumber>=blockchain.size())
            return null;
        return blockchain.get(blockNumber);
    }

    public List<Transaction> getUnused() {
        return unused;
    }

    public Block getNextBlock(String name, Transaction[] transactions) {
        int length = 0;
        for (Transaction transaction : transactions)
            length += transaction.getBufferLength();
        final byte[] bytes = new byte[length];
        final ByteBuffer bb = ByteBuffer.wrap(bytes);

        for (Transaction transaction : transactions) {
            final ByteBuffer buffer = ByteBuffer.allocate(transaction.getBufferLength());
            transaction.toBuffer(buffer);
            buffer.flip();
            bb.put(buffer.array());
        }

        final byte[] nextHash = getNextHash(latestHash, bytes);
        return (new Block(name, latestHash, nextHash, transactions, this.blockchain.size()));
    }

    public Constants.Status checkHash(Block block) {
        if (block.blockLength > this.blockchain.size()) {
            // This block is in the future
            if (DEBUG)
                System.out.println(owner+" found a future block. lengths="+this.blockchain.size()+"\n"+"block={\n"+block.toString()+"\n}");
            return Constants.Status.FUTURE_BLOCK;
        }

        int length = 0;
        for (Transaction transaction : block.transactions)
            length += transaction.getBufferLength();
        final byte[] bytes = new byte[length];
        final ByteBuffer bb = ByteBuffer.wrap(bytes);

        for (Transaction transaction : block.transactions) {
            final ByteBuffer buffer = ByteBuffer.allocate(transaction.getBufferLength());
            transaction.toBuffer(buffer);
            buffer.flip();
            bb.put(buffer.array());
        }

        // Calculate what I think the next has should be
        final byte[] nextHash = getNextHash(latestHash, bytes);

        // Store the previous and next hash from the block
        final byte[] incomingPrev = block.prev;
        final byte[] incomingHash = block.hash;

        if (!(Arrays.equals(incomingHash, nextHash))) {
            // This block has a different 'next' hash then I expect, someone is out of sync
            if (DEBUG) {
                StringBuilder builder = new StringBuilder();
                builder.append(owner).append(" Invalid hash in block\n");
                builder.append("confirmed="+block.confirmed).append("\n");
                builder.append("length=").append(this.blockchain.size()).append("\n");
                builder.append("latest=["+HashUtils.bytesToHex(latestHash)+"]\n");
                builder.append("next=["+HashUtils.bytesToHex(nextHash)+"]\n");
                builder.append("incomingLength=").append(block.blockLength).append("\n");
                builder.append("incomingPrev=["+HashUtils.bytesToHex(incomingPrev)+"]\n");
                builder.append("incomingNext=["+HashUtils.bytesToHex(incomingHash)+"]\n");
                System.err.println(builder.toString());
            }
            return Constants.Status.BAD_HASH;
        }

        return Constants.Status.SUCCESS;
    }

    public Constants.Status addBlock(String dataFrom, Block block) {
        // Already processed this block? Happens if a miner is slow and isn't first to confirm the block
        if (blockchain.contains(block))
            return Constants.Status.DUPLICATE;

        // Check to see if the block's hash is what I expect
        final Constants.Status status = checkHash(block);
        if (status != Constants.Status.SUCCESS)
            return status;

        // Get the aggregate transaction for processing
        for (Transaction transaction : block.transactions) {
            // Remove the inputs from the unused pool
            for (Transaction t : transaction.inputs) {
                boolean exists = unused.remove(t);
                if (exists == false) {
                    if (DEBUG)
                        System.err.println(owner+" Bad inputs in block from '"+dataFrom+"'. block={\n"+block.toString()+"\n}");
                    return Constants.Status.BAD_INPUTS;
                }
            }
        }

        // Add outputs to unused pool
        for (Transaction transaction : block.transactions) {
            for (Transaction t : transaction.outputs)
                unused.add(t);
        }

        // Update the hash and add the new transaction to the list
        final byte[] prevHash = latestHash;
        final byte[] nextHash = block.hash;
        blockchain.add(block);
        for (Transaction transaction : block.transactions)
            transactions.add(transaction);
        latestHash = nextHash;

        if (DEBUG) {
            final String prev = HashUtils.bytesToHex(prevHash);
            final String next = HashUtils.bytesToHex(nextHash);
            final StringBuilder builder = new StringBuilder();
            builder.append(owner).append(" updated hash").append(" msg_from='"+dataFrom+"'").append(" block_from='"+block.from+"'\n");
            builder.append("blockchain length=").append(blockchain.size()).append("\n");
            builder.append("transactions=[\n");
            for (Transaction t : block.transactions) {
                builder.append(t.toString()).append("\n");
            }
            builder.append("]\n");
            builder.append("prev=[").append(prev).append("]\n");
            builder.append("next=[").append(next).append("]\n");
            System.err.println(builder.toString());
        }

        return Constants.Status.SUCCESS;
    }

    public long getBalance(String name) {
        long result = 0;
        for (Transaction t : transactions) {
            // Remove the inputs
            for (Transaction c : t.inputs) {
                if (name.equals(c.to))
                    result -= c.value;
            }
            // Add the outputs
            for (Transaction c : t.outputs) {
                if (name.equals(c.to))
                    result += c.value;
            }
        }
        return result;
    }

    public static final byte[] getNextHash(byte[] hash, byte[] bytes) {
        final StringBuilder builder = new StringBuilder();
        builder.append(HashUtils.bytesToHex(hash)).append(HashUtils.bytesToHex(bytes));
        final String string = builder.toString();
        final byte[] output = HashUtils.calculateSha256(string);
        return output;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Blockchain))
            return false;
        final Blockchain other = (Blockchain) o;
        for (Block b : blockchain) {
            if (!(other.blockchain.contains(b)))
                return false;
        }
        for (Transaction t : transactions) {
            if (!(other.transactions.contains(t)))
                return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("hash=[").append(HashUtils.bytesToHex(latestHash)).append("]\n");
        builder.append("inputs={").append("\n");
        for (Transaction c : transactions) {
            for (Transaction i : c.inputs)
                builder.append('\t').append(i.value).append(" from '").append(i.from).append("' to '").append(i.to).append("'\n");
            builder.append("}\n");
        }
        builder.append("outputs={").append("\n");
        for (Transaction c : transactions) {
            for (Transaction i : c.outputs)
                builder.append('\t').append(i.value).append(" from '").append(i.from).append("' to '").append(i.to).append("'\n");
            builder.append("}\n");
        }
        return builder.toString();
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/Peer.java
================================================
package com.jwetherell.bitcoin;

import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.jwetherell.bitcoin.common.Constants;
import com.jwetherell.bitcoin.common.HashUtils;
import com.jwetherell.bitcoin.data_model.Transaction;
import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.data_model.Block;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.networking.Multicast;
import com.jwetherell.bitcoin.networking.TCP;

/**
 * Class which handles lower level sending and receiving of messages.
 * 
 * Thread-Safe (Hopefully)
 */
public abstract class Peer {

    protected static final boolean                DEBUG                         = Boolean.getBoolean("debug");

    private static final int                      HEADER_LENGTH                 = 16;
    private static final String                   WHOIS                         = "Who is          ";
    private static final String                   IAM                           = "I am            ";
    private static final String                   TRANSACTION                   = "Transaction     ";
    private static final String                   TRANSACTION_ACK               = "Transaction ACK ";
    private static final String                   BLOCK                         = "Block           ";
    private static final String                   CONFIRMATION                  = "Block Confirm   ";
    private static final String                   RESEND                        = "Resend          ";
    private static final String                   REHASH                        = "Rehash          ";

    private static final int                      KEY_LENGTH                    = 4;
    private static final int                      NAME_LENGTH                   = 4;

    private static final String                   EVERY_ONE                     = "EVERYONE";
    private static final byte[]                   NO_SIG                        = new byte[0];

    private final TCP.Peer.RunnableSend           runnableSendTcp               = new TCP.Peer.RunnableSend();
    private final Multicast.Peer.RunnableSend     runnableSendMulti             = new Multicast.Peer.RunnableSend();

    private final MessageListener                 listener                      = new MessageListener() {
        /**
         * {@inheritDoc}
         */
        @Override
        public void onMessage(Receiver recv) {
            Data data = recv.getQueue().poll();
            while (data != null) {
                final String from = data.from;

                final byte[] bytes = data.message.array();
                final String string = new String(bytes);
                final String hdr = string.substring(0, HEADER_LENGTH);
                if (DEBUG) 
                    System.out.println(myName+" Listener received '"+hdr+"' msg");
                if (hdr.equals(WHOIS)) {
                    handleWhois(bytes,data);
                } else if (hdr.equals(IAM)) {
                    handleIam(bytes,data);
                    processTransactionsToSend(from);
                    processTransactionsToRecv(from);
                } else if (hdr.equals(TRANSACTION)) {
                    handleTransaction(from,bytes,data);
                } else if (hdr.equals(TRANSACTION_ACK)) {
                    handleTransactionAck(from,bytes,data);
                } else if (hdr.equals(BLOCK)) {
                    handleBlock(from,bytes,data);
                } else if (hdr.equals(CONFIRMATION)) {
                    handleConfirmation(from,bytes,data);
                    processFutureBlocksToRecv(from);
                } else if (hdr.equals(RESEND)) {
                    handleResend(from,bytes,data);
                } else if (hdr.equals(REHASH)) {
                    handleRehash(from,bytes,data);
                } else {
                    System.err.println(myName+" Cannot handle msg. hdr="+hdr);
                }

                // Get next message
                data = recv.getQueue().poll();
                Thread.yield();
            }
            Thread.yield();
        }
    };

    // Keep track of everyone's name -> ip+port
    private final Map<String,Host>                peers                         = new ConcurrentHashMap<String,Host>();

    // Pending msgs (This happens if we don't know the ip+port OR the public key of a host
    private final Map<String,Queue<Queued>>       transactionsToSend            = new ConcurrentHashMap<String,Queue<Queued>>();
    private final Map<String,Queue<Queued>>       transactionsToRecv            = new ConcurrentHashMap<String,Queue<Queued>>();
    private final Map<String,Queue<Queued>>       futureTransactionsToRecv      = new ConcurrentHashMap<String,Queue<Queued>>();

    private final Map<String,TimerTask>           timerMap                      = new ConcurrentHashMap<String,TimerTask>();
    private final Timer                           timer                         = new Timer();

    private final Thread                          tcpSendThread;
    private final Thread                          tcpRecvThread;
    private final Thread                          multiSendThread;
    private final Thread                          multiRecvThread;

    // Thread safe queues for sending messages
    private final Queue<Data>                     sendTcpQueue;
    private final Queue<Data>                     sendMultiQueue;

    protected final TCP.Peer.RunnableRecv         runnableRecvTcp               = new TCP.Peer.RunnableRecv(listener);
    protected final Multicast.Peer.RunnableRecv   runnableRecvMulti             = new Multicast.Peer.RunnableRecv(listener);
    protected final String                        myName;

    protected Peer(String name) {
        this.myName = name;

        // Receivers

        tcpRecvThread = new Thread(runnableRecvTcp, myName+" recvTcp");
        tcpRecvThread.start();

        multiRecvThread = new Thread(runnableRecvMulti, myName+" recvMulti");
        multiRecvThread.start();

        // Senders

        tcpSendThread = new Thread(runnableSendTcp, myName+" sendTcp");
        sendTcpQueue = runnableSendTcp.getQueue();
        tcpSendThread.start();

        multiSendThread = new Thread(runnableSendMulti, myName+" sendMulti");
        sendMultiQueue = runnableSendMulti.getQueue();
        multiSendThread.start();

        while (isReady()==false) {
            Thread.yield();
        }
    }

    public String getName() {
        return myName;
    }

    public void shutdown() throws InterruptedException {
        TCP.Peer.RunnableSend.run = false;
        TCP.Peer.RunnableRecv.run = false;

        Multicast.Peer.RunnableSend.run = false;
        Multicast.Peer.RunnableRecv.run = false;

        // Senders

        multiSendThread.interrupt();
        multiSendThread.join();

        tcpSendThread.interrupt();
        tcpSendThread.join();

        // Receivers

        tcpRecvThread.interrupt();
        tcpRecvThread.join();

        multiRecvThread.interrupt();
        multiRecvThread.join();
    }

    public boolean isReady() {
        return (runnableRecvTcp.isReady() && runnableSendTcp.isReady() && runnableRecvMulti.isReady() && runnableSendMulti.isReady());
    }

    public abstract Blockchain getBlockChain();

    /** Get encoded public key **/
    protected abstract byte[] getPublicKey();

    private void sendWhois(String who) {
        final byte[] msg = getWhoisMsg(who);
        final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), EVERY_ONE, runnableRecvMulti.getHost(), runnableRecvMulti.getPort(), NO_SIG, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg");
        }
        sendMultiQueue.add(data);
    }

    private void handleWhois(byte[] bytes, Data data) {
        final String name = parseWhoisMsg(bytes);

        // If your name and not from myself then shout it out!
        if (name.equals(this.myName) && !(data.from.equals(this.myName)))
            sendIam();
    }

    private void sendIam() {
        final byte[] msg = getIamMsg(getPublicKey());
        final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), EVERY_ONE, runnableRecvMulti.getHost(), runnableRecvMulti.getPort(), NO_SIG, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg");
        }
        sendMultiQueue.add(data);
    }

    private void handleIam(byte[] bytes, Data data) {
        final String name = data.from;
        final byte[] key = parseIamMsg(bytes);

        // Update peers
        peers.put(name, new Host(data.sourceAddr.getHostAddress(), data.sourcePort));

        // New public key
        newPublicKey(name, key);
    }

    /** What do you want to do with a new public key **/
    protected abstract void newPublicKey(String name, byte[] publicKey);

    /** Sign message with private key **/
    protected abstract byte[] signMsg(byte[] bytes);

    /** Verify the bytes given the public key and signature **/
    protected abstract boolean verifyMsg(byte[] publicKey, byte[] signature, byte[] bytes);

    /** Send transaction to the peer named 'to' **/
    protected void sendTransaction(String to, Transaction transaction) {
        final Host d = peers.get(to);
        if (d == null){
            // Could not find peer, broadcast a whois
            addTransactionToSend(Queued.State.NEW, to, transaction);
            sendWhois(to);
            return;
        }

        final byte[] msg = getTransactionMsg(transaction);
        final byte[] sig = signMsg(msg);
        final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), to, d.ip, d.port, sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg");
        }
        sendTcpQueue.add(data);
    }

    private Constants.Status handleTransaction(String dataFrom, byte[] bytes, Data data) {
        final Transaction trans = parseTransactionMsg(bytes);
        return handleTransaction(dataFrom, trans, data);
    }

    private Constants.Status handleTransaction(String dataFrom, Transaction transaction, Data data) {
        // I shouldn't ACK my own transaction
        if (transaction.from.equals(myName))
            return Constants.Status.OWN_TRANSACTION;

        // Let the app logic do what it needs to
        final Constants.Status status = handleTransaction(dataFrom, transaction, data.signature.array(), data.message.array());
        if (status == Constants.Status.NO_PUBLIC_KEY) {
            addTransactionToRecv(Queued.State.NEW, dataFrom, transaction, data);
            sendWhois(dataFrom);
            return status;
        } else if (status != Constants.Status.SUCCESS) {
            return status;
        }

        // Send an ACK msg
        ackTransaction(dataFrom, transaction);

        return status;
    }

    /** What do you want to do now that you have received a transaction, return the KeyStatus **/
    protected abstract Constants.Status handleTransaction(String dataFrom, Transaction transaction, byte[] signature, byte[] bytes);

    private void ackTransaction(String to, Transaction transaction) {
        final Host d = peers.get(to);
        if (d == null){
            // Could not find peer, broadcast a whois
            addTransactionToSend(Queued.State.ACK, to, transaction);
            sendWhois(to);
            return;
        }

        final byte[] msg = getTransactionAckMsg(transaction);
        final byte[] sig = signMsg(msg);
        final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), to, d.ip, d.port, sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg");
        }
        sendTcpQueue.add(data);
    }

    private void handleTransactionAck(String dataFrom, byte[] bytes, Data data) {
        final Transaction trans = parseTransactionAckMsg(bytes);
        handleTransactionAck(dataFrom, trans, data);
    }

    private void handleTransactionAck(String dataFrom, Transaction transaction, Data data) {
        // Let the app logic do what it needs to
        Constants.Status knownPublicKey = handleTransactionAck(dataFrom, transaction, data.signature.array(), data.message.array());
        if (knownPublicKey == Constants.Status.NO_PUBLIC_KEY) {
            addTransactionToRecv(Queued.State.ACK, dataFrom, transaction, data);
            sendWhois(dataFrom);
            return;
        } else if (knownPublicKey != Constants.Status.SUCCESS) {
            return;
        }

        final Block block = aggegateTransaction(transaction);
        if (block != null) 
            sendBlock(block, data);
    }

    /** What do you want to do now that you received an ACK for a sent transaction, return the KeyStatus **/
    protected abstract Constants.Status handleTransactionAck(String dataFrom, Transaction transaction, byte[] signature, byte[] bytes);

    /** Collect the transactions, either return null or a new Block **/
    protected abstract Block aggegateTransaction(Transaction transaction);

    protected void sendBlock(Block block, Data data) {
        final byte[] msg = getBlockMsg(block);
        final byte[] sig = signMsg(msg);
        final Data dataToSend = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), data.from, data.sourceAddr.getHostAddress(), data.sourcePort, sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg. confirmed="+block.confirmed);
        }
        sendTcpQueue.add(dataToSend);
    }

    private void handleBlock(String dataFrom, byte[] bytes, Data data) {
        final Block block = parseBlockMsg(bytes);
        handleBlock(dataFrom, block, data);
    }

    private void handleBlock(String dataFrom, Block block, Data data) {
        // I shouldn't handle my own block
        if (block.from.equals(myName))
            return;

        final int length = this.getBlockChain().getLength();
        final Constants.Status status = checkTransaction(data.from, block, data.signature.array(), data.message.array());
        if (status == Constants.Status.NO_PUBLIC_KEY) {
            addBlockToRecv(Queued.State.CONFIRM, dataFrom, block, data);
            sendWhois(dataFrom);
            return;
        } else if (status == Constants.Status.BAD_HASH) {
            sendRehash(block, data);
            return;
        } else if (status == Constants.Status.FUTURE_BLOCK) {
            // If we have a transaction which is in the future, we are likely missing a previous block
            // Save the block for later processing and ask the network for the missing block.
            addFutureBlockToRecv(Queued.State.FUTURE, dataFrom, block, data);
            sendResend(length);
            return;
        } else if (status != Constants.Status.SUCCESS) {
            System.out.println(myName+" handleBlock() error="+status);
            // Drop all other errors
            return;
        }

        // Hash looks good to me, ask everyone else
        sendConfirmation(block);
    }

    /** multicast **/
    protected void sendConfirmation(Block block) {
        final byte[] msg = getConfirmationMsg(block);
        final byte[] sig = signMsg(msg);
        final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), EVERY_ONE, runnableRecvMulti.getHost(), runnableRecvMulti.getPort(), sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending multicast '"+hdr+"' msg. confirmed="+block.confirmed);
        }
        sendMultiQueue.add(data);
    }

    /** tcp **/
    protected void sendConfirmation(Block block, Data data) {
        final byte[] msg = getConfirmationMsg(block);
        final byte[] sig = signMsg(msg);
        final Data dataToSend = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), data.from, data.sourceAddr.getHostAddress(), data.sourcePort, sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending unicast '"+hdr+"' msg. confirmed="+block.confirmed);
        }
        sendTcpQueue.add(dataToSend);
    }

    private void handleConfirmation(String dataFrom, byte[] bytes, Data data) {
        final Block block = parseConfirmationMsg(bytes);
        handleConfirmation(dataFrom, block, data);
    }

    private void handleConfirmation(String dataFrom, Block block, Data data) {
        if (block.confirmed) {
            // Yey! we got a confirmation from the community

            final int length = this.getBlockChain().getLength();
            final Constants.Status status = handleConfirmation(dataFrom, block, data.signature.array(), data.message.array());
            if (status == Constants.Status.SUCCESS) {
                // Someone solved the nonce, cancel async task - if we have one
                final String hex = HashUtils.bytesToHex(block.hash);
                final TimerTask miner = timerMap.get(hex);
                if (miner != null)
                    miner.cancel();
                return;
            } else if (status == Constants.Status.NO_PUBLIC_KEY) {
                addBlockToRecv(Queued.State.CONFIRM, dataFrom, block, data);
                sendWhois(dataFrom);
                return;
            } else if (status == Constants.Status.BAD_HASH) {
                sendRehash(block, data);
                return;
            } else if (status == Constants.Status.FUTURE_BLOCK) {
                // If we have a transaction which is in the future, we are likely missing a previous block
                // Save the block for later processing and ask the network for the missing block.
                addFutureBlockToRecv(Queued.State.FUTURE, dataFrom, block, data);
                sendResend(length);
                return;
            } else if (status != Constants.Status.SUCCESS) {
                // Drop all other errors
                return;
            }

            return;
        }

        // Don't confirm my own block
        if (block.from.equals(myName))
            return;

        final int length = this.getBlockChain().getLength();
        final Constants.Status status = checkTransaction(dataFrom, block, data.signature.array(), data.message.array());
        if (status == Constants.Status.NO_PUBLIC_KEY) {
            addBlockToRecv(Queued.State.CONFIRM, dataFrom, block, data);
            sendWhois(dataFrom);
            return;
        } else if (status == Constants.Status.FUTURE_BLOCK) {
            // Do not add to 'FutureBlockToRecv' since this block isn't confirmed
            sendResend(length);
            return;
        } else if (status == Constants.Status.BAD_HASH) {
            sendRehash(block, data);
            return;
        } else if (status != Constants.Status.SUCCESS) {
            // Drop all other errors
            return;
        }

        // async task
        final String hex = HashUtils.bytesToHex(block.hash);
        final TimerTask miningTask = new MiningTask(this,hex,block);
        timer.schedule(miningTask, 0);
    }

    /** What do you want to do now that you received a block, return the HashStatus **/
    protected abstract Constants.Status checkTransaction(String dataFrom, Block block, byte[] signature, byte[] bytes);

    /** What do you want to do now that you received a valid block, return the HashStatus **/
    protected abstract Constants.Status handleConfirmation(String dataFrom, Block block, byte[] signature, byte[] bytes);

    /** Mine the nonce sent in the transaction **/
    protected abstract int mineHash(MiningTask timer, byte[] sha256, long numberOfZerosInPrefix);

    private void sendResend(int blockNumber) {
        final byte[] msg = getResendBlockMsg(blockNumber);
        final byte[] sig = signMsg(msg);
        final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), EVERY_ONE, runnableRecvMulti.getHost(), runnableRecvMulti.getPort(), sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg");
        }
        sendMultiQueue.add(data);
    }

    private void handleResend(String dataFrom, byte[] bytes, Data data) {
        // I shouldn't resend from my own resend msg (flood warning!)
        if (dataFrom.equals(myName))
            return;

        final int blockNumber = parseResendBlockMsg(bytes);
        final int length = this.getBlockChain().getLength();
        // I don't have the block, cannot help.
        if (length<=blockNumber)
            return;

        final Block toSend = this.getBlockChain().getBlock(blockNumber);
        sendConfirmation(toSend, data);
    }

    protected void sendRehash(Block block, Data data) {
        final byte[] msg = getRehashMsg(block);
        final byte[] sig = signMsg(msg);
        final Data dataToSend = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), data.from, data.sourceAddr.getHostAddress(), data.sourcePort, sig, msg);
        if (DEBUG) {
            final String string = new String(msg);
            final String hdr = string.substring(0, HEADER_LENGTH);
            System.out.println(myName+" Sending '"+hdr+"' msg");
        }
        sendTcpQueue.add(dataToSend);
    }

    private void handleRehash(String dataFrom, byte[] bytes, Data data) {
        final Block block = parseRehashMsg(bytes);
        // hash was out of sync with the blockchain, reprocess it.
        for (Transaction t : block.transactions)
            handleTransactionAck(dataFrom, t, data);
    }

    private void addTransactionToSend(Queued.State state, String to, Transaction transaction) {
        final Queued q = new Queued(state, transaction, null);
        Queue<Queued> l = transactionsToSend.get(to);
        if (l == null) {
            l = new ConcurrentLinkedQueue<Queued>();
            transactionsToSend.put(to, l);
        }
        l.add(q);
    }

    private void processTransactionsToSend(String to) {
        Queue<Queued> l = transactionsToSend.get(to);
        if (l==null || l.size()==0)
            return;
        while (l.size()>0) {
            final Queued q = l.poll();
            if (q == null)
                return;
            final Host d = peers.get(to); // Do not use the data object in the queue object
            final byte[] msg = getTransactionMsg(q.transaction);
            final byte[] sig = signMsg(msg);
            final Data data = new Data(myName, runnableRecvTcp.getHost(), runnableRecvTcp.getPort(), to, d.ip, d.port, sig, msg);
            if (DEBUG) {
                final String string = new String(msg);
                final String hdr = string.substring(0, HEADER_LENGTH);
                System.out.println(myName+" Sending '"+hdr+"' msg");
            }
            sendTcpQueue.add(data);
        }
    }

    private void addTransactionToRecv(Queued.State state, String dataFrom, Transaction transaction, Data data) {
        final Queued q = new Queued(state, transaction, data);
        Queue<Queued> lc = transactionsToRecv.get(dataFrom);
        if (lc == null) {
            lc = new ConcurrentLinkedQueue<Queued>();
            transactionsToRecv.put(dataFrom, lc);
        }
        lc.add(q);
    }

    private void addBlockToRecv(Queued.State state, String dataFrom, Block block, Data data) {
        final Queued q = new Queued(state, block, data);
        Queue<Queued> lc = transactionsToRecv.get(dataFrom);
        if (lc == null) {
            lc = new ConcurrentLinkedQueue<Queued>();
            transactionsToRecv.put(dataFrom, lc);
        }
        lc.add(q);
    }

    private void processTransactionsToRecv(String from) {
        Queue<Queued> l = transactionsToRecv.get(from);
        if (l==null || l.size()==0)
            return;
        while (l.size()>0) {
            final Queued q = l.poll();
            if (q == null)
                return;
            if (q.state == Queued.State.CONFIRM)
                handleConfirmation(from, q.block, q.data);
            else if (q.state == Queued.State.ACK)
                handleTransactionAck(from, q.transaction, q.data);
            else
                handleTransaction(from, q.transaction, q.data);
        }
    }

    private void addFutureBlockToRecv(Queued.State state, String dataFrom, Block block, Data data) {
        final Queued q = new Queued(state, block, data);
        Queue<Queued> lc = futureTransactionsToRecv.get(dataFrom);
        if (lc == null) {
            lc = new ConcurrentLinkedQueue<Queued>();
            futureTransactionsToRecv.put(dataFrom, lc);
        }
        lc.add(q);
    }

    private void processFutureBlocksToRecv(String from) {
        Queue<Queued> l = futureTransactionsToRecv.get(from);
        if (l==null || l.size()==0)
            return;
        while (l.size()>0) {
            final Queued q = l.poll();
            if (q == null)
                return;
            if (q.state == Queued.State.FUTURE)
                handleConfirmation(from, q.block, q.data);
        }
    }

    private static final class Host {

        private final String    ip;
        private final int       port;

        private Host(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
    }

    private static final class Queued {

        private enum                State {NEW, ACK, CONFIRM, FUTURE};

        private final State         state;
        private final Transaction   transaction;
        private final Block         block;
        private final Data          data;

        private Queued(State state, Transaction transaction, Data data) {
            if (state == State.CONFIRM)
                throw new RuntimeException("Cannot have a CONFIRM without a Block");
            this.state = state;
            this.transaction = transaction;
            this.block = null;
            this.data = data;
        }

        private Queued(State state, Block block, Data data) {
            if (state != State.CONFIRM && state != State.FUTURE)
                throw new RuntimeException("Cannot have a non-CONFIRM/FUTURE with a Block");
            this.state = state;
            this.transaction = null;
            this.block = block;
            this.data = data;
        }
    }

    public static class MiningTask extends TimerTask {

        public volatile boolean run = true;

        private final   Peer    peer;
        private final   String  hex;
        private final   Block   block;

        /** unit testing constructor **/
        public MiningTask() {
            this.peer = null;
            this.hex = null;
            this.block = null;
        }

        public MiningTask(Peer peer, String hex, Block block) {
            this.peer = peer;
            this.hex = hex;
            this.block = block;

            TimerTask oldTask = peer.timerMap.put(hex, this);
            if (oldTask != null)
                oldTask.cancel();
        }

        @Override
        public void run() {
            peer.timerMap.remove(hex);

            // Let's mine this sucker.
            final int nonce = peer.mineHash(this, block.hash, block.numberOfZeros);

            if (nonce < 0)
                return;

            // Hash looks good to me and I have computed a nonce, let everyone know
            block.confirmed = true;
            block.nonce = nonce;

            // Make sure the block is still valid (hasn't been solved by someone else while we were processing)
            Constants.Status status = peer.getBlockChain().checkHash(block);
            if (status != Constants.Status.SUCCESS) {
                System.err.println(peer.myName +" NOT sending block, hash is no longer valid.");
                return;
            }

            peer.sendConfirmation(block);
        }

        @Override
        public boolean cancel() {
            peer.timerMap.remove(hex);
            run = false;
            return super.cancel();
        }
    }

    public static final byte[] getWhoisMsg(String name) {
        final byte[] bName = name.getBytes();
        final int nameLength = bName.length;
        final byte[] msg = new byte[HEADER_LENGTH + NAME_LENGTH + nameLength];
        final ByteBuffer buffer = ByteBuffer.wrap(msg);

        buffer.put(WHOIS.getBytes());
        buffer.putInt(nameLength);
        buffer.put(bName);

        return msg;
    }

    public static final String parseWhoisMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);

        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final int nLength = buffer.getInt();
        final byte [] bName = new byte[nLength];
        buffer.get(bName);

        return new String(bName);
    }

    public static final byte[] getIamMsg(byte[] publicKey) {
        final int publicKeyLength = publicKey.length;
        final byte[] msg = new byte[HEADER_LENGTH + KEY_LENGTH + publicKeyLength];
        final ByteBuffer buffer = ByteBuffer.wrap(msg);

        buffer.put(IAM.getBytes());

        buffer.putInt(publicKeyLength);
        buffer.put(publicKey);

        return msg;
    }

    public static final byte[] parseIamMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);

        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final int kLength = buffer.getInt();
        final byte [] bKey = new byte[kLength];
        buffer.get(bKey);

        return bKey;
    }

    public static final byte[] getTransactionMsg(Transaction transaction) {
        final byte[] msg = new byte[HEADER_LENGTH + transaction.getBufferLength()];
        final ByteBuffer transactionBuffer = ByteBuffer.allocate(transaction.getBufferLength());
        transaction.toBuffer(transactionBuffer);
        transactionBuffer.flip();

        final ByteBuffer buffer = ByteBuffer.wrap(msg);
        buffer.put(TRANSACTION.getBytes());

        buffer.put(transactionBuffer);

        return msg;
    }

    public static final Transaction parseTransactionMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final Transaction transaction = new Transaction();
        transaction.fromBuffer(buffer);

        return transaction;
    }

    public static final byte[] getTransactionAckMsg(Transaction transaction) {
        final byte[] msg = new byte[HEADER_LENGTH + transaction.getBufferLength()];
        final ByteBuffer transactionBuffer = ByteBuffer.allocate(transaction.getBufferLength());
        transaction.toBuffer(transactionBuffer);
        transactionBuffer.flip();

        final ByteBuffer buffer = ByteBuffer.wrap(msg);
        buffer.put(TRANSACTION_ACK.getBytes());

        buffer.put(transactionBuffer);

        return msg;
    }

    public static final Transaction parseTransactionAckMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final Transaction block = new Transaction();
        block.fromBuffer(buffer);

        return block;
    }

    public static final byte[] getBlockMsg(Block block) {
        final byte[] msg = new byte[HEADER_LENGTH + block.getBufferLength()];
        final ByteBuffer blockBuffer = ByteBuffer.allocate(block.getBufferLength());
        block.toBuffer(blockBuffer);
        blockBuffer.flip();

        final ByteBuffer buffer = ByteBuffer.wrap(msg);
        buffer.put(BLOCK.getBytes());

        buffer.put(blockBuffer);

        return msg;
    }

    public static final Block parseBlockMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final Block block = new Block();
        block.fromBuffer(buffer);

        return block;
    }

    public static final byte[] getConfirmationMsg(Block block) {
        final byte[] msg = new byte[HEADER_LENGTH + block.getBufferLength()];
        final ByteBuffer blockBuffer = ByteBuffer.allocate(block.getBufferLength());
        block.toBuffer(blockBuffer);
        blockBuffer.flip();

        final ByteBuffer buffer = ByteBuffer.wrap(msg);
        buffer.put(CONFIRMATION.getBytes());

        buffer.put(blockBuffer);

        return msg;
    }

    public static final Block parseConfirmationMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final Block block = new Block();
        block.fromBuffer(buffer);

        return block;
    }

    public static final byte[] getResendBlockMsg(int blockNumber) {
        final byte[] msg = new byte[HEADER_LENGTH + 4];

        final ByteBuffer buffer = ByteBuffer.wrap(msg);
        buffer.put(RESEND.getBytes());

        buffer.putInt(blockNumber);

        return msg;
    }

    public static final int parseResendBlockMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final int blockNumber = buffer.getInt();

        return blockNumber;
    }

    public static final byte[] getRehashMsg(Block block) {
        final byte[] msg = new byte[HEADER_LENGTH + block.getBufferLength()];
        final ByteBuffer blockBuffer = ByteBuffer.allocate(block.getBufferLength());
        block.toBuffer(blockBuffer);
        blockBuffer.flip();

        final ByteBuffer buffer = ByteBuffer.wrap(msg);
        buffer.put(REHASH.getBytes());

        buffer.put(blockBuffer);

        return msg;
    }

    public static final Block parseRehashMsg(byte[] bytes) {
        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
        final byte [] bMsgType = new byte[HEADER_LENGTH];
        buffer.get(bMsgType);

        final Block block = new Block();
        block.fromBuffer(buffer);

        return block;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("name=").append(myName);
        return builder.toString();
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/ProofOfWork.java
================================================
package com.jwetherell.bitcoin;

import java.nio.ByteBuffer;

import com.jwetherell.bitcoin.Peer.MiningTask;
import com.jwetherell.bitcoin.common.HashUtils;

public class ProofOfWork {

    private static final byte getBit(int ID, int position) {
       return (byte) ((ID >> position) & (byte)1);
    }

    /** 
     * Given the sha256 hash, what number can we append to the hash which'll create a hash
     * which has a leading 'numberOfZerosInPrefix' number of zeros.
     * 
     * e.g.
     * if numberOfZerosInPrefix == 3
     * input = 7982970534e089b839957b7e174725ce1878731ed6d700766e59cb16f1c25e27
     * solution = 0000fedb24e31adb71559d43a9a3ddc8be7606fa6befe3260b3eee2cf2aeb642
     * output = 21080
     * 
     **/
    public static final int solve(MiningTask task, byte[] sha256, long numberOfZerosInPrefix) {
        final int length = sha256.length;

        final ByteBuffer buffer = ByteBuffer.allocate(length+4);
        buffer.put(sha256, 0, length);

        int x = 0;
        while (task.run && x < Integer.MAX_VALUE) {
            // append x
            buffer.putInt(length, x);
            // calculate new hash
            final byte[] result = HashUtils.calculateSha256(buffer.array());
            // wrap in buffer for easier processing
            final ByteBuffer bb = ByteBuffer.wrap(result);

            boolean wrong = false;
            boolean done = false;
            int numOfZeros = 0;
            for (int i=0; i<bb.limit(); i++) {
                final byte b = bb.get(i);
                for (int j=0; j<8; j++) {
                    final byte a = getBit(b,(i*8)+j);
                    if (a == 0) {
                        numOfZeros++;
                    } else {
                        wrong = true;
                        break;
                    }
                    if (numOfZeros == numberOfZerosInPrefix) {
                        done = true;
                        break;
                    }
                }
                if (done || wrong)
                    break;
            }
            if (done)
                break;
            x++;
        }
        if (!task.run)
            return Integer.MIN_VALUE;
        return x;
    }

    /** Does the given nonce create a hash which starts with 'numberOfZerosInPrefix' number of zeros **/
    public static final boolean check(byte[] sha256, int nonce, long numberOfZerosInPrefix) {
        final int length = sha256.length;

        final ByteBuffer buffer = ByteBuffer.allocate(length+4);
        buffer.put(sha256, 0, length);

        // append nonce
        buffer.putInt(length, nonce);
        // calculate new hash
        final byte[] result = HashUtils.calculateSha256(buffer.array());
        // wrap in buffer for easier processing
        final ByteBuffer bb = ByteBuffer.wrap(result);

        boolean incorrect = false;
        boolean correct = false;
        int numOfZeros = 0;
        for (int i=0; i<bb.limit(); i++) {
            final byte b = bb.get(i);
            for (int j=0; j<8; j++) {
                final byte a = getBit(b,(i*8)+j);
                if (a == 0) {
                    numOfZeros++;
                } else {
                    incorrect = true;
                    break;
                }
                if (numOfZeros == numberOfZerosInPrefix) {
                    correct = true;
                    break;
                }
            }
            if (correct || incorrect)
                break;
        }
        return correct;
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/Wallet.java
================================================
package com.jwetherell.bitcoin;

import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.ConcurrentHashMap;

import com.jwetherell.bitcoin.common.Constants;
import com.jwetherell.bitcoin.common.HashUtils;
import com.jwetherell.bitcoin.common.KeyUtils;
import com.jwetherell.bitcoin.data_model.Block;
import com.jwetherell.bitcoin.data_model.Transaction;

/**
 * Class which handles the logic of maintaining the wallet including tracking serial numbers and public/private key encryption.
 * 
 * Thread-Safe (Hopefully)
 */
public class Wallet extends Peer {

    protected final KeyPairGenerator                        gen;
    protected final SecureRandom                            random;
    protected final Signature                               enc;
    protected final Signature                               dec;
    protected final KeyPair                                 pair;
    protected final PrivateKey                              privateKey;
    protected final KeyFactory                              keyFactory;
    protected final PublicKey                               publicKey;
    protected final byte[]                                  bPublicKey;
    { // initialize the private/public key associated with this wallet
        try {
            gen = KeyPairGenerator.getInstance("DSA", "SUN");
            random = SecureRandom.getInstance("SHA1PRNG", "SUN");
            gen.initialize(512, random);

            enc = Signature.getInstance("SHA1withDSA", "SUN");
            dec = Signature.getInstance("SHA1withDSA", "SUN");

            pair = gen.generateKeyPair();
            privateKey = pair.getPrivate();
            enc.initSign(privateKey);

            publicKey = pair.getPublic();
            bPublicKey = publicKey.getEncoded();

            keyFactory = KeyFactory.getInstance("DSA", "SUN");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Number of zeros in prefix of has to compute as the proof of work.
    private static final int                                NUMBER_OF_ZEROS                 = 32;
    // Number of transactions to aggregate in a Block
    private static final int                                NUMBER_OF_TRANSACTIONS_IN_BLOCK = 1;
    // Empty list
    private static final Transaction[]                      EMPTY                           = new Transaction[0];
    // Ranks Transactions by their value
    private static final Comparator<Transaction>            TRANSACTION_COMPARATOR          = new Comparator<Transaction>() {
        @Override
        public int compare(Transaction o1, Transaction o2) {
            // Higher value is processed quicker
            if (o1.value > o2.value)
                return -1;
            if (o2.value > o1.value)
                return 1;
            return 0;
        }
    };

    // Keep track of everyone's name -> public key
    private final Map<String,ByteBuffer>                    publicKeys                      = new ConcurrentHashMap<String,ByteBuffer>();
    // My BLockChain
    private final Blockchain                                blockchain;
    // Ranks transactions by their value to me 
    private final PriorityQueue<Transaction>                transactionQueue                = new PriorityQueue<Transaction>(10, TRANSACTION_COMPARATOR);

    public Wallet(String name) {
        super(name);
        // add the initial pub key
        this.publicKeys.put(Blockchain.NO_ONE, ByteBuffer.wrap(Blockchain.NO_ONE_PUB_KEY));
        this.publicKeys.put(myName, ByteBuffer.wrap(bPublicKey));
        // initialize the blockchain
        this.blockchain = new Blockchain(name);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected byte[] getPublicKey() {
        return bPublicKey;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Blockchain getBlockChain() {
        return blockchain;
    }

    public long getBalance() {
        return blockchain.getBalance(myName);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void newPublicKey(String name, byte[] publicKey) {
        publicKeys.put(name, ByteBuffer.wrap(publicKey));
    }

    /**
     * {@inheritDoc}
     * 
     * synchronized to protect enc from changing while processing
     */
    @Override
    protected synchronized byte[] signMsg(byte[] bytes) {
        return KeyUtils.signMsg(enc, bytes);
    }

    /**
     * {@inheritDoc}
     * 
     * synchronized to protect keyfactory/dec from changing while processing
     */
    @Override
    protected synchronized boolean verifyMsg(byte[] publicKey, byte[] signature, byte[] bytes) {
        return KeyUtils.verifyMsg(keyFactory, dec, publicKey, signature, bytes);
    }

    // synchronized to protect the blockchain from changing while processing
    public synchronized void sendCoin(String name, int value) {
        // Iterate through the our unused transactions to see if we have enough coins
        final List<Transaction> inputList = new ArrayList<Transaction>();
        int coins = 0;
        for (Transaction t : this.blockchain.getUnused()) {
            if (!(t.to.equals(myName))) 
                continue;
            coins += t.value;
            inputList.add(t);
            if (coins >= value)
                break;
        }
        if (coins < value) {
            System.err.println(myName+" Sorry, you do not have enough coins.");
            return;
        }
        // Convert the list into an array
        final Transaction[] inputs = new Transaction[inputList.size()];
        for (int i=0; i<inputList.size(); i++)
            inputs[i] = inputList.get(i);

        // Since the entire input has to be used up, calculate if we will get any change left over
        // and send it back to myself.
        List<Transaction> outputList = new ArrayList<Transaction>();
        if (coins > value) {
            // I get some change back, so add myself as an output
            final int myCoins = coins - value;
            final String msg = "I get some change back.";
            final Transaction t = Transaction.newSignedTransaction(enc, myName, myName, msg, myCoins, inputs, EMPTY);
            outputList.add(t);
        }
        // Create the transaction for the recipient
        final String msg = "Here are some coins for you";
        final Transaction t = Transaction.newSignedTransaction(enc, myName, name, msg, value, inputs, EMPTY);
        outputList.add(t);
        // Convert the list into an array
        final Transaction[] outputs = new Transaction[outputList.size()];
        for (int i=0; i<outputList.size(); i++)
            outputs[i] = outputList.get(i);

        // Create the aggregate transaction, any value here will be a reward for the miner.
        final String myMsg = value+" from "+myName+" to "+name;
        final Transaction transaction = Transaction.newSignedTransaction(enc, myName, name, myMsg, 0, inputs, outputs);

        super.sendTransaction(name, transaction);
    }

    /** make sure this message is from who it says it is **/
    private Constants.Status checkSignature(String dataFrom, byte[] signature, byte[] bytes) {
        if (!publicKeys.containsKey(dataFrom))
            return Constants.Status.NO_PUBLIC_KEY;

        final byte[] key = publicKeys.get(dataFrom).array();
        if (!verifyMsg(key, signature, bytes))
            return Constants.Status.BAD_SIGNATURE;

        return Constants.Status.SUCCESS;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Constants.Status handleTransaction(String dataFrom, Transaction transaction, byte[] signature, byte[] bytes) {
        final Constants.Status status = checkSignature(dataFrom, signature, bytes);
        if (status != Constants.Status.SUCCESS) {
            if (DEBUG)
                System.err.println(myName+" handleTransaction() from '"+dataFrom+"' status="+status+"\n"+"transaction={\n"+transaction.toString()+"\n}");
            return status;
        }

        return Constants.Status.SUCCESS;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Constants.Status handleTransactionAck(String dataFrom, Transaction transaction, byte[] signature, byte[] bytes) {
        final Constants.Status status = checkSignature(dataFrom, signature, bytes);
        if (status != Constants.Status.SUCCESS) {
            if (DEBUG)
                System.err.println(myName+" handleTransactionAck() from '"+dataFrom+"' status="+status+"\n"+"transaction={\n"+transaction.toString()+"\n}");
            return status;
        }

        return Constants.Status.SUCCESS;
    }

    /** Create a block given the these Transactions **/
    private Block getNextBlock(Transaction[] transactions) {
        Block trans = blockchain.getNextBlock(myName, transactions);
        // Need to be confirmed
        trans.confirmed = false;
        // Number of zeros in prefix of hash to compute
        trans.numberOfZeros = NUMBER_OF_ZEROS;
        return trans;
    }

    /**
     * {@inheritDoc}
     * 
     * synchronized to protected queue from changing while processing
     */
    @Override
    protected synchronized Block aggegateTransaction(Transaction transaction) {
        transactionQueue.add(transaction);
        if (transactionQueue.size() >= NUMBER_OF_TRANSACTIONS_IN_BLOCK) {
            Transaction[] transactions = new Transaction[NUMBER_OF_TRANSACTIONS_IN_BLOCK];
            for (int i=0; i<NUMBER_OF_TRANSACTIONS_IN_BLOCK; i++) {
                final Transaction t = transactionQueue.poll();
                transactions[i] = t;
            }
            final Block block = getNextBlock(transactions);
            return block;
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Constants.Status checkTransaction(String dataFrom, Block block, byte[] signature, byte[] bytes) {
        final Constants.Status status = checkSignature(dataFrom, signature, bytes);
        if (status != Constants.Status.SUCCESS) {
            if (DEBUG)
                System.err.println(myName+" checkTransaction() from '"+dataFrom+"' status="+status+"\n"+"block={\n"+block.toString()+"\n}\n");
            return status;
        }

        return blockchain.checkHash(block);
    }

    /**
     * {@inheritDoc}
     * 
     * synchronized to protect the blockchain from changing while processing
     */
    @Override
    protected synchronized Constants.Status handleConfirmation(String dataFrom, Block block, byte[] signature, byte[] bytes) {
        // Let's see if the nonce was computed correctly
        final boolean nonceComputedCorrectly = ProofOfWork.check(block.hash, (int)block.nonce, block.numberOfZeros);
        if (!nonceComputedCorrectly) {
            if (DEBUG)
                System.err.println(myName+" handleConfirmation() from '"+dataFrom+"' Nonce was not computed correctly. block={\n"+block.toString()+"\n}");
            return Constants.Status.INCORRECT_NONCE;
        }

        // Check signature on the block
        final Constants.Status status = checkSignature(dataFrom, signature, bytes);
        if (status != Constants.Status.SUCCESS) {
            if (DEBUG)
                System.err.println(myName+" handleConfirmation() from '"+dataFrom+"' status="+status+"\n"+"block={\n"+block.toString()+"\n}\n");
            return status;
        }

        for (Transaction trans : block.transactions) {
        // Check signature on the aggregate transaction and it's inputs/outputs
            { // Check aggregate transaction
                final String transactionFrom = trans.from;
                final byte[] transactionSignature = trans.signature.array();
                final byte[] transactionBytes = trans.header.getBytes();
                final Constants.Status transactionStatus = checkSignature(transactionFrom, transactionSignature, transactionBytes);
                if (transactionStatus != Constants.Status.SUCCESS) {
                    if (DEBUG)
                        System.err.println(myName+" handleConfirmation() from '"+dataFrom+"' status="+transactionStatus+"\n"+"transaction={\n"+trans.toString()+"\n}\n");
                    return status;
                }
            }

            { // check signature on inputs
                for (Transaction i : trans.inputs) {
                    final String iFrom = i.from;
                    final byte[] iSignature = i.signature.array();
                    final byte[] iBytes = i.header.getBytes();
                    final Constants.Status iStatus = checkSignature(iFrom, iSignature, iBytes);
                    if (iStatus != Constants.Status.SUCCESS) {
                        if (DEBUG)
                            System.err.println(myName+" handleConfirmation() from '"+dataFrom+"' status="+iStatus+"\n"+"transaction={\n"+i.toString()+"\n}\n");
                        return status;
                    }
                }
            }
            { // check signature on outputs
                for (Transaction o : trans.outputs) {
                    final String oFrom = o.from;
                    final byte[] oSignature = o.signature.array();
                    final byte[] oBytes = o.header.getBytes();
                    final Constants.Status oStatus = checkSignature(oFrom, oSignature, oBytes);
                    if (oStatus != Constants.Status.SUCCESS) {
                        if (DEBUG)
                            System.err.println(myName+" handleConfirmation() from '"+dataFrom+"' status="+oStatus+"\n"+"transaction={\n"+o.toString()+"\n}\n");
                        return status;
                    }
                }
            }
        }

        // Everything looks good to me, try and add to blockchain
        return blockchain.addBlock(dataFrom, block);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected int mineHash(MiningTask task, byte[] sha256, long numberOfZerosInPrefix) {
        final int nonce = ProofOfWork.solve(task, sha256, numberOfZerosInPrefix);
        if (DEBUG) {
            String status = "CANCELLED";
            if (nonce >= 0)
                status = "SOLVED";
            System.err.println(myName+" mineHash() "+status+". nonce="+nonce+"\n"+"sha256=["+HashUtils.bytesToHex(sha256)+"]\n");
        }
        return nonce;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append(super.toString()).append(" blockChain={").append(blockchain.toString()).append("}");
        return builder.toString();
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/common/Constants.java
================================================
package com.jwetherell.bitcoin.common;

public abstract class Constants {

    public static enum  Status {    
                                    OWN_TRANSACTION, 
                                    NO_PUBLIC_KEY, 
                                    INCORRECT_NONCE, 
                                    FUTURE_BLOCK, 
                                    BAD_HASH, 
                                    BAD_SIGNATURE, 
                                    BAD_INPUTS, 
                                    DUPLICATE, 
                                    SUCCESS, 
                                    UNKNOWN
   };

}


================================================
FILE: src/com/jwetherell/bitcoin/common/HashUtils.java
================================================
package com.jwetherell.bitcoin.common;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;

public abstract class HashUtils {

    public static final byte[] calculateSha256(String text) {
        byte[] hash2;
        try {
            hash2 = calculateSha256(text.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return hash2;
    }

    public static final byte[] calculateSha256(byte[] utf8Bytes) {
        byte[] hash2;
        try {
            final MessageDigest digest = MessageDigest.getInstance("SHA-256");
            final byte[] hash1 = digest.digest(utf8Bytes);
            hash2 = digest.digest(hash1);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return hash2;
    }

    public static final String bytesToHex(byte[] bytes) {
        final StringBuilder result = new StringBuilder();
        for (byte byt : bytes) 
            result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }

}


================================================
FILE: src/com/jwetherell/bitcoin/common/KeyUtils.java
================================================
package com.jwetherell.bitcoin.common;

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;

public abstract class KeyUtils {

    public static final byte[] signMsg(Signature enc, byte[] bytes) {
        byte[] signed = null;
        try {
            enc.update(bytes);
            signed = enc.sign();
        } catch (Exception e) {
            System.err.println("Could not encode msg. "+e);
        }
        return signed;
    }

    public static final boolean verifyMsg(KeyFactory keyFactory, Signature dec, byte[] publicKey, byte[] signature, byte[] bytes) {
        boolean verified = false;
        try {
            PublicKey key = keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
            dec.initVerify(key);
            dec.update(bytes);
            verified = dec.verify(signature);
        } catch (Exception e) {
            System.err.println("Could not decode msg. "+e);
        }
        return verified;
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/data_model/Block.java
================================================
package com.jwetherell.bitcoin.data_model;

import java.nio.ByteBuffer;
import java.util.Arrays;

import com.jwetherell.bitcoin.common.HashUtils;

public class Block {

    private static final int    FROM_LENGTH             = 4;
    private static final int    BOOLEAN_LENGTH          = 2;
    private static final int    NUM_OF_ZEROS_LENGTH     = 4;
    private static final int    NONCE_LENGTH            = 4;
    private static final int    BLOCK_LENGTH            = 4;
    private static final int    LENGTH_LENGTH           = 4;

    public String               from;
    public boolean              confirmed               = false;
    public int                  numberOfZeros;
    public int                  nonce;
    public int                  blockLength;
    public Transaction[]        transactions;
    public byte[]               prev;
    public byte[]               hash;

    public Block() { }

    public Block(String from, byte[] prevHash, byte[] hash, Transaction[] transactions, int blockLength) {
        this.from = from;
        this.prev = prevHash;
        this.hash = hash;
        this.transactions = transactions;
        this.blockLength = blockLength;
    }

    public int getBufferLength() {
        int transactionsLength = 0;
        for (Transaction t : transactions)
            transactionsLength += LENGTH_LENGTH + t.getBufferLength();
        return  FROM_LENGTH + from.length() +
                BOOLEAN_LENGTH + 
                NUM_OF_ZEROS_LENGTH +
                NONCE_LENGTH +
                BLOCK_LENGTH +
                LENGTH_LENGTH + prev.length + 
                LENGTH_LENGTH + hash.length + 
                LENGTH_LENGTH + transactionsLength;
    }

    public void toBuffer(ByteBuffer buffer) {
        final byte[] fBytes = from.getBytes();
        buffer.putInt(fBytes.length);
        buffer.put(fBytes);
        
        buffer.putChar(getBoolean(confirmed));
        buffer.putInt(numberOfZeros);
        buffer.putInt(nonce);
        buffer.putInt(blockLength);

        buffer.putInt(prev.length);
        buffer.put(prev);

        buffer.putInt(hash.length);
        buffer.put(hash);

        buffer.putInt(transactions.length);
        for (Transaction t : transactions) {
            buffer.putInt(t.getBufferLength());
            t.toBuffer(buffer);
        }
    }

    public void fromBuffer(ByteBuffer buffer) {
        final int fLength = buffer.getInt();
        final byte[] fBytes = new byte[fLength];
        buffer.get(fBytes, 0, fLength);
        from = new String(fBytes);

        confirmed = parseBoolean(buffer.getChar());
        numberOfZeros = buffer.getInt();
        nonce = buffer.getInt();
        blockLength = buffer.getInt();

        { // previous hash
            final int length = buffer.getInt();
            prev = new byte[length];
            buffer.get(prev);
        }

        { // next hash
            final int length = buffer.getInt();
            hash = new byte[length];
            buffer.get(hash);
        }

        int tLength = buffer.getInt();
        transactions =  new Transaction[tLength];
        for (int i=0; i < tLength; i++) {
            int length = buffer.getInt();
            final byte[] bytes = new byte[length];
            buffer.get(bytes);
            final ByteBuffer bb = ByteBuffer.wrap(bytes);
            final Transaction t = new Transaction();
            t.fromBuffer(bb);
            transactions[i] = t;
        }
    }

    private static final char getBoolean(boolean bool) {
        return (bool?'T':'F');
    }

    private static final boolean parseBoolean(char bool) {
        return (bool=='T'?true:false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        int hashCode = 0;
        hashCode += from.length();
        if (confirmed)
            hashCode += 1;
        hashCode += nonce;
        hashCode += blockLength;
        hashCode += numberOfZeros;
        hashCode += transactions.length;
        for (Transaction t : transactions)
            hashCode += t.hashCode();
        for (byte b : prev)
            hashCode += b;
        for (byte b : hash)
            hashCode += b;
        return 31 * hashCode;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Block))
            return false;
        Block c = (Block) o;
        if (!(c.from.equals(from)))
            return false;
        if (confirmed != c.confirmed)
            return false;
        if (nonce != c.nonce)
            return false;
        if (blockLength != c.blockLength)
            return false;
        if (numberOfZeros != c.numberOfZeros)
            return false;
        if (c.transactions.length != this.transactions.length)
            return false;
        { // compare transactions
            for (int i=0; i<c.transactions.length; i++) {
                if (!(c.transactions[i].equals(this.transactions[i])))
                    return false;
            }
        }
        if (!(Arrays.equals(c.prev, prev)))
            return false;
        if (!(Arrays.equals(c.hash, hash)))
            return false;
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("isValid=").append(confirmed).append("\n");
        builder.append("numberOfZerosToCompute=").append(numberOfZeros).append("\n");
        builder.append("nonce=").append(nonce).append("\n");
        builder.append("blockLength=").append(blockLength).append("\n");
        builder.append("prev=[").append(HashUtils.bytesToHex(prev)).append("]\n");
        builder.append("hash=[").append(HashUtils.bytesToHex(hash)).append("]\n");
        builder.append("block={").append("\n");
        for (Transaction t : transactions) {
            builder.append("transaction={").append("\n");
            builder.append(t.toString()).append("\n");
            builder.append("}").append("\n");
        }
        builder.append("}");
        return builder.toString();
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/data_model/Data.java
================================================
package com.jwetherell.bitcoin.data_model;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Data {

    private static final int    LENGTH_LENGTH   = 4;
    private static final int    FROM_LENGTH     = 4;
    private static final int    TO_LENGTH       = 4;

    public String               from;
    public String               to;
    public InetAddress          sourceAddr;
    public int                  sourcePort;
    public InetAddress          destAddr;
    public int                  destPort;
    public ByteBuffer           signature;
    public ByteBuffer           message;

    public Data() { }

    public Data(String from, String sourceAddr, int sourcePort, 
                String to, String destAddr, int destPort, 
                byte[] signature, byte[] bytes) {
        this.from = from;
        this.to = to;
        try {
            this.sourceAddr = InetAddress.getByName(sourceAddr);
            this.destAddr = InetAddress.getByName(destAddr);
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
        this.sourcePort = sourcePort;
        this.destPort = destPort;

        this.signature = ByteBuffer.allocate(signature.length);
        this.signature.put(signature);
        this.signature.flip();

        this.message = ByteBuffer.allocate(bytes.length);
        this.message.put(bytes);
        this.message.flip();
    }

    public int getBufferLength() {
        return  FROM_LENGTH + from.getBytes().length +
                TO_LENGTH + to.getBytes().length +
                LENGTH_LENGTH + sourceAddr.getHostAddress().getBytes().length + 
                LENGTH_LENGTH + String.valueOf(sourcePort).getBytes().length + 
                LENGTH_LENGTH + destAddr.getHostAddress().getBytes().length + 
                LENGTH_LENGTH + String.valueOf(destPort).getBytes().length + 
                LENGTH_LENGTH + signature.limit() +
                LENGTH_LENGTH + message.limit();
    }

    public void toBuffer(ByteBuffer buffer) {
        final byte[] fBytes = from.getBytes();
        buffer.putInt(fBytes.length);
        buffer.put(fBytes);

        final byte[] oBytes = to.getBytes();
        buffer.putInt(oBytes.length);
        buffer.put(oBytes);

        { // Source
            final byte[] hBytes = sourceAddr.getHostAddress().getBytes();
            final int hLength = hBytes.length;
            buffer.putInt(hLength);
            buffer.put(hBytes);
    
            final byte[] pBytes = String.valueOf(sourcePort).getBytes();
            final int pLength = pBytes.length;
            buffer.putInt(pLength);
            buffer.put(pBytes);
        }

        { // Destination
            final byte[] hBytes = destAddr.getHostAddress().getBytes();
            final int hLength = hBytes.length;
            buffer.putInt(hLength);
            buffer.put(hBytes);
    
            final byte[] pBytes = String.valueOf(destPort).getBytes();
            final int pLength = pBytes.length;
            buffer.putInt(pLength);
            buffer.put(pBytes);
        }

        // Sig
        buffer.putInt(signature.limit());
        buffer.put(signature);

        // Data
        buffer.putInt(message.limit());
        buffer.put(message);
    }

    public void fromBuffer(ByteBuffer buffer) {
        final int fLength = buffer.getInt();
        final byte[] fBytes = new byte[fLength];
        buffer.get(fBytes, 0, fLength);
        from = new String(fBytes);

        final int oLength = buffer.getInt();
        final byte[] oBytes = new byte[oLength];
        buffer.get(oBytes, 0, oLength);
        to = new String(oBytes);

        { // Source
            final int hLength = buffer.getInt();
            final byte[] hostBytes = new byte[hLength];
            buffer.get(hostBytes);
            final String sHost = new String(hostBytes);
            try {
                this.sourceAddr = InetAddress.getByName(sHost);
            } catch (UnknownHostException e) {
                throw new RuntimeException(e);
            }
    
            final int pLength = buffer.getInt();
            final byte[] portBytes = new byte[pLength];
            buffer.get(portBytes);
            final String sPort = new String(portBytes);
            this.sourcePort = Integer.parseInt(sPort);
        }

        { // Destination
            final int hLength = buffer.getInt();
            final byte[] hostBytes = new byte[hLength];
            buffer.get(hostBytes);
            final String sHost = new String(hostBytes);
            try {
                this.destAddr = InetAddress.getByName(sHost);
            } catch (UnknownHostException e) {
                throw new RuntimeException(e);
            }
    
            final int pLength = buffer.getInt();
            final byte[] portBytes = new byte[pLength];
            buffer.get(portBytes);
            final String sPort = new String(portBytes);
            this.destPort = Integer.parseInt(sPort);
        }

        // Sig
        final int sLength = buffer.getInt();
        final byte[] sBytes = new byte[sLength];
        buffer.get(sBytes, 0, sLength);
        this.signature = ByteBuffer.allocate(sBytes.length);
        this.signature.put(sBytes);

        // Data
        final int dLength = buffer.getInt();
        final byte[] dBytes = new byte[dLength];
        buffer.get(dBytes, 0, dLength);
        this.message = ByteBuffer.allocate(dBytes.length);
        this.message.put(dBytes);

        this.message.flip();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Data))
            return false;
        Data d = (Data) o;
        if (!(d.from.equals(this.from)))
            return false;
        if (!(d.to.equals(this.to)))
            return false;
        if (!(sourceAddr.equals(d.sourceAddr)))
            return false;
        if (sourcePort != d.sourcePort)
            return false;
        if (!(destAddr.equals(d.destAddr)))
            return false;
        if (destPort != d.destPort)
            return false;
        if (!(Arrays.equals(this.signature.array(), d.signature.array())))
            return false;
        if (!(Arrays.equals(this.message.array(), d.message.array())))
            return false;
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("from='").append(from).append("'\n");
        builder.append("source=").append(sourceAddr.getHostAddress()).append(":").append(sourcePort).append("\n");
        builder.append("to='").append(to).append("'\n");
        builder.append("destination=").append(destAddr.getHostAddress()).append(":").append(destPort).append("\n");
        builder.append("data=").append(new String(message.array()));
        return builder.toString();
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/data_model/Transaction.java
================================================
package com.jwetherell.bitcoin.data_model;

import java.nio.ByteBuffer;
import java.security.Signature;
import java.util.Arrays;

import com.jwetherell.bitcoin.common.HashUtils;
import com.jwetherell.bitcoin.common.KeyUtils;

public class Transaction {

    private static final int    FROM_LENGTH         = 4;
    private static final int    TO_LENGTH           = 4;
    private static final int    TIMESTAMP_LENGTH    = 8;
    private static final int    HEADER_LENGTH       = 4;
    private static final int    VALUE_LENGTH        = 4;
    private static final int    LENGTH_LENGTH       = 4;

    public String               from;
    public String               to;
    public long                 timestamp;
    public String               header;
    public int                  value;
    public ByteBuffer           signature;

    public Transaction[]        inputs;
    public Transaction[]        outputs;

    public Transaction() { }

    public Transaction(String from, String to, String header, int value, byte[] signature, Transaction[] inputs, Transaction[] outputs) {
        this.from = from;
        this.to = to;
        this.timestamp = 0;
        this.header = header;
        this.value = value;
        final byte[] sig = new byte[signature.length];
        System.arraycopy(signature, 0, sig, 0, signature.length);
        this.signature = ByteBuffer.wrap(sig);

        this.inputs = new Transaction[inputs.length];
        for (int i=0; i<inputs.length; i++)
            this.inputs[i] = inputs[i];

        this.outputs = new Transaction[outputs.length];
        for (int i=0; i<outputs.length; i++)
            this.outputs[i] = outputs[i];
    }

    /** Create the aggregate Transaction and sign it **/
    public static final Transaction newSignedTransaction(Signature signature, 
                                                         String from, String to, 
                                                         String header, int value, 
                                                         Transaction[] inputs, Transaction[] outputs)
    {
        final byte[] sig = KeyUtils.signMsg(signature, header.getBytes());
        Transaction transaction = new Transaction(from, to, header, value, sig, inputs, outputs);
        return transaction;
    }

    public void updateTimestamp() {
        this.timestamp = System.currentTimeMillis();
    }

    public int getBufferLength() {
        int iLength = 0;
        for (Transaction t : inputs)
            iLength += LENGTH_LENGTH + t.getBufferLength();

        int oLength = 0;
        for (Transaction t : outputs)
            oLength += LENGTH_LENGTH + t.getBufferLength();

        int length =    LENGTH_LENGTH + signature.limit() +
                        LENGTH_LENGTH + iLength +
                        LENGTH_LENGTH + oLength +
                        TIMESTAMP_LENGTH +
                        VALUE_LENGTH +
                        HEADER_LENGTH + header.getBytes().length + 
                        FROM_LENGTH + from.getBytes().length + 
                        TO_LENGTH + to.getBytes().length;
        return length;
    }

    public void toBuffer(ByteBuffer buffer) {
        { // signature
            buffer.putInt(signature.limit());
            buffer.put(signature);
            signature.flip();
        }

        { // inputs
            buffer.putInt(inputs.length);
            for (Transaction t : inputs) {
                buffer.putInt(t.getBufferLength());
                t.toBuffer(buffer);
                // do not flip buffer here
            }
        }

        { // outputs
            buffer.putInt(outputs.length);
            for (Transaction t : outputs) {
                buffer.putInt(t.getBufferLength());
                t.toBuffer(buffer);
                // do not flip buffer here
            }
        }

        buffer.putLong(timestamp);
        buffer.putInt(value);

        final int mLength = header.length();
        buffer.putInt(mLength);
        final byte[] mBytes = header.getBytes();
        buffer.put(mBytes);

        final byte[] fBytes = from.getBytes();
        buffer.putInt(fBytes.length);
        buffer.put(fBytes);

        final byte[] oBytes = to.getBytes();
        buffer.putInt(oBytes.length);
        buffer.put(oBytes);
    }

    public void fromBuffer(ByteBuffer buffer) {
        { // signature
            int sLength = buffer.getInt();
            byte[] bSignature = new byte[sLength];
            buffer.get(bSignature);
            this.signature = ByteBuffer.wrap(bSignature);
        }

        { // inputs
            int iLength = buffer.getInt();
            this.inputs = new Transaction[iLength];
            for (int i=0; i<iLength; i++) {
                int tLength = buffer.getInt();
                Transaction t = new Transaction();
                final byte[] bytes = new byte[tLength];
                buffer.get(bytes);
                ByteBuffer bb = ByteBuffer.wrap(bytes);
                t.fromBuffer(bb);
                this.inputs[i] = t;
            }
        }

        { // ouputs
            int oLength = buffer.getInt();
            this.outputs = new Transaction[oLength];
            for (int i=0; i<oLength; i++) {
                int tLength = buffer.getInt();
                Transaction t = new Transaction();
                final byte[] bytes = new byte[tLength];
                buffer.get(bytes);
                ByteBuffer bb = ByteBuffer.wrap(bytes);
                t.fromBuffer(bb);
                this.outputs[i] = t;
            }
        }

        timestamp = buffer.getLong();
        value = buffer.getInt();

        final int mLength = buffer.getInt();
        final byte[] mBytes = new byte[mLength];
        buffer.get(mBytes, 0, mLength);
        header = new String(mBytes);

        final int fLength = buffer.getInt();
        final byte[] fBytes = new byte[fLength];
        buffer.get(fBytes, 0, fLength);
        from = new String(fBytes);

        final int tLength = buffer.getInt();
        final byte[] tBytes = new byte[tLength];
        buffer.get(tBytes, 0, tLength);
        to = new String(tBytes);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        int hashCode = 0;
        hashCode += from.length();
        hashCode += to.length();
        hashCode += timestamp;
        hashCode += header.length();
        hashCode += value;
        for (byte b :signature.array())
            hashCode += b;
        for (Transaction t : inputs)
            hashCode += t.hashCode();
        for (Transaction t : outputs)
            hashCode += t.hashCode();
        return 31 * hashCode;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Transaction))
            return false;
        Transaction c = (Transaction) o;
        { // signature
            if (c.signature.limit() != this.signature.limit())
                return false;
            if (!(Arrays.equals(c.signature.array(), this.signature.array())))
                return false;
        }
        { // inputs
            if (c.inputs.length != this.inputs.length)
                return false;
            for (int i=0; i<c.inputs.length; i++)
                if (!(c.inputs[i].equals(inputs[i])))
                    return false;
        }
        { // ouputs
            if (c.outputs.length != this.outputs.length)
                return false;
            for (int i=0; i<c.outputs.length; i++)
                if (!(c.outputs[i].equals(outputs[i])))
                    return false;
        }
        if (c.timestamp != this.timestamp)
            return false;
        if (c.value != this.value)
            return false;
        if (!(c.from.equals(this.from)))
            return false;
        if (!(c.to.equals(this.to)))
            return false;
        if (!(c.header.equals(this.header)))
            return false;
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("signature=[").append(HashUtils.bytesToHex(this.signature.array())).append("]\n");
        builder.append("inputs=").append(inputs.length).append("\n");
        builder.append("outputs=").append(outputs.length).append("\n");
        builder.append("time='").append(timestamp).append("'\n");
        builder.append("value='").append(value).append("'\n");
        builder.append("from='").append(from).append("'\n");
        builder.append("to='").append(to).append("'\n");
        builder.append("header=[").append(header).append("]");
        return builder.toString();
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/interfaces/MessageListener.java
================================================
package com.jwetherell.bitcoin.interfaces;

public interface MessageListener {

    public void onMessage(Receiver recv);

}


================================================
FILE: src/com/jwetherell/bitcoin/interfaces/Receiver.java
================================================
package com.jwetherell.bitcoin.interfaces;

import java.util.Queue;

import com.jwetherell.bitcoin.data_model.Data;

public interface Receiver {

    public Queue<Data> getQueue();

    public boolean isReady();

    public String getHost();

    public int getPort();
}


================================================
FILE: src/com/jwetherell/bitcoin/interfaces/Sender.java
================================================
package com.jwetherell.bitcoin.interfaces;

import java.util.Queue;

import com.jwetherell.bitcoin.data_model.Data;

public interface Sender {

    public Queue<Data> getQueue();

    public boolean isReady();
}


================================================
FILE: src/com/jwetherell/bitcoin/networking/Multicast.java
================================================
package com.jwetherell.bitcoin.networking;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.interfaces.Sender;

public class Multicast {

    private static final boolean    DEBUG       = Boolean.getBoolean("debug_all");

    public static final int         PORT        = 5000;
    public static final String      GROUP       = "225.4.5.6";

    public static MulticastSocket createReceiver() throws IOException {
        // Create the socket and bind it to port 'port'.
        final MulticastSocket s = new MulticastSocket(PORT);
        // join the multicast group
        s.joinGroup(InetAddress.getByName(GROUP));
        // Now the socket is set up and we are ready to receive packets
        return s;
    }

    public static void destoryReceiver(MulticastSocket s) throws UnknownHostException, IOException {
        if (s == null)
            return;

        // Leave the multicast group and close the socket
        s.leaveGroup(InetAddress.getByName(GROUP));
        s.close();
    }

    /**
     * Blocking call
     */
    public static boolean recvData(MulticastSocket s, byte[] buffer) throws IOException {
        s.setSoTimeout(100);
        // Create a DatagramPacket and do a receive
        final DatagramPacket pack = new DatagramPacket(buffer, buffer.length);
        try {
            s.receive(pack);
        } catch (SocketTimeoutException e) {
            return false;
        }
        // We have finished receiving data
        return true;
    }

    public static MulticastSocket createSender() throws IOException {
        // Create the socket but we don't bind it as we are only going to send data
        final MulticastSocket s = new MulticastSocket();
        // Note that we don't have to join the multicast group if we are only
        // sending data and not receiving
        return s;
    }

    public static void destroySender(MulticastSocket s) throws IOException {
        if (s == null)
            return;

        // When we have finished sending data close the socket
        s.close();
    }

    public static void sendData(MulticastSocket s, int ourTTL, byte[] buffer) throws IOException {
        // Create a DatagramPacket 
        final DatagramPacket pack = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(GROUP), PORT);
        // Get the current TTL, set our TTL, do a send, reset the TTL  
        final int ttl = s.getTimeToLive(); 
        s.setTimeToLive(ourTTL); 
        s.send(pack); 
        s.setTimeToLive(ttl);
    }

    public static final class Peer {

        private static final int BUFFER_SIZE = 10*1024;

        public static final class RunnableRecv implements Runnable, Receiver {

            public static volatile boolean                      run         = true;

            private final ConcurrentLinkedQueue<Data>           toRecv      = new ConcurrentLinkedQueue<Data>();
            private final MessageListener                       listener;

            private volatile boolean                            isReady     = false;

            public RunnableRecv(MessageListener listener) {
                run = true;
                this.listener = listener;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Queue<Data> getQueue() {
                return toRecv;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean isReady() {
                return isReady;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String getHost() {
                return GROUP;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public int getPort() {
                return PORT;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                MulticastSocket s = null;
                try {
                    if (DEBUG)
                        System.out.println("Creating receiver");
                    s = Multicast.createReceiver();
                    final byte[] array = new byte[BUFFER_SIZE];
                    final ByteBuffer bb = ByteBuffer.wrap(array);
                    isReady = true;
                    while (run) {
                        bb.clear();
                        final boolean p = Multicast.recvData(s, array);
                        if (!p) {
                            Thread.yield();
                            continue;
                        }

                        final Data data = new Data();
                        data.fromBuffer(bb);

                        if (DEBUG)
                            System.out.println("Server received '"+new String(data.message.array())+"' from "+data.sourceAddr.getHostAddress()+":"+data.sourcePort);

                        toRecv.add(data);
                        listener.onMessage(this);

                        Thread.yield();
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        Multicast.destoryReceiver(s);
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        public static final class RunnableSend implements Runnable, Sender {

            private static final int                            ttl         = 1;

            public static volatile boolean                      run         = true;

            public final ConcurrentLinkedQueue<Data>            toSend      = new ConcurrentLinkedQueue<Data>();

            private volatile boolean                            isReady     = false;

            public RunnableSend() {
                run = true;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Queue<Data> getQueue() {
                return toSend;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean isReady() {
                return isReady;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                MulticastSocket s = null;
                try {
                    if (DEBUG)
                        System.out.println("Creating sender");
                    s = Multicast.createSender();
                    final byte[] buffer = new byte[BUFFER_SIZE];
                    final ByteBuffer bb = ByteBuffer.wrap(buffer);
                    isReady = true;
                    while (run) {
                        if (DEBUG && toSend.size()>1)
                            System.out.println("Client toSend size="+toSend.size());
                        final Data d = toSend.poll();
                        if (d != null) {
                            bb.clear();
                            d.toBuffer(bb);
                            bb.flip();

                            if (DEBUG)
                                System.out.println("Client ("+d.sourceAddr.getHostAddress()+":"+d.sourcePort+") sending '"+new String(d.message.array())+"'");

                            Multicast.sendData(s, ttl, buffer);
                        }

                        Thread.yield();
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        Multicast.destroySender(s);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/networking/TCP.java
================================================
package com.jwetherell.bitcoin.networking;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.interfaces.Sender;

public class TCP {

    private static final boolean    DEBUG       = Boolean.getBoolean("debug_all");

    public static final String      LOCAL       = "127.0.0.1";

    public static int               port        = 2221;

    public static ServerSocket createServer(int port) throws IOException {
        final ServerSocket serverSocket = new ServerSocket(port);
        return serverSocket;
    }

    public static void destoryServer(ServerSocket s) throws IOException {
        if (s != null)
            s.close();
    }

    public static Socket createClient(String host, int port) throws IOException {
        final Socket outgoingSocket = new Socket(host, port);
        return outgoingSocket;
    }

    public static void destoryClient(Socket s) throws IOException {
        if (s != null)
            s.close();
    }

    public static void sendData(Socket socket, byte[] buffer) throws IOException {
        final OutputStream out = socket.getOutputStream(); 
        final DataOutputStream dos = new DataOutputStream(out);
        dos.write(buffer);
        dos.flush();
        dos.close();
        out.flush();
        out.close();
    }

    /**
     * Blocking call
     */
    public static boolean recvData(ServerSocket serverSocket, byte[] buffer) throws IOException {
        serverSocket.setSoTimeout(10);
        Socket incomingSocket = null;
        try {
            incomingSocket = serverSocket.accept();
        } catch (SocketTimeoutException e) {
            return false;
        }
        final InputStream input = incomingSocket.getInputStream();
        input.read(buffer);
        input.close();
        return true;
    }

    public static final class Peer { 

        private static final int        BUFFER_SIZE     = 10*1024;

        private Peer() { }

        public static final class RunnableRecv implements Runnable, Receiver {

            public static volatile boolean                      run         = true;

            private final ConcurrentLinkedQueue<Data>           toRecv      = new ConcurrentLinkedQueue<Data>();
            private final int                                   port;
            private final MessageListener                       listener;

            private volatile boolean                            isReady     = false;

            public RunnableRecv(MessageListener listener) {
                run = true;
                this.port = TCP.port++;
                this.listener = listener;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Queue<Data> getQueue() {
                return toRecv;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean isReady() {
                return isReady;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String getHost() {
                return LOCAL;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public int getPort() {
                return port;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                ServerSocket s = null;
                try {
                    if (DEBUG) 
                        System.out.println("Creating server. port="+port);
                    s = TCP.createServer(port);
                    final byte[] array = new byte[BUFFER_SIZE];
                    final ByteBuffer bb = ByteBuffer.wrap(array);
                    isReady = true;
                    while (run) {
                        bb.clear();
                        final boolean p = TCP.recvData(s,array);
                        if (!p) {
                            Thread.yield();
                            continue;
                        }

                        final Data data = new Data();
                        data.fromBuffer(bb);

                        if (DEBUG) 
                            System.out.println("Server ("+getHost()+":"+getPort()+") received '"+new String(data.message.array())+"' from "+data.sourceAddr.getHostAddress()+":"+data.sourcePort);

                        toRecv.add(data);
                        listener.onMessage(this);

                        Thread.yield();
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        TCP.destoryServer(s);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        public static final class RunnableSend implements Runnable, Sender {

            public static volatile boolean                      run         = true;

            private final ConcurrentLinkedQueue<Data>           toSend      = new ConcurrentLinkedQueue<Data>();

            private volatile boolean                            isReady     = false;

            public RunnableSend() {
                run = true;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Queue<Data> getQueue() {
                return toSend;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean isReady() {
                return isReady;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                Socket s = null;
                try {
                    final byte[] buffer = new byte[BUFFER_SIZE];
                    final ByteBuffer bb = ByteBuffer.wrap(buffer);
                    isReady = true;
                    while (run) {
                        if (DEBUG && toSend.size()>1)
                            System.out.println("Client toSend size="+toSend.size());
                        final Data d = toSend.poll();
                        if (d != null) {
                            bb.clear();
                            d.toBuffer(bb);
                            bb.flip();

                            if (DEBUG) 
                                System.out.println("Client ("+d.sourceAddr.getHostAddress()+":"+d.sourcePort+") sending '"+new String(d.message.array())+"'");

                            s = TCP.createClient(d.destAddr.getHostAddress(), d.destPort);
                            TCP.sendData(s, buffer);
                        }

                        Thread.yield();
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally { 
                    try {
                        TCP.destoryClient(s);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/networking/UDP.java
================================================
package com.jwetherell.bitcoin.networking;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.interfaces.Sender;

public class UDP {

    private static final boolean    DEBUG       = Boolean.getBoolean("debug_all");

    public static final String      LOCAL       = "127.0.0.1";

    public static int               port        = 1111;

    public static DatagramSocket createServer(int port) throws SocketException {
        final DatagramSocket serverSocket = new DatagramSocket(port);
        return serverSocket;
    }

    public static void destoryServer(DatagramSocket s) {
        if (s != null)
            s.close();
    }

    public static DatagramSocket createClient() throws SocketException {
        final DatagramSocket clientSocket = new DatagramSocket();
        return clientSocket;
    }

    public static void destoryClient(DatagramSocket s) {
        if (s != null)
            s.close();
    }

    public static void sendData(DatagramSocket socket, InetAddress IPAddress, int port, byte[] buffer) throws IOException {
        final DatagramPacket sendPacket = new DatagramPacket(buffer, buffer.length, IPAddress, port);
        socket.send(sendPacket);
    }

    /**
     * Blocking call
     */
    public static boolean recvData(DatagramSocket socket, byte[] buffer) throws IOException {
        socket.setSoTimeout(100);
        final DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
        try {
            socket.receive(receivePacket);
        } catch (SocketTimeoutException e) {
            return false;
        }
        return true;
    }

    public static final class Peer { 

        private static final int        BUFFER_SIZE     = 10*1024;

        private Peer() { }

        public static final class RunnableRecv implements Runnable, Receiver {

            public static volatile boolean                      run         = true;

            private final ConcurrentLinkedQueue<Data>           toRecv      = new ConcurrentLinkedQueue<Data>();
            private final int                                   port;
            private final MessageListener                       listener;

            private volatile boolean                            isReady     = false;

            public RunnableRecv(MessageListener listener) {
                run = true;
                this.port = UDP.port++;
                this.listener = listener;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Queue<Data> getQueue() {
                return toRecv;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean isReady() {
                return isReady;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String getHost() {
                return LOCAL;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public int getPort() {
                return port;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                DatagramSocket s = null;
                try {
                    if (DEBUG) 
                        System.out.println("Creating server. port="+port);
                    s = UDP.createServer(port);
                    final byte[] array = new byte[BUFFER_SIZE];
                    final ByteBuffer bb = ByteBuffer.wrap(array);
                    isReady = true;
                    while (run) {
                        bb.clear();
                        final boolean p = UDP.recvData(s,bb.array());
                        if (!p) {
                            Thread.yield();
                            continue;
                        }

                        final Data data = new Data();
                        data.fromBuffer(bb);

                        if (DEBUG) 
                            System.out.println("Server ("+getHost()+":"+getPort()+") received '"+new String(data.message.array())+"' from "+data.sourceAddr.getHostAddress()+":"+data.sourcePort);

                        toRecv.add(data);
                        listener.onMessage(this);

                        Thread.yield();
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    UDP.destoryServer(s);
                }
            }
        };

        public static final class RunnableSend implements Runnable, Sender {

            public static volatile boolean                      run         = true;

            private final ConcurrentLinkedQueue<Data>           toSend      = new ConcurrentLinkedQueue<Data>();

            private volatile boolean                            isReady     = false;

            public RunnableSend() {
                run = true;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Queue<Data> getQueue() {
                return toSend;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean isReady() {
                return isReady;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                DatagramSocket s = null;
                try {
                    if (DEBUG) 
                        System.out.println("Creating client");
                    s = UDP.createClient();
                    final byte[] buffer = new byte[BUFFER_SIZE];
                    final ByteBuffer bb = ByteBuffer.wrap(buffer);
                    isReady = true;
                    while (run) {
                        if (DEBUG && toSend.size()>1)
                            System.out.println("Client toSend size="+toSend.size());
                        final Data d = toSend.poll();
                        if (d != null) {
                            bb.clear();
                            d.toBuffer(bb);
                            bb.flip();

                            if (DEBUG) 
                                System.out.println("Client ("+d.sourceAddr.getHostAddress()+":"+d.sourcePort+") sending '"+new String(d.message.array())+"'");

                            UDP.sendData(s, d.destAddr, d.destPort, buffer);
                        }

                        Thread.yield();
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally { 
                    if (s != null)
                        UDP.destoryClient(s);
                }
            }
        };
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/AllTest.java
================================================
package com.jwetherell.bitcoin.test;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

//@formatter:off
@RunWith(Suite.class)
@SuiteClasses(
    {
        com.jwetherell.bitcoin.test.ProofOfWorkTest.class,
        com.jwetherell.bitcoin.test.BlockChainTest.class,
        com.jwetherell.bitcoin.test.EncodeDecodeTest.class,
        com.jwetherell.bitcoin.test.TransactionTest.class,
        com.jwetherell.bitcoin.test.BlockTest.class,
        com.jwetherell.bitcoin.test.DataTest.class,
        com.jwetherell.bitcoin.test.UDPTest.class,
        com.jwetherell.bitcoin.test.TCPTest.class,
        com.jwetherell.bitcoin.test.MulticastTest.class,
        com.jwetherell.bitcoin.test.PeerTest.class,
        com.jwetherell.bitcoin.test.WalletTest.class,
    }
)
//@formatter:on

public class AllTest {
    // Ignore
}


================================================
FILE: src/com/jwetherell/bitcoin/test/BlockChainTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.nio.ByteBuffer;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.Blockchain;
import com.jwetherell.bitcoin.data_model.Transaction;

public class BlockChainTest {

    private static final Transaction[]  EMPTY       = new Transaction[0];
    private static final byte[]         SIGNATURE   = "sig".getBytes();

    @Test
    public void test() {

        final byte[] hash1;
        {
            final byte[] hash = "This is a hash".getBytes();
            final Transaction block = new Transaction("me","you","msg",7,SIGNATURE,EMPTY,EMPTY);
            final ByteBuffer buffer = ByteBuffer.allocate(block.getBufferLength());
            block.toBuffer(buffer);
            buffer.flip();

            final byte[] bytes = buffer.array();
            hash1 = Blockchain.getNextHash(hash, bytes);
        }

        final byte[] hash2;
        {
            byte[] hash = "This is a hash".getBytes();
            final Transaction block = new Transaction("me","you","msg",7,SIGNATURE,EMPTY,EMPTY);
            final ByteBuffer buffer = ByteBuffer.allocate(block.getBufferLength());
            block.toBuffer(buffer);
            buffer.flip();

            final byte[] bytes = buffer.array();
            hash2 = Blockchain.getNextHash(hash, bytes);
        }

        Assert.assertTrue(Arrays.equals(hash1, hash2));
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/BlockTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.nio.ByteBuffer;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.data_model.Transaction;
import com.jwetherell.bitcoin.data_model.Block;

public class BlockTest {

    private static final Transaction[]  EMPTY       = new Transaction[0];
    private static final byte[]         SIGNATURE   = "sig".getBytes();


    @Test
    public void testSerialization() {
        final String f = "me";
        final String t = "you";
        final String m = "Here is a coin for you!";

        final Transaction trans = new Transaction(f, t, m, 10, SIGNATURE, EMPTY, EMPTY);
        final Transaction[] transactions = new Transaction[]{ trans };
        byte[] prev = "I am a hash!".getBytes();
        byte[] hash = "I am also a hash!".getBytes();
        final Block block = new Block(f,prev,hash,transactions,1);
        final ByteBuffer buffer = ByteBuffer.allocate(block.getBufferLength());
        block.toBuffer(buffer);
        buffer.flip();

        final Block block2 = new Block();
        block2.fromBuffer(buffer);

        Assert.assertTrue(block.equals(block2));
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/DataTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.nio.ByteBuffer;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.data_model.Data;

public class DataTest {

    @Test
    public void testSerialization() {
        final String from = "me";
        final String sHost = "127.0.0.1";
        final int sPort = 1024;
        final String to = "you";
        final String dHost = "localhost";
        final int dPort = 1025;
        final byte[] sig = "sig".getBytes();
        final byte[] msg = "This is a message".getBytes();

        final Data d1 = new Data(from,sHost,sPort,to,dHost,dPort,sig,msg);
        final ByteBuffer buffer = ByteBuffer.allocate(d1.getBufferLength());
        d1.toBuffer(buffer);
        buffer.flip();

        final Data d2 = new Data();
        d2.fromBuffer(buffer);

        Assert.assertTrue(d1.equals(d2));
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/EncodeDecodeTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;

import org.junit.Assert;
import org.junit.Test;

public class EncodeDecodeTest {
    
    @Test
    public void test1() {
        byte[] data = "hello.".getBytes();

        /* Test generating and verifying a DSA signature */
        try {
            /* generate a key pair */
            final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
            keyGen.initialize(1024, new SecureRandom());
            final KeyPair pair = keyGen.generateKeyPair();

            /* create a Signature object to use for signing and verifying */
            final Signature dsa = Signature.getInstance("SHA/DSA"); 

            /* initialize the Signature object for signing */
            final PrivateKey priv = pair.getPrivate();
            dsa.initSign(priv);

            /* Update and sign the data */
            dsa.update(data);

            /* Now that all the data to be signed has been read in, sign it */
            final byte[] sig = dsa.sign();

            /* Verify the signature */

            /* Initialize the Signature object for verification */
            final PublicKey pub = pair.getPublic();
            dsa.initVerify(pub);

            /* Update and verify the data */
            dsa.update(data);

            final boolean verified = dsa.verify(sig);
            Assert.assertTrue(verified);
        } catch (Exception e) {
            System.err.println("Caught exception " + e.toString());
        }
    }
    
    @Test
    public void test2() {
        byte[] data = "hello.".getBytes();

        /* Test generating and verifying a DSA signature */
        try {
            /* generate a key pair */
            final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
            keyGen.initialize(1024, new SecureRandom());
            final KeyPair pair = keyGen.generateKeyPair();

            /* create a Signature object to use
             * for signing and verifying */
            final Signature dsa = Signature.getInstance("SHA/DSA"); 

            /* initialize the Signature object for signing */
            final PrivateKey priv = pair.getPrivate();
            dsa.initSign(priv);

            /* Update and sign the data */
            dsa.update(data);

            /* Now that all the data to be signed has been read in, sign it */
            final byte[] sig = dsa.sign();

            /* Verify the signature */

            /* Initialize the Signature object for verification */
            final PublicKey pub = pair.getPublic();
            /* Encode the public key into a byte array */
            final byte[] encoded = pub.getEncoded();
            /* Get the public key from the encoded byte array */
            final PublicKey fromEncoded = KeyFactory.getInstance("DSA", "SUN").generatePublic(new X509EncodedKeySpec(encoded));
            dsa.initVerify(fromEncoded);

            /* Update and verify the data */
            dsa.update(data);

            final boolean verified = dsa.verify(sig);
            Assert.assertTrue(verified);
        } catch (Exception e) {
            System.err.println("Caught exception " + e.toString());
        }
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/MulticastTest.java
================================================
package com.jwetherell.bitcoin.test;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.networking.Multicast;

public class MulticastTest {

    private static final boolean DEBUG = Boolean.getBoolean("debug");

    @Test(timeout=5000)
    public void test() throws InterruptedException {
        final String from = "me";
        final String to = "you";
        final byte[] sig = "sig".getBytes();
        final byte[] toSend = "Hello world.".getBytes();
        final MessageListener listener = new MessageListener() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void onMessage(Receiver recv) {
                Data d = recv.getQueue().poll();
                while (d != null) {
                    final byte[] data = d.message.array();
                    if (DEBUG)
                        System.out.println("Listener received '"+new String(data)+"'");
                    Assert.assertTrue(isEquals(toSend,data,toSend.length));
                    d = recv.getQueue().poll();
                }
                Multicast.Peer.RunnableRecv.run = false;
                Multicast.Peer.RunnableSend.run = false;
            }
        };

        // Start both the sender and receiver
        final Multicast.Peer.RunnableRecv recv = new Multicast.Peer.RunnableRecv(listener);
        final Thread r = new Thread(recv,"recv");
        r.start();     

        final Multicast.Peer.RunnableSend send = new Multicast.Peer.RunnableSend();
        final Thread s = new Thread(send,"send");
        s.start();

        // Wait for everyone to initialize
        while (recv.isReady()==false || send.isReady()==false) {
            Thread.yield();
        }

        final Data data = new Data(from, recv.getHost(), recv.getPort(), to, recv.getHost(), recv.getPort(), sig, toSend);
        send.getQueue().add(data);

        // Wait for threads to finish
        r.join();
        s.join();
    }

    private static final boolean isEquals(byte[] a, byte[] b, int length) {
        for (int i=0; i<length; i++)
            if (a[i] != b[i])
                return false;
        return true;
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/PeerTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.util.Arrays;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.Peer;
import com.jwetherell.bitcoin.data_model.Transaction;

public class PeerTest {

    private static final Transaction[]  EMPTY       = new Transaction[0];
    private static final byte[]         SIGNATURE   = "sig".getBytes();

    @Test
    public void testHello() {
        final byte[] key = "key".getBytes();
        final byte[] p1 = Peer.getIamMsg(key);
        final byte[] k = Peer.parseIamMsg(p1);

        Assert.assertTrue(Arrays.equals(key, k));
    }

    @Test
    public void testWhois() {
        final String hello = "hello";
        final byte[] p1 = Peer.getWhoisMsg(hello);
        final String result = Peer.parseWhoisMsg(p1);

        Assert.assertTrue(hello.equals(result));
    }

    @Test
    public void testTransaction() {
        final Transaction c1 = new Transaction("me","you","I give you 1 coin", 1, SIGNATURE, EMPTY, EMPTY);
        byte[] b = Peer.getTransactionMsg(c1);
        final Transaction c2 = Peer.parseTransactionMsg(b);

        Assert.assertTrue(c1.equals(c2));
    }

    @Test
    public void testTransactionAck() {
        final Transaction c1 = new Transaction("me","you","I give you 2 coins", 2, SIGNATURE, EMPTY, EMPTY);
        byte[] b = Peer.getTransactionAckMsg(c1);
        final Transaction c2 = Peer.parseTransactionAckMsg(b);

        Assert.assertTrue(c1.equals(c2));
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/ProofOfWorkTest.java
================================================
package com.jwetherell.bitcoin.test;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.Peer;
import com.jwetherell.bitcoin.ProofOfWork;
import com.jwetherell.bitcoin.common.HashUtils;

public class ProofOfWorkTest {

    private Peer.MiningTask task = new Peer.MiningTask();

    @Test
    public void test() {
        final int numberOfZerosInPrefix = 32;
        final byte[] sha256 = HashUtils.calculateSha256("Hello world!");
        task.run = true;
        final int nonce = ProofOfWork.solve(task, sha256, numberOfZerosInPrefix);

        Assert.assertTrue(ProofOfWork.check(sha256, nonce, numberOfZerosInPrefix));
    }

    @Test
    public void test2() {
        final int numberOfZerosInPrefix = 32;
        final byte[] sha256 = HashUtils.calculateSha256("Hello, I am a very nice hash. I work well with others and whatnot.");
        task.run = true;
        final int nonce = ProofOfWork.solve(task, sha256, numberOfZerosInPrefix);

        Assert.assertTrue(ProofOfWork.check(sha256, nonce, numberOfZerosInPrefix));
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/TCPTest.java
================================================
package com.jwetherell.bitcoin.test;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.networking.TCP;

public class TCPTest {

    private static final boolean DEBUG = Boolean.getBoolean("debug");

    @Test(timeout=5000)
    public void test() throws InterruptedException {
        final String from = "me";
        final String to = "you";
        final byte[] sig = "sig".getBytes();
        final byte[] toSend = "Hello world.".getBytes();
        final MessageListener listener = new MessageListener() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void onMessage(Receiver recv) {
                Data d = recv.getQueue().poll();
                while (d != null) {
                    final byte[] data = d.message.array();
                    if (DEBUG)
                        System.out.println("Listener received '"+new String(data)+"'");
                    Assert.assertTrue(isEquals(toSend,data,toSend.length));
                    d = recv.getQueue().poll();
                }
                TCP.Peer.RunnableRecv.run = false;
                TCP.Peer.RunnableSend.run = false;
            }
        };

        // Start both the sender and receiver
        final TCP.Peer.RunnableRecv recv = new TCP.Peer.RunnableRecv(listener);
        final Thread r = new Thread(recv,"recv");
        r.start();     

        final TCP.Peer.RunnableSend send = new TCP.Peer.RunnableSend();
        final Thread s = new Thread(send,"send");
        s.start();

        // Wait for everyone to initialize
        while (recv.isReady()==false || send.isReady()==false) {
            Thread.yield();
        }

        final Data data = new Data(from, recv.getHost(), recv.getPort(), to, recv.getHost(), recv.getPort(), sig, toSend);
        send.getQueue().add(data);

        // Wait for threads to finish
        r.join();
        s.join();
    }

    private static final boolean isEquals(byte[] a, byte[] b, int length) {
        for (int i=0; i<length; i++)
            if (a[i] != b[i])
                return false;
        return true;
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/TransactionTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.nio.ByteBuffer;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.data_model.Transaction;

public class TransactionTest {

    private static final Transaction[]  EMPTY       = new Transaction[0];
    private static final byte[]         SIGNATURE   = "sig".getBytes();

    @Test
    public void testSerialization() {
        final String f = "me";
        final String t = "you";

        final Transaction[] inputs = new Transaction[2];
        inputs[0] = new Transaction(f, t, "Here is a coin for you!", 1, SIGNATURE, EMPTY, EMPTY);
        inputs[1] = new Transaction(f, t, "Here is 2 coins for you!", 2, SIGNATURE, EMPTY, EMPTY);

        final Transaction[] outputs = new Transaction[1];
        outputs[0] = new Transaction(t, f, "Here is three coins for you!", 3, SIGNATURE, EMPTY, EMPTY);

        final Transaction trans = new Transaction(f, t, "Here is a transaction", 0, SIGNATURE, inputs, outputs);
        final ByteBuffer buffer = ByteBuffer.allocate(trans.getBufferLength());
        trans.toBuffer(buffer);
        buffer.flip();

        final Transaction trans2 = new Transaction();
        trans2.fromBuffer(buffer);

        Assert.assertTrue(trans.equals(trans2));
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/UDPTest.java
================================================
package com.jwetherell.bitcoin.test;

import org.junit.Assert;
import org.junit.Test;

import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.interfaces.MessageListener;
import com.jwetherell.bitcoin.interfaces.Receiver;
import com.jwetherell.bitcoin.networking.UDP;

public class UDPTest {

    private static final boolean DEBUG = Boolean.getBoolean("debug");

    @Test(timeout=5000)
    public void test() throws InterruptedException {
        final String from = "me";
        final String to = "you";
        final byte[] sig = "sig".getBytes();
        final byte[] toSend = "Hello world.".getBytes();
        final MessageListener listener = new MessageListener() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void onMessage(Receiver recv) {
                Data d = recv.getQueue().poll();
                while (d != null) {
                    final byte[] data = d.message.array();
                    if (DEBUG)
                        System.out.println("Listener received '"+new String(data)+"'");
                    Assert.assertTrue(isEquals(toSend,data,toSend.length));
                    d = recv.getQueue().poll();
                }
                UDP.Peer.RunnableRecv.run = false;
                UDP.Peer.RunnableSend.run = false;
            }
        };

        // Start both the sender and receiver
        final UDP.Peer.RunnableRecv recv = new UDP.Peer.RunnableRecv(listener);
        final Thread r = new Thread(recv,"recv");
        r.start();     

        final UDP.Peer.RunnableSend send = new UDP.Peer.RunnableSend();
        final Thread s = new Thread(send,"send");
        s.start();

        // Wait for everyone to initialize
        while (recv.isReady()==false || send.isReady()==false) {
            Thread.yield();
        }

        final Data data = new Data(from ,recv.getHost(), recv.getPort(), to, recv.getHost(), recv.getPort(), sig, toSend);
        send.getQueue().add(data);

        // Wait for threads to finish
        r.join();
        s.join();
    }

    private static final boolean isEquals(byte[] a, byte[] b, int length) {
        for (int i=0; i<length; i++)
            if (a[i] != b[i])
                return false;
        return true;
    }
}


================================================
FILE: src/com/jwetherell/bitcoin/test/WalletTest.java
================================================
package com.jwetherell.bitcoin.test;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.jwetherell.bitcoin.Blockchain;
import com.jwetherell.bitcoin.Wallet;
import com.jwetherell.bitcoin.data_model.Block;
import com.jwetherell.bitcoin.data_model.Data;
import com.jwetherell.bitcoin.data_model.Transaction;

public class WalletTest {

    // Create genesis entity
    private Wallet              genesis;

    @Before
    public void startGenesis() {
        genesis = new Wallet(Blockchain.GENESIS_NAME);
    }

    @After
    public void stopGenersis() throws InterruptedException {
        genesis.shutdown();
        genesis = null;
    }

    private static final void distributeGenesisCoins(Wallet genesis, Wallet... wallets) throws InterruptedException {
        long balance = genesis.getBalance();
        final int each = (int) (balance/wallets.length);

        for (Wallet w : wallets) {
            balance -= each;
            // Distribute genesis coins
            genesis.sendCoin(w.getName(), each);
            while (genesis.getBalance()!=balance || w.getBalance()!=each)
                Thread.yield();
        }
    }

    @Test(timeout=10000)
    public void testBadSignature() throws InterruptedException {
        final String n1 = "n1";
        final String n2 = "n2";
        final String n3 = "n3";
        final BadKeyWallet p1 = new BadKeyWallet(n1);
        final Wallet p2 = new Wallet(n2);
        final Wallet p3 = new Wallet(n3);

        // Distribute genesis coins evenly
        distributeGenesisCoins(genesis,p1,p2,p3);
        // genesis=2, p1=16, p2=16, p3=16

        // Switch to use the 'bad' key
        p1.switchKeys();

        // Send coin (which'll be rejected for a bad signature)
        p1.sendCoin(n2,10);
        // p1=16, p2=16, p3=16

        while (p1.getBalance()!=16 || p2.getBalance()!=16 || p3.getBalance()!=16) {
            Thread.yield();
        }

        p2.sendCoin(n3,2);
        // p1=16, p2=14, p3=18

        while (p1.getBalance()!=16 || p2.getBalance()!=14 || p3.getBalance()!=18) {
            Thread.yield();
        }

        Assert.assertTrue(p1.getBalance()==16);
        Assert.assertTrue(p2.getBalance()==14);
        Assert.assertTrue(p3.getBalance()==18);

        Thread.yield();

        p1.shutdown();
        p2.shutdown();
        p3.shutdown();
    }

    @Test(timeout=10000)
    public void testCoinExchangers2() throws InterruptedException {
        final String n1 = "n1";
        final String n2 = "n2";
        final Wallet p1 = new Wallet(n1);
        final Wallet p2 = new Wallet(n2);

        // Distribute genesis coins evenly
        distributeGenesisCoins(genesis,p1,p2);
        // genesis=0, p1=25, p2=25

        p1.sendCoin(n2, 3);
        // genesis=0, p1=22, p2=28

        while (p1.getBalance()!=22 || p2.getBalance()!=28) {
            Thread.yield();
        }

        p2.sendCoin(n1, 7);
        // genesis=0, p1=29, p2=21

        while (genesis.getBalance()!=0 || p1.getBalance()!=29 || p2.getBalance()!=21) {
            Thread.yield();
        }

        Assert.assertTrue(genesis.getBalance()==0);
        Assert.assertTrue(p1.getBalance()==29);
        Assert.assertTrue(p2.getBalance()==21);

        Thread.yield();

        p1.shutdown();
        p2.shutdown();
    }

    @Test(timeout=10000)
    public void testCoinExchangers3() throws InterruptedException {
        final String n1 = "n1";
        final String n2 = "n2";
        final String n3 = "n3";
        final Wallet p1 = new Wallet(n1);
        final Wallet p2 = new Wallet(n2);
        final Wallet p3 = new Wallet(n3);

        // Distribute genesis coins evenly
        distributeGenesisCoins(genesis,p1,p2,p3);
        // genesis=2, p1=16, p2=16, p3=16

        p1.sendCoin(n2, 3);
        // p1=13, p2=19, p3=16

        while (p1.getBalance()!=13 || p2.getBalance()!=19 || p3.getBalance()!=16) {
            Thread.yield();
        }

        p2.sendCoin(n3, 7);
        // p1=13, p2=12, p3=23

        while (p1.getBalance()!=13 || p2.getBalance()!=12 || p3.getBalance()!=23) {
            Thread.yield();
        }

        p3.sendCoin(n1, 11);
        // p1=24, p2=12, p3=12

        while (genesis.getBalance()!=2 || p1.getBalance()!=24 || p2.getBalance()!=12 || p3.getBalance()!=12) {
            Thread.yield();
        }

        Assert.assertTrue(genesis.getBalance()==2);
        Assert.assertTrue(p1.getBalance()==24);
        Assert.assertTrue(p2.getBalance()==12);
        Assert.assertTrue(p3.getBalance()==12);

        Thread.yield();

        p1.shutdown();
        p2.shutdown();
        p3.shutdown();
    }

    @Test(timeout=10000)
    public void testBadHash() throws InterruptedException {
        final Transaction[] EMPTY = new Transaction[0];
        final String n1 = "n1";
        final String n2 = "n2";
        final BadHashWallet p1 = new BadHashWallet(n1);
        final BadHashWallet p2 = new BadHashWallet(n2);

        // Distribute genesis coins evenly
        distributeGenesisCoins(genesis,p1,p2);
        // genesis=0, p1=25, p2=25

        // Send coin
        p1.sendCoin(n2,10);
        // p1=15, p2=35

        while (p1.getBalance()!=15 || p2.getBalance()!=35) {
            Thread.yield();
        }

        // This block has a bad hash
        final Transaction transaction = Transaction.newSignedTransaction(p1.getSignature(), n1, n2, "Please reject me!", 1, EMPTY, EMPTY);
        final byte[] prev = "This is a bad hash".getBytes();
        final byte[] hash = "This is a VERY bad hash".getBytes();
        final Transaction[] trans = new Transaction[]{ transaction };
        final Block block = new Block(n1, prev, hash, trans, 0);
        // Dummy data object, only care about the destination host and port
        final Data data = new Data(p1.getName(), p1.getHost(), p1.getPort(), p2.getName(), p2.getHost(), p2.getPort(), "".getBytes(), "".getBytes());
        p1.sendBlock(block, data);
        // p1=15, p2=35 (nothing changes)

        while (p1.getBalance()!=15 || p2.getBalance()!=35) {
            Thread.yield();
        }

        // This should be accepted
        p1.sendCoin(n2,10);
        // p1=5, p2=45

        while (genesis.getBalance()!=0 || p1.getBalance()!=5 || p2.getBalance()!=45) {
            Thread.yield();
        }

        Assert.assertTrue(genesis.getBalance()==0);
        Assert.assertTrue(p1.getBalance()==5);
        Assert.assertTrue(p2.getBalance()==45);

        Thread.yield();

        p1.shutdown();
        p2.shutdown();
    }

    private static class BadHashWallet extends Wallet {

        public BadHashWallet(String name) {
            super(name);
        }

        public Signature getSignature() {
            return enc;
        }

        public String getHost() {
            return runnableRecvTcp.getHost();
        }

        public int getPort() {
            return runnableRecvTcp.getPort();
        }

        /** Really only here to open up the method for JUnits **/
        public void sendBlock(Block block, Data data) {
            super.sendBlock(block, data);
        }
    }

    private static class BadKeyWallet extends Wallet {

        private final KeyPairGenerator              gen;
        private final SecureRandom                  random;
        private final Signature                     enc;
        private final KeyPair                       pair;
        private final PrivateKey                    privateKey;
        {
            try {
                gen = KeyPairGenerator.getInstance("DSA", "SUN");
                random = SecureRandom.getInstance("SHA1PRNG", "SUN");
                gen.initialize(512, random);

                enc = Signature.getInstance("SHA1withDSA", "SUN");

                pair = gen.generateKeyPair();
                privateKey = pair.getPrivate();
                enc.initSign(privateKey);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        private boolean                             switchToBadKey = false;;

        public BadKeyWallet(String name) {
            super(name);
        }

        public void switchKeys() {
            switchToBadKey = !switchToBadKey;
        }

        @Override
        protected byte[] signMsg(byte[] bytes) {
            if (!switchToBadKey)
                return super.signMsg(bytes);

            byte[] signed = null;
            try {
                enc.update(bytes);
                signed = enc.sign();
            } catch (Exception e) {
                System.err.println("Could not encode msg. "+e);
            }
            return signed;
        }
    }
}
Download .txt
gitextract_1ggmfz6a/

├── .classpath
├── .gitignore
├── .project
├── LICENSE
├── README.md
└── src/
    └── com/
        └── jwetherell/
            └── bitcoin/
                ├── Blockchain.java
                ├── Peer.java
                ├── ProofOfWork.java
                ├── Wallet.java
                ├── common/
                │   ├── Constants.java
                │   ├── HashUtils.java
                │   └── KeyUtils.java
                ├── data_model/
                │   ├── Block.java
                │   ├── Data.java
                │   └── Transaction.java
                ├── interfaces/
                │   ├── MessageListener.java
                │   ├── Receiver.java
                │   └── Sender.java
                ├── networking/
                │   ├── Multicast.java
                │   ├── TCP.java
                │   └── UDP.java
                └── test/
                    ├── AllTest.java
                    ├── BlockChainTest.java
                    ├── BlockTest.java
                    ├── DataTest.java
                    ├── EncodeDecodeTest.java
                    ├── MulticastTest.java
                    ├── PeerTest.java
                    ├── ProofOfWorkTest.java
                    ├── TCPTest.java
                    ├── TransactionTest.java
                    ├── UDPTest.java
                    └── WalletTest.java
Download .txt
SYMBOL INDEX (266 symbols across 28 files)

FILE: src/com/jwetherell/bitcoin/Blockchain.java
  class Blockchain (line 19) | public class Blockchain {
    method Blockchain (line 80) | public Blockchain(String owner) {
    method getLength (line 86) | public int getLength() {
    method getBlock (line 90) | public Block getBlock(int blockNumber) {
    method getUnused (line 96) | public List<Transaction> getUnused() {
    method getNextBlock (line 100) | public Block getNextBlock(String name, Transaction[] transactions) {
    method checkHash (line 118) | public Constants.Status checkHash(Block block) {
    method addBlock (line 166) | public Constants.Status addBlock(String dataFrom, Block block) {
    method getBalance (line 222) | public long getBalance(String name) {
    method getNextHash (line 239) | public static final byte[] getNextHash(byte[] hash, byte[] bytes) {
    method equals (line 250) | @Override
    method toString (line 269) | @Override

FILE: src/com/jwetherell/bitcoin/Peer.java
  class Peer (line 26) | public abstract class Peer {
    method onMessage (line 53) | @Override
    method Peer (line 119) | protected Peer(String name) {
    method getName (line 145) | public String getName() {
    method shutdown (line 149) | public void shutdown() throws InterruptedException {
    method isReady (line 173) | public boolean isReady() {
    method getBlockChain (line 177) | public abstract Blockchain getBlockChain();
    method getPublicKey (line 180) | protected abstract byte[] getPublicKey();
    method sendWhois (line 182) | private void sendWhois(String who) {
    method handleWhois (line 193) | private void handleWhois(byte[] bytes, Data data) {
    method sendIam (line 201) | private void sendIam() {
    method handleIam (line 212) | private void handleIam(byte[] bytes, Data data) {
    method newPublicKey (line 224) | protected abstract void newPublicKey(String name, byte[] publicKey);
    method signMsg (line 227) | protected abstract byte[] signMsg(byte[] bytes);
    method verifyMsg (line 230) | protected abstract boolean verifyMsg(byte[] publicKey, byte[] signatur...
    method sendTransaction (line 233) | protected void sendTransaction(String to, Transaction transaction) {
    method handleTransaction (line 253) | private Constants.Status handleTransaction(String dataFrom, byte[] byt...
    method handleTransaction (line 258) | private Constants.Status handleTransaction(String dataFrom, Transactio...
    method handleTransaction (line 280) | protected abstract Constants.Status handleTransaction(String dataFrom,...
    method ackTransaction (line 282) | private void ackTransaction(String to, Transaction transaction) {
    method handleTransactionAck (line 302) | private void handleTransactionAck(String dataFrom, byte[] bytes, Data ...
    method handleTransactionAck (line 307) | private void handleTransactionAck(String dataFrom, Transaction transac...
    method handleTransactionAck (line 324) | protected abstract Constants.Status handleTransactionAck(String dataFr...
    method aggegateTransaction (line 327) | protected abstract Block aggegateTransaction(Transaction transaction);
    method sendBlock (line 329) | protected void sendBlock(Block block, Data data) {
    method handleBlock (line 341) | private void handleBlock(String dataFrom, byte[] bytes, Data data) {
    method handleBlock (line 346) | private void handleBlock(String dataFrom, Block block, Data data) {
    method sendConfirmation (line 377) | protected void sendConfirmation(Block block) {
    method sendConfirmation (line 390) | protected void sendConfirmation(Block block, Data data) {
    method handleConfirmation (line 402) | private void handleConfirmation(String dataFrom, byte[] bytes, Data da...
    method handleConfirmation (line 407) | private void handleConfirmation(String dataFrom, Block block, Data dat...
    method checkTransaction (line 470) | protected abstract Constants.Status checkTransaction(String dataFrom, ...
    method handleConfirmation (line 473) | protected abstract Constants.Status handleConfirmation(String dataFrom...
    method mineHash (line 476) | protected abstract int mineHash(MiningTask timer, byte[] sha256, long ...
    method sendResend (line 478) | private void sendResend(int blockNumber) {
    method handleResend (line 490) | private void handleResend(String dataFrom, byte[] bytes, Data data) {
    method sendRehash (line 505) | protected void sendRehash(Block block, Data data) {
    method handleRehash (line 517) | private void handleRehash(String dataFrom, byte[] bytes, Data data) {
    method addTransactionToSend (line 524) | private void addTransactionToSend(Queued.State state, String to, Trans...
    method processTransactionsToSend (line 534) | private void processTransactionsToSend(String to) {
    method addTransactionToRecv (line 555) | private void addTransactionToRecv(Queued.State state, String dataFrom,...
    method addBlockToRecv (line 565) | private void addBlockToRecv(Queued.State state, String dataFrom, Block...
    method processTransactionsToRecv (line 575) | private void processTransactionsToRecv(String from) {
    method addFutureBlockToRecv (line 592) | private void addFutureBlockToRecv(Queued.State state, String dataFrom,...
    method processFutureBlocksToRecv (line 602) | private void processFutureBlocksToRecv(String from) {
    class Host (line 615) | private static final class Host {
      method Host (line 620) | private Host(String ip, int port) {
    class Queued (line 626) | private static final class Queued {
      type State (line 628) | private enum                State {NEW, ACK, CONFIRM, FUTURE}
      method Queued (line 635) | private Queued(State state, Transaction transaction, Data data) {
      method Queued (line 644) | private Queued(State state, Block block, Data data) {
    class MiningTask (line 654) | public static class MiningTask extends TimerTask {
      method MiningTask (line 663) | public MiningTask() {
      method MiningTask (line 669) | public MiningTask(Peer peer, String hex, Block block) {
      method run (line 679) | @Override
      method cancel (line 703) | @Override
    method getWhoisMsg (line 711) | public static final byte[] getWhoisMsg(String name) {
    method parseWhoisMsg (line 724) | public static final String parseWhoisMsg(byte[] bytes) {
    method getIamMsg (line 737) | public static final byte[] getIamMsg(byte[] publicKey) {
    method parseIamMsg (line 750) | public static final byte[] parseIamMsg(byte[] bytes) {
    method getTransactionMsg (line 763) | public static final byte[] getTransactionMsg(Transaction transaction) {
    method parseTransactionMsg (line 777) | public static final Transaction parseTransactionMsg(byte[] bytes) {
    method getTransactionAckMsg (line 788) | public static final byte[] getTransactionAckMsg(Transaction transactio...
    method parseTransactionAckMsg (line 802) | public static final Transaction parseTransactionAckMsg(byte[] bytes) {
    method getBlockMsg (line 813) | public static final byte[] getBlockMsg(Block block) {
    method parseBlockMsg (line 827) | public static final Block parseBlockMsg(byte[] bytes) {
    method getConfirmationMsg (line 838) | public static final byte[] getConfirmationMsg(Block block) {
    method parseConfirmationMsg (line 852) | public static final Block parseConfirmationMsg(byte[] bytes) {
    method getResendBlockMsg (line 863) | public static final byte[] getResendBlockMsg(int blockNumber) {
    method parseResendBlockMsg (line 874) | public static final int parseResendBlockMsg(byte[] bytes) {
    method getRehashMsg (line 884) | public static final byte[] getRehashMsg(Block block) {
    method parseRehashMsg (line 898) | public static final Block parseRehashMsg(byte[] bytes) {
    method toString (line 912) | @Override

FILE: src/com/jwetherell/bitcoin/ProofOfWork.java
  class ProofOfWork (line 8) | public class ProofOfWork {
    method getBit (line 10) | private static final byte getBit(int ID, int position) {
    method solve (line 25) | public static final int solve(MiningTask task, byte[] sha256, long num...
    method check (line 71) | public static final boolean check(byte[] sha256, int nonce, long numbe...

FILE: src/com/jwetherell/bitcoin/Wallet.java
  class Wallet (line 29) | public class Wallet extends Peer {
    method compare (line 70) | @Override
    method Wallet (line 88) | public Wallet(String name) {
    method getPublicKey (line 100) | @Override
    method getBlockChain (line 108) | @Override
    method getBalance (line 113) | public long getBalance() {
    method newPublicKey (line 120) | @Override
    method signMsg (line 130) | @Override
    method verifyMsg (line 140) | @Override
    method sendCoin (line 146) | public synchronized void sendCoin(String name, int value) {
    method checkSignature (line 194) | private Constants.Status checkSignature(String dataFrom, byte[] signat...
    method handleTransaction (line 208) | @Override
    method handleTransactionAck (line 223) | @Override
    method getNextBlock (line 236) | private Block getNextBlock(Transaction[] transactions) {
    method aggegateTransaction (line 250) | @Override
    method checkTransaction (line 268) | @Override
    method handleConfirmation (line 285) | @Override
    method mineHash (line 352) | @Override
    method toString (line 367) | @Override

FILE: src/com/jwetherell/bitcoin/common/Constants.java
  class Constants (line 3) | public abstract class Constants {
    type Status (line 5) | public static enum  Status {

FILE: src/com/jwetherell/bitcoin/common/HashUtils.java
  class HashUtils (line 6) | public abstract class HashUtils {
    method calculateSha256 (line 8) | public static final byte[] calculateSha256(String text) {
    method calculateSha256 (line 18) | public static final byte[] calculateSha256(byte[] utf8Bytes) {
    method bytesToHex (line 30) | public static final String bytesToHex(byte[] bytes) {

FILE: src/com/jwetherell/bitcoin/common/KeyUtils.java
  class KeyUtils (line 8) | public abstract class KeyUtils {
    method signMsg (line 10) | public static final byte[] signMsg(Signature enc, byte[] bytes) {
    method verifyMsg (line 21) | public static final boolean verifyMsg(KeyFactory keyFactory, Signature...

FILE: src/com/jwetherell/bitcoin/data_model/Block.java
  class Block (line 8) | public class Block {
    method Block (line 26) | public Block() { }
    method Block (line 28) | public Block(String from, byte[] prevHash, byte[] hash, Transaction[] ...
    method getBufferLength (line 36) | public int getBufferLength() {
    method toBuffer (line 50) | public void toBuffer(ByteBuffer buffer) {
    method fromBuffer (line 73) | public void fromBuffer(ByteBuffer buffer) {
    method getBoolean (line 109) | private static final char getBoolean(boolean bool) {
    method parseBoolean (line 113) | private static final boolean parseBoolean(char bool) {
    method hashCode (line 120) | @Override
    method equals (line 142) | @Override
    method toString (line 175) | @Override

FILE: src/com/jwetherell/bitcoin/data_model/Data.java
  class Data (line 8) | public class Data {
    method Data (line 23) | public Data() { }
    method Data (line 25) | public Data(String from, String sourceAddr, int sourcePort,
    method getBufferLength (line 48) | public int getBufferLength() {
    method toBuffer (line 59) | public void toBuffer(ByteBuffer buffer) {
    method fromBuffer (line 101) | public void fromBuffer(ByteBuffer buffer) {
    method equals (line 168) | @Override
    method toString (line 195) | @Override

FILE: src/com/jwetherell/bitcoin/data_model/Transaction.java
  class Transaction (line 10) | public class Transaction {
    method Transaction (line 29) | public Transaction() { }
    method Transaction (line 31) | public Transaction(String from, String to, String header, int value, b...
    method newSignedTransaction (line 51) | public static final Transaction newSignedTransaction(Signature signature,
    method updateTimestamp (line 61) | public void updateTimestamp() {
    method getBufferLength (line 65) | public int getBufferLength() {
    method toBuffer (line 85) | public void toBuffer(ByteBuffer buffer) {
    method fromBuffer (line 127) | public void fromBuffer(ByteBuffer buffer) {
    method hashCode (line 185) | @Override
    method equals (line 205) | @Override
    method toString (line 246) | @Override

FILE: src/com/jwetherell/bitcoin/interfaces/MessageListener.java
  type MessageListener (line 3) | public interface MessageListener {
    method onMessage (line 5) | public void onMessage(Receiver recv);

FILE: src/com/jwetherell/bitcoin/interfaces/Receiver.java
  type Receiver (line 7) | public interface Receiver {
    method getQueue (line 9) | public Queue<Data> getQueue();
    method isReady (line 11) | public boolean isReady();
    method getHost (line 13) | public String getHost();
    method getPort (line 15) | public int getPort();

FILE: src/com/jwetherell/bitcoin/interfaces/Sender.java
  type Sender (line 7) | public interface Sender {
    method getQueue (line 9) | public Queue<Data> getQueue();
    method isReady (line 11) | public boolean isReady();

FILE: src/com/jwetherell/bitcoin/networking/Multicast.java
  class Multicast (line 19) | public class Multicast {
    method createReceiver (line 26) | public static MulticastSocket createReceiver() throws IOException {
    method destoryReceiver (line 35) | public static void destoryReceiver(MulticastSocket s) throws UnknownHo...
    method recvData (line 47) | public static boolean recvData(MulticastSocket s, byte[] buffer) throw...
    method createSender (line 60) | public static MulticastSocket createSender() throws IOException {
    method destroySender (line 68) | public static void destroySender(MulticastSocket s) throws IOException {
    method sendData (line 76) | public static void sendData(MulticastSocket s, int ourTTL, byte[] buff...
    class Peer (line 86) | public static final class Peer {
      class RunnableRecv (line 90) | public static final class RunnableRecv implements Runnable, Receiver {
        method RunnableRecv (line 99) | public RunnableRecv(MessageListener listener) {
        method getQueue (line 107) | @Override
        method isReady (line 115) | @Override
        method getHost (line 123) | @Override
        method getPort (line 131) | @Override
        method run (line 139) | @Override
      class RunnableSend (line 184) | public static final class RunnableSend implements Runnable, Sender {
        method RunnableSend (line 194) | public RunnableSend() {
        method getQueue (line 201) | @Override
        method isReady (line 209) | @Override
        method run (line 217) | @Override

FILE: src/com/jwetherell/bitcoin/networking/TCP.java
  class TCP (line 20) | public class TCP {
    method createServer (line 28) | public static ServerSocket createServer(int port) throws IOException {
    method destoryServer (line 33) | public static void destoryServer(ServerSocket s) throws IOException {
    method createClient (line 38) | public static Socket createClient(String host, int port) throws IOExce...
    method destoryClient (line 43) | public static void destoryClient(Socket s) throws IOException {
    method sendData (line 48) | public static void sendData(Socket socket, byte[] buffer) throws IOExc...
    method recvData (line 61) | public static boolean recvData(ServerSocket serverSocket, byte[] buffe...
    class Peer (line 75) | public static final class Peer {
      method Peer (line 79) | private Peer() { }
      class RunnableRecv (line 81) | public static final class RunnableRecv implements Runnable, Receiver {
        method RunnableRecv (line 91) | public RunnableRecv(MessageListener listener) {
        method getQueue (line 100) | @Override
        method isReady (line 108) | @Override
        method getHost (line 116) | @Override
        method getPort (line 124) | @Override
        method run (line 132) | @Override
      class RunnableSend (line 175) | public static final class RunnableSend implements Runnable, Sender {
        method RunnableSend (line 183) | public RunnableSend() {
        method getQueue (line 190) | @Override
        method isReady (line 198) | @Override
        method run (line 206) | @Override

FILE: src/com/jwetherell/bitcoin/networking/UDP.java
  class UDP (line 18) | public class UDP {
    method createServer (line 26) | public static DatagramSocket createServer(int port) throws SocketExcep...
    method destoryServer (line 31) | public static void destoryServer(DatagramSocket s) {
    method createClient (line 36) | public static DatagramSocket createClient() throws SocketException {
    method destoryClient (line 41) | public static void destoryClient(DatagramSocket s) {
    method sendData (line 46) | public static void sendData(DatagramSocket socket, InetAddress IPAddre...
    method recvData (line 54) | public static boolean recvData(DatagramSocket socket, byte[] buffer) t...
    class Peer (line 65) | public static final class Peer {
      method Peer (line 69) | private Peer() { }
      class RunnableRecv (line 71) | public static final class RunnableRecv implements Runnable, Receiver {
        method RunnableRecv (line 81) | public RunnableRecv(MessageListener listener) {
        method getQueue (line 90) | @Override
        method isReady (line 98) | @Override
        method getHost (line 106) | @Override
        method getPort (line 114) | @Override
        method run (line 122) | @Override
      class RunnableSend (line 161) | public static final class RunnableSend implements Runnable, Sender {
        method RunnableSend (line 169) | public RunnableSend() {
        method getQueue (line 176) | @Override
        method isReady (line 184) | @Override
        method run (line 192) | @Override

FILE: src/com/jwetherell/bitcoin/test/AllTest.java
  class AllTest (line 8) | @RunWith(Suite.class)

FILE: src/com/jwetherell/bitcoin/test/BlockChainTest.java
  class BlockChainTest (line 12) | public class BlockChainTest {
    method test (line 17) | @Test

FILE: src/com/jwetherell/bitcoin/test/BlockTest.java
  class BlockTest (line 11) | public class BlockTest {
    method testSerialization (line 17) | @Test

FILE: src/com/jwetherell/bitcoin/test/DataTest.java
  class DataTest (line 10) | public class DataTest {
    method testSerialization (line 12) | @Test

FILE: src/com/jwetherell/bitcoin/test/EncodeDecodeTest.java
  class EncodeDecodeTest (line 15) | public class EncodeDecodeTest {
    method test1 (line 17) | @Test
    method test2 (line 57) | @Test

FILE: src/com/jwetherell/bitcoin/test/MulticastTest.java
  class MulticastTest (line 11) | public class MulticastTest {
    method test (line 15) | @Test(timeout=5000)
    method isEquals (line 62) | private static final boolean isEquals(byte[] a, byte[] b, int length) {

FILE: src/com/jwetherell/bitcoin/test/PeerTest.java
  class PeerTest (line 11) | public class PeerTest {
    method testHello (line 16) | @Test
    method testWhois (line 25) | @Test
    method testTransaction (line 34) | @Test
    method testTransactionAck (line 43) | @Test

FILE: src/com/jwetherell/bitcoin/test/ProofOfWorkTest.java
  class ProofOfWorkTest (line 10) | public class ProofOfWorkTest {
    method test (line 14) | @Test
    method test2 (line 24) | @Test

FILE: src/com/jwetherell/bitcoin/test/TCPTest.java
  class TCPTest (line 11) | public class TCPTest {
    method test (line 15) | @Test(timeout=5000)
    method isEquals (line 62) | private static final boolean isEquals(byte[] a, byte[] b, int length) {

FILE: src/com/jwetherell/bitcoin/test/TransactionTest.java
  class TransactionTest (line 10) | public class TransactionTest {
    method testSerialization (line 15) | @Test

FILE: src/com/jwetherell/bitcoin/test/UDPTest.java
  class UDPTest (line 11) | public class UDPTest {
    method test (line 15) | @Test(timeout=5000)
    method isEquals (line 62) | private static final boolean isEquals(byte[] a, byte[] b, int length) {

FILE: src/com/jwetherell/bitcoin/test/WalletTest.java
  class WalletTest (line 20) | public class WalletTest {
    method startGenesis (line 25) | @Before
    method stopGenersis (line 30) | @After
    method distributeGenesisCoins (line 36) | private static final void distributeGenesisCoins(Wallet genesis, Walle...
    method testBadSignature (line 49) | @Test(timeout=10000)
    method testCoinExchangers2 (line 91) | @Test(timeout=10000)
    method testCoinExchangers3 (line 126) | @Test(timeout=10000)
    method testBadHash (line 172) | @Test(timeout=10000)
    class BadHashWallet (line 225) | private static class BadHashWallet extends Wallet {
      method BadHashWallet (line 227) | public BadHashWallet(String name) {
      method getSignature (line 231) | public Signature getSignature() {
      method getHost (line 235) | public String getHost() {
      method getPort (line 239) | public int getPort() {
      method sendBlock (line 244) | public void sendBlock(Block block, Data data) {
    class BadKeyWallet (line 249) | private static class BadKeyWallet extends Wallet {
      method BadKeyWallet (line 274) | public BadKeyWallet(String name) {
      method switchKeys (line 278) | public void switchKeys() {
      method signMsg (line 282) | @Override
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (171K chars).
[
  {
    "path": ".classpath",
    "chars": 303,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"con\" "
  },
  {
    "path": ".gitignore",
    "chars": 6,
    "preview": "/bin/\n"
  },
  {
    "path": ".project",
    "chars": 366,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>Bitcoin</name>\n\t<comment></comment>\n\t<projects>\n\t</pr"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 9116,
    "preview": "# Bitcoin\nAn example Bitcoin implementation which can be used to learn about Bitcoin/Blockchain. This implementations is"
  },
  {
    "path": "src/com/jwetherell/bitcoin/Blockchain.java",
    "chars": 11605,
    "preview": "package com.jwetherell.bitcoin;\n\nimport java.nio.ByteBuffer;\nimport java.security.KeyPair;\nimport java.security.KeyPairG"
  },
  {
    "path": "src/com/jwetherell/bitcoin/Peer.java",
    "chars": 35542,
    "preview": "package com.jwetherell.bitcoin;\n\nimport java.nio.ByteBuffer;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.u"
  },
  {
    "path": "src/com/jwetherell/bitcoin/ProofOfWork.java",
    "chars": 3525,
    "preview": "package com.jwetherell.bitcoin;\n\nimport java.nio.ByteBuffer;\n\nimport com.jwetherell.bitcoin.Peer.MiningTask;\nimport com."
  },
  {
    "path": "src/com/jwetherell/bitcoin/Wallet.java",
    "chars": 15001,
    "preview": "package com.jwetherell.bitcoin;\n\nimport java.nio.ByteBuffer;\nimport java.security.KeyFactory;\nimport java.security.KeyPa"
  },
  {
    "path": "src/com/jwetherell/bitcoin/common/Constants.java",
    "chars": 618,
    "preview": "package com.jwetherell.bitcoin.common;\n\npublic abstract class Constants {\n\n    public static enum  Status {    \n        "
  },
  {
    "path": "src/com/jwetherell/bitcoin/common/HashUtils.java",
    "chars": 1121,
    "preview": "package com.jwetherell.bitcoin.common;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;"
  },
  {
    "path": "src/com/jwetherell/bitcoin/common/KeyUtils.java",
    "chars": 1043,
    "preview": "package com.jwetherell.bitcoin.common;\n\nimport java.security.KeyFactory;\nimport java.security.PublicKey;\nimport java.sec"
  },
  {
    "path": "src/com/jwetherell/bitcoin/data_model/Block.java",
    "chars": 6132,
    "preview": "package com.jwetherell.bitcoin.data_model;\n\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\n\nimport com.jwetherell."
  },
  {
    "path": "src/com/jwetherell/bitcoin/data_model/Data.java",
    "chars": 7000,
    "preview": "package com.jwetherell.bitcoin.data_model;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport ja"
  },
  {
    "path": "src/com/jwetherell/bitcoin/data_model/Transaction.java",
    "chars": 8725,
    "preview": "package com.jwetherell.bitcoin.data_model;\n\nimport java.nio.ByteBuffer;\nimport java.security.Signature;\nimport java.util"
  },
  {
    "path": "src/com/jwetherell/bitcoin/interfaces/MessageListener.java",
    "chars": 125,
    "preview": "package com.jwetherell.bitcoin.interfaces;\n\npublic interface MessageListener {\n\n    public void onMessage(Receiver recv)"
  },
  {
    "path": "src/com/jwetherell/bitcoin/interfaces/Receiver.java",
    "chars": 271,
    "preview": "package com.jwetherell.bitcoin.interfaces;\n\nimport java.util.Queue;\n\nimport com.jwetherell.bitcoin.data_model.Data;\n\npub"
  },
  {
    "path": "src/com/jwetherell/bitcoin/interfaces/Sender.java",
    "chars": 212,
    "preview": "package com.jwetherell.bitcoin.interfaces;\n\nimport java.util.Queue;\n\nimport com.jwetherell.bitcoin.data_model.Data;\n\npub"
  },
  {
    "path": "src/com/jwetherell/bitcoin/networking/Multicast.java",
    "chars": 8639,
    "preview": "package com.jwetherell.bitcoin.networking;\n\nimport java.io.IOException;\nimport java.net.DatagramPacket;\nimport java.net."
  },
  {
    "path": "src/com/jwetherell/bitcoin/networking/TCP.java",
    "chars": 7831,
    "preview": "package com.jwetherell.bitcoin.networking;\n\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io."
  },
  {
    "path": "src/com/jwetherell/bitcoin/networking/UDP.java",
    "chars": 7450,
    "preview": "package com.jwetherell.bitcoin.networking;\n\nimport java.io.IOException;\nimport java.net.DatagramPacket;\nimport java.net."
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/AllTest.java",
    "chars": 879,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit."
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/BlockChainTest.java",
    "chars": 1432,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\n\nimport org.junit.Assert;\nimp"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/BlockTest.java",
    "chars": 1158,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.nio.ByteBuffer;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimpo"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/DataTest.java",
    "chars": 880,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.nio.ByteBuffer;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimpo"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/EncodeDecodeTest.java",
    "chars": 3467,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.securit"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/MulticastTest.java",
    "chars": 2343,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport com.jwetherell.bitcoin.dat"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/PeerTest.java",
    "chars": 1486,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.util.Arrays;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport "
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/ProofOfWorkTest.java",
    "chars": 1068,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport com.jwetherell.bitcoin.Pee"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/TCPTest.java",
    "chars": 2295,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport com.jwetherell.bitcoin.dat"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/TransactionTest.java",
    "chars": 1276,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.nio.ByteBuffer;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimpo"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/UDPTest.java",
    "chars": 2295,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport com.jwetherell.bitcoin.dat"
  },
  {
    "path": "src/com/jwetherell/bitcoin/test/WalletTest.java",
    "chars": 8872,
    "preview": "package com.jwetherell.bitcoin.test;\n\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.s"
  }
]

About this extraction

This page contains the full source code of the phishman3579/Bitcoin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (159.6 KB), approximately 34.3k tokens, and a symbol index with 266 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!