Initial checkin of BitCoinJ

This commit is contained in:
Mike Hearn
2011-03-07 10:17:10 +00:00
parent d1036b101f
commit fbb93e4c27
858 changed files with 122170 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class AddressTest {
static final NetworkParameters testParams = NetworkParameters.testNet();
static final NetworkParameters prodParams = NetworkParameters.prodNet();
@Test public void testStringification() throws Exception {
// Test a testnet address.
Address a = new Address(testParams, Hex.decode("fda79a24e50ff70ff42f7d89585da5bd19d9e5cc"));
assertEquals("n4eA2nbYqErp7H6jebchxAN59DmNpksexv", a.toString());
Address b = new Address(prodParams, Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));
assertEquals("17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL", b.toString());
}
@Test public void testDecoding() throws Exception {
Address a = new Address(testParams, "n4eA2nbYqErp7H6jebchxAN59DmNpksexv");
assertEquals("fda79a24e50ff70ff42f7d89585da5bd19d9e5cc", Utils.bytesToHexString(a.getHash160()));
Address b = new Address(prodParams, "17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL");
assertEquals("4a22c3c4cbb31e4d03b15550636762bda0baf85a", Utils.bytesToHexString(b.getHash160()));
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import junit.framework.TestCase;
import java.math.BigInteger;
import java.util.Arrays;
public class Base58Test extends TestCase {
public void testEncode() {
byte[] testbytes = "Hello World".getBytes();
assertEquals("JxF12TrwUP45BMd", Base58.encode(testbytes));
BigInteger bi = BigInteger.valueOf(3471844090L);
assertEquals("16Ho7Hs", Base58.encode(bi.toByteArray()));
}
public void testDecode() {
byte[] testbytes = "Hello World".getBytes();
byte[] actualbytes = Base58.decode("JxF12TrwUP45BMd");
assertTrue(new String(actualbytes), Arrays.equals(testbytes, actualbytes));
}
}

View File

@@ -0,0 +1,131 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import org.junit.Before;
import org.junit.Test;
import java.math.BigInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
// Tests still to write:
// - Rest of checkDifficultyTransitions: verify we don't accept invalid transitions.
// - Fragmented chains can be joined together.
// - Longest chain is selected based on total difficulty not length.
// - Many more ...
public class BlockChainTest {
private static final NetworkParameters params = NetworkParameters.testNet();
private BlockChain chain;
@Before
public void setUp() {
Wallet wallet = new Wallet(params);
chain = new BlockChain(params, wallet);
}
@Test
public void testBasicChaining() throws Exception {
// Check that we can plug a few blocks together.
// Block 1 from the testnet.
Block b1 = getBlock1();
assertTrue(chain.add(b1));
// Block 2 from the testnet.
Block b2 = getBlock2();
// Let's try adding an invalid block.
long n = b2.nonce;
try {
// TODO: Encapsulate these fields behind setters that recalc the hash automatically.
b2.nonce = 12345;
b2.hash = null;
chain.add(b2);
// We should not get here.
assertTrue(false);
} catch (VerificationException e) {
// Pass.
b2.nonce = n;
b2.hash = null;
}
assertTrue(chain.add(b2));
}
@Test
public void testBadDifficulty() throws Exception {
assertTrue(chain.add(getBlock1()));
Block b2 = getBlock2();
assertTrue(chain.add(b2));
NetworkParameters params2 = NetworkParameters.testNet();
Block bad = new Block(params2);
// Merkle root can be anything here, doesn't matter.
bad.merkleRoot = Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
// Nonce was just some number that made the hash < difficulty limit set below, it can be anything.
bad.nonce = 140548933;
bad.time = 1279242649;
bad.prevBlockHash = b2.getHash();
// We're going to make this block so easy 50% of solutions will pass, and check it gets rejected for having a
// bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all
// solutions.
bad.difficultyTarget = 0x207fFFFFL;
try {
chain.add(bad);
// The difficulty target above should be rejected on the grounds of being easier than the networks
// allowable difficulty.
assertTrue(false);
} catch (VerificationException e) {
assertTrue(e.getMessage(), e.getMessage().indexOf("Difficulty target is bad") >= 0);
}
// Accept any level of difficulty now.
params2.proofOfWorkLimit = new BigInteger
("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
try {
chain.add(bad);
// We should not get here as the difficulty target should not be changing at this point.
assertTrue(false);
} catch (VerificationException e) {
assertTrue(e.getMessage(), e.getMessage().indexOf("Unexpected change in difficulty") >= 0);
}
// TODO: Test difficulty change is not out of range when a transition period becomes valid.
}
private Block getBlock2() throws Exception {
Block b2 = new Block(params);
b2.merkleRoot = Hex.decode("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b");
b2.nonce = 2642058077L;
b2.time = 1296734343;
b2.prevBlockHash =
Hex.decode("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604");
assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString());
b2.verify();
return b2;
}
private Block getBlock1() throws Exception {
Block b1 = new Block(params);
b1.merkleRoot = Hex.decode("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10");
b1.nonce = 236038445;
b1.time = 1296734340;
b1.prevBlockHash = Hex.decode("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString());
b1.verify();
return b1;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,56 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import java.math.BigInteger;
import static com.google.bitcoin.core.Utils.*;
import static org.junit.Assert.assertTrue;
public class ECKeyTest {
@Test
public void testSignatures() {
// Test that we can construct an ECKey from a private key (deriving the public from the private), then signing
// a message with it.
BigInteger privkey = new BigInteger(1, Hex.decode("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"));
ECKey key = new ECKey(privkey);
byte[] message = new byte[32]; // All zeroes.
byte[] output = key.sign(message);
assertTrue(key.verify(message, output));
// Test interop with a signature from elsewhere.
byte[] sig = Hex.decode(
"3046022100dffbc26774fc841bbe1c1362fd643609c6e42dcb274763476d87af2c0597e89e022100c59e3c13b96b316cae9fa0ab0260612c7a133a6fe2b3445b6bf80b3123bf274d");
assertTrue(key.verify(message, sig));
}
@Test
public void testASNDecode() {
byte[] privkeyASN1 = Hex.decode("3082011302010104205c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4aa081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200042af7a2aafe8dafd7dc7f9cfb58ce09bda7dce28653ab229b98d1d3d759660c672dd0db18c8c2d76aa470448e876fc2089ab1354c01a6e72cefc50915f4a963ee");
ECKey key = ECKey.fromASN1(privkeyASN1);
byte[] message = reverseBytes(Hex.decode("11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
byte[] output = key.sign(message);
assertTrue(key.verify(message, output));
output = Hex.decode
("304502206faa2ebc614bf4a0b31f0ce4ed9012eb193302ec2bcaccc7ae8bb40577f47549022100c73a1a1acc209f3f860bf9b9f5e13e9433db6f8b7bd527a088a0e0cd0a4c83e9");
assertTrue(key.verify(message, output));
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ScriptTest {
// From tx 05e04c26c12fe408a3c1b71aa7996403f6acad1045252b1c62e055496f4d2cb1 on the testnet.
static final String sigProg = "473 04402202b4da291cc39faf8433911988f9f49fc5c995812ca2f94db61468839c228c3e90220628bff3ff32ec95825092fa051cba28558a981fcf59ce184b14f2e215e69106701410414b38f4be3bb9fa0f4f32b74af07152b2f2f630bc02122a491137b6c523e46f18a0d5034418966f93dfc37cc3739ef7b2007213a302b7fba161557f4ad644a1c";
static final String pubkeyProg = "76a91433e81a941e64cda12c6a299ed322ddbdd03f8d0e88ac";
static final NetworkParameters params = NetworkParameters.testNet();
@Test
public void testScriptSig() throws Exception {
byte[] sigProgBytes = Hex.decode(sigProg);
Script script = new Script(params, sigProgBytes, 0, sigProgBytes.length);
script.setTracing(true);
// Test we can extract the from address.
byte[] hash160 = Utils.sha256hash160(script.getPubKey());
Address a = new Address(params, hash160);
assertEquals("mkFQohBpy2HDXrCwyMrYL5RtfrmeiuuPY2", a.toString());
}
@Test
public void testScriptPubKey() throws Exception {
// Check we can extract the to address
byte[] pubkeyBytes = Hex.decode(pubkeyProg);
Script pubkey = new Script(params, pubkeyBytes, 0, pubkeyBytes.length);
Address toAddr = new Address(params, pubkey.getPubKeyHash());
assertEquals("mkFQohBpy2HDXrCwyMrYL5RtfrmeiuuPY2", toAddr.toString());
// To verify a transaction as legitimate, both scripts are concatenated and then the whole
// thing is run. So check we can do that.
byte[] sigBytes = Hex.decode(sigProg);
Script sig = new Script(params, sigBytes, 0, sigBytes.length);
Script allScript = Script.join(sig, pubkey);
allScript.setTracing(true);
assertTrue(allScript.run(null));
allScript.logStack();
}
@Test
public void testIp() throws Exception {
byte[] bytes = Hex.decode("41043e96222332ea7848323c08116dddafbfa917b8e37f0bdf63841628267148588a09a43540942d58d49717ad3fabfe14978cf4f0a8b84d2435dad16e9aa4d7f935ac");
Script s = new Script(params, bytes, 0, bytes.length);
s.setTracing(true);
assertTrue(s.isSentToIP());
}
}

View File

@@ -0,0 +1,43 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import junit.framework.TestCase;
public class VarIntTest extends TestCase {
public void testVarInts() throws Exception {
VarInt a;
// Bytes
a = new VarInt(10);
assertEquals(1, a.getSizeInBytes());
assertEquals(1, a.encode().length);
assertEquals(10, new VarInt(a.encode(), 0).value);
// Shorts
a = new VarInt(64000);
assertEquals(3, a.getSizeInBytes());
assertEquals(3, a.encode().length);
assertEquals(64000, new VarInt(a.encode(), 0).value);
a = new VarInt(0xAABBCCDDL);
assertEquals(5, a.getSizeInBytes());
assertEquals(5, a.encode().length);
byte[] bytes = a.encode();
assertEquals(0xAABBCCDDL, 0xFFFFFFFFL & new VarInt(bytes, 0).value);
}
}

View File

@@ -0,0 +1,141 @@
/**
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.bitcoin.core;
import org.junit.Before;
import org.junit.Test;
import java.math.BigInteger;
import static com.google.bitcoin.core.Utils.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class WalletTest {
static final NetworkParameters params = NetworkParameters.testNet();
private Address myAddress;
private Wallet wallet;
@Before
public void setUp() {
ECKey myKey = new ECKey();
myAddress = myKey.toAddress(params);
wallet = new Wallet(params);
wallet.addKey(myKey);
}
private Transaction createFakeTx(BigInteger nanocoins, Address to) {
Transaction t = new Transaction(params);
TransactionOutput o1 = new TransactionOutput(params, nanocoins, to);
t.addOutput(o1);
// t1 is not a valid transaction - it has no inputs. Nonetheless, if we set it up with a fake hash it'll be
// valid enough for these tests.
byte[] hash = new byte[32];
for (byte i = 0; i < 32; i++) hash[i] = i;
t.setFakeHashForTesting(hash);
return t;
}
@Test
public void testBasicSpending() throws Exception {
// We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
BigInteger v1 = Utils.toNanoCoins(1, 0);
Transaction t1 = createFakeTx(v1, myAddress);
wallet.receive(t1);
assertEquals(v1, wallet.getBalance());
ECKey k2 = new ECKey();
BigInteger v2 = toNanoCoins(0, 50);
Transaction t2 = wallet.createSend(k2.toAddress(params), v2);
// Do some basic sanity checks.
assertEquals(1, t2.inputs.size());
LOG(t2.inputs.get(0).getScriptSig().toString());
assertEquals(myAddress, t2.inputs.get(0).getScriptSig().getFromAddress());
// We have NOT proven that the signature is correct!
}
@Test
public void testListener() throws Exception {
final Transaction fakeTx = createFakeTx(Utils.toNanoCoins(1, 0), myAddress);
final boolean[] didRun = new boolean[1];
WalletEventListener listener = new WalletEventListener() {
public void onCoinsReceived(Wallet w, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
assertTrue(prevBalance.equals(BigInteger.ZERO));
assertTrue(newBalance.equals(Utils.toNanoCoins(1, 0)));
assertEquals(tx, fakeTx); // Same object.
assertEquals(w, wallet); // Same object.
didRun[0] = true;
}
};
wallet.addEventListener(listener);
wallet.receive(fakeTx);
assertTrue(didRun[0]);
}
@Test
public void testBalance() throws Exception {
// Receive 5 coins then half a coin.
BigInteger v1 = toNanoCoins(5, 0);
BigInteger v2 = toNanoCoins(0, 50);
Transaction t1 = createFakeTx(v1, myAddress);
Transaction t2 = createFakeTx(v2, myAddress);
BigInteger expected = toNanoCoins(5, 50);
wallet.receive(t1);
wallet.receive(t2);
assertEquals(expected, wallet.getBalance());
// Now spend one coin.
BigInteger v3 = toNanoCoins(1, 0);
Transaction spend = wallet.createSend(new ECKey().toAddress(params), v3);
wallet.confirmSend(spend);
// We started with 5.50 so we should have 4.50 left.
BigInteger v4 = toNanoCoins(4, 50);
assertEquals(bitcoinValueToFriendlyString(v4),
bitcoinValueToFriendlyString(wallet.getBalance()));
// And spend another coin ...
wallet.confirmSend(wallet.createSend(new ECKey().toAddress(params), v3));
BigInteger v5 = toNanoCoins(3, 50);
assertEquals(bitcoinValueToFriendlyString(v5),
bitcoinValueToFriendlyString(wallet.getBalance()));
}
// Intuitively you'd expect to be able to create a transaction with identical inputs and outputs and get an
// identical result to the official client. However the signatures are not deterministic - signing the same data
// with the same key twice gives two different outputs. So we cannot prove bit-for-bit compatibility in this test
// suite.
@Test
public void testBlockChainCatchup() throws Exception {
Transaction tx1 = createFakeTx(Utils.toNanoCoins(1, 0), myAddress);
wallet.receive(tx1);
// Send 0.10 to somebody else.
Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
// Pretend it makes it into the block chain, our wallet state is cleared but we still have the keys, and we
// want to get back to our previous state.
wallet.receive(send1);
assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.90");
// And we do it again after the catchup.
Transaction send2 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
// What we'd really like to do is prove the official client would accept it .... no such luck unfortunately.
wallet.confirmSend(send2);
assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.80");
}
}