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;
}
}
}
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
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.