From 245505e9846dadbac5e582bb116070d963b27ba1 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 22 May 2015 00:00:23 +0100 Subject: [PATCH] Started connecting altcoinj to bitcoinj. Merged in files from bitcoinj which were rejected for altcoin support. --- .../java/org/altcoinj/core/AltcoinBlock.java | 229 ++ .../main/java/org/altcoinj/core/AuxPoW.java | 274 +++ .../java/org/altcoinj/core/MerkleBranch.java | 223 ++ .../params/AbstractDogecoinParams.java | 53 +- .../params/DogecoinMainNetParams.java | 15 +- .../params/DogecoinTestNet3Params.java | 12 +- .../protocols/payments/PaymentProtocol.java | 423 ---- .../protocols/payments/package-info.java | 5 - .../org/altcoinj/utils/MonetaryFormat.java | 451 ---- .../AbstractFullPrunedBlockChainTest.java | 341 --- .../dogecoinj/core/AlertMessageTest.java | 59 - .../dogecoin/dogecoinj/core/Base58Test.java | 91 - .../dogecoinj/core/BitcoinSerializerTest.java | 288 --- .../core/BitcoindComparisonTool.java | 372 ---- .../dogecoinj/core/BlockChainTest.java | 458 ---- .../dogecoin/dogecoinj/core/BlockTest.java | 189 -- .../dogecoinj/core/BloomFilterTest.java | 91 - .../dogecoinj/core/ChainSplitTest.java | 688 ------ .../com/dogecoin/dogecoinj/core/CoinTest.java | 140 -- .../dogecoinj/core/CoinbaseBlockTest.java | 102 - .../dogecoin/dogecoinj/core/ECKeyTest.java | 464 ---- ...ilteredBlockAndPartialMerkleTreeTests.java | 213 -- .../core/FullBlockTestGenerator.java | 1869 ----------------- .../core/H2FullPrunedBlockChainTest.java | 39 - .../core/LazyParseByteCacheTest.java | 529 ----- .../core/MemoryFullPrunedBlockChainTest.java | 23 - .../dogecoinj/core/MemoryPoolTest.java | 79 - .../dogecoin/dogecoinj/core/MessageTest.java | 70 - .../core/MySQLFullPrunedBlockChainTest.java | 51 - .../dogecoinj/core/PeerAddressTest.java | 46 - .../com/dogecoin/dogecoinj/core/PeerTest.java | 872 -------- .../PostgresFullPrunedBlockChainTest.java | 58 - .../dogecoinj/core/TransactionOutputTest.java | 69 - .../dogecoinj/core/TransactionTest.java | 92 - .../dogecoin/dogecoinj/core/UtilsTest.java | 65 - .../dogecoin/dogecoinj/core/VarIntTest.java | 66 - .../dogecoinj/core/VersionMessageTest.java | 52 - .../dogecoinj/core/WalletExtensionsTest.java | 28 - .../dogecoin/dogecoinj/crypto/BIP32Test.java | 185 -- .../dogecoinj/crypto/BIP38PrivateKeyTest.java | 172 -- .../crypto/ChildKeyDerivationTest.java | 250 --- .../dogecoinj/crypto/HDUtilsTest.java | 184 -- .../crypto/KeyCrypterScryptTest.java | 164 -- .../dogecoinj/crypto/MnemonicCodeTest.java | 218 -- .../dogecoinj/crypto/X509UtilsTest.java | 40 - .../net/NetworkAbstractionTests.java | 662 ------ .../net/discovery/SeedPeersTest.java | 50 - .../channels/ChannelConnectionTest.java | 781 ------- .../protocols/channels/ChannelTestUtils.java | 170 -- .../channels/PaymentChannelClientTest.java | 73 - .../channels/PaymentChannelServerTest.java | 149 -- .../channels/PaymentChannelStateTest.java | 805 ------- .../payments/PaymentProtocolTest.java | 154 -- .../payments/PaymentSessionTest.java | 224 -- .../dogecoinj/script/ScriptChunkTest.java | 49 - .../dogecoin/dogecoinj/script/ScriptTest.java | 405 ---- .../dogecoinj/store/SPVBlockStoreTest.java | 61 - .../store/WalletProtobufSerializerTest.java | 363 ---- .../dogecoinj/uri/BitcoinURITest.java | 403 ---- .../utils/BaseTaggableObjectTest.java | 29 - .../dogecoinj/utils/BtcFormatTest.java | 1477 ------------- .../dogecoinj/utils/ExchangeRateTest.java | 77 - .../utils/ExponentialBackoffTest.java | 81 - .../dogecoin/dogecoinj/utils/FiatTest.java | 59 - .../dogecoinj/utils/MonetaryFormatTest.java | 333 --- .../dogecoinj/wallet/BasicKeyChainTest.java | 274 --- .../wallet/DefaultCoinSelectorTest.java | 133 -- .../wallet/DefaultRiskAnalysisTest.java | 202 -- .../wallet/DeterministicKeyChainTest.java | 327 --- .../dogecoinj/wallet/KeyChainGroupTest.java | 588 ------ .../altcoinj}/core/AddressTest.java | 0 .../java/org/altcoinj/core/AuxPoWTest.java | 50 + .../altcoinj}/core/DumpedPrivateKeyTest.java | 0 .../org/altcoinj/core/MerkleBranchTest.java | 62 + .../altcoinj}/core/PeerGroupTest.java | 0 .../core/TransactionBroadcastTest.java | 0 .../core/VersionedChecksummedBytesTest.java | 0 .../altcoinj}/core/WalletTest.java | 0 78 files changed, 897 insertions(+), 17546 deletions(-) create mode 100644 core/src/main/java/org/altcoinj/core/AltcoinBlock.java create mode 100644 core/src/main/java/org/altcoinj/core/AuxPoW.java create mode 100644 core/src/main/java/org/altcoinj/core/MerkleBranch.java delete mode 100644 core/src/main/java/org/altcoinj/protocols/payments/PaymentProtocol.java delete mode 100644 core/src/main/java/org/altcoinj/protocols/payments/package-info.java delete mode 100644 core/src/main/java/org/altcoinj/utils/MonetaryFormat.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/AbstractFullPrunedBlockChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/AlertMessageTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/Base58Test.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/BitcoinSerializerTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/BitcoindComparisonTool.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/BlockChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/BlockTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/BloomFilterTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/ChainSplitTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/CoinTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/CoinbaseBlockTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/ECKeyTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/FilteredBlockAndPartialMerkleTreeTests.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/FullBlockTestGenerator.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/H2FullPrunedBlockChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/LazyParseByteCacheTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/MemoryFullPrunedBlockChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/MemoryPoolTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/MessageTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/MySQLFullPrunedBlockChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/PeerAddressTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/PeerTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/PostgresFullPrunedBlockChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/TransactionOutputTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/TransactionTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/UtilsTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/VarIntTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/VersionMessageTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/core/WalletExtensionsTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP32Test.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP38PrivateKeyTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/ChildKeyDerivationTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/HDUtilsTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/KeyCrypterScryptTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/MnemonicCodeTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/crypto/X509UtilsTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/net/NetworkAbstractionTests.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/net/discovery/SeedPeersTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelConnectionTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelTestUtils.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelClientTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelServerTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelStateTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentProtocolTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentSessionTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/script/ScriptChunkTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/script/ScriptTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/store/SPVBlockStoreTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/store/WalletProtobufSerializerTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/uri/BitcoinURITest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/utils/BaseTaggableObjectTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/utils/BtcFormatTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/utils/ExchangeRateTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/utils/ExponentialBackoffTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/utils/FiatTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/utils/MonetaryFormatTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/wallet/BasicKeyChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultCoinSelectorTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultRiskAnalysisTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/wallet/DeterministicKeyChainTest.java delete mode 100644 core/src/test/java/com/dogecoin/dogecoinj/wallet/KeyChainGroupTest.java rename core/src/test/java/{com/dogecoin/dogecoinj => org/altcoinj}/core/AddressTest.java (100%) create mode 100644 core/src/test/java/org/altcoinj/core/AuxPoWTest.java rename core/src/test/java/{com/dogecoin/dogecoinj => org/altcoinj}/core/DumpedPrivateKeyTest.java (100%) create mode 100644 core/src/test/java/org/altcoinj/core/MerkleBranchTest.java rename core/src/test/java/{com/dogecoin/dogecoinj => org/altcoinj}/core/PeerGroupTest.java (100%) rename core/src/test/java/{com/dogecoin/dogecoinj => org/altcoinj}/core/TransactionBroadcastTest.java (100%) rename core/src/test/java/{com/dogecoin/dogecoinj => org/altcoinj}/core/VersionedChecksummedBytesTest.java (100%) rename core/src/test/java/{com/dogecoin/dogecoinj => org/altcoinj}/core/WalletTest.java (100%) diff --git a/core/src/main/java/org/altcoinj/core/AltcoinBlock.java b/core/src/main/java/org/altcoinj/core/AltcoinBlock.java new file mode 100644 index 00000000..a3f19bd4 --- /dev/null +++ b/core/src/main/java/org/altcoinj/core/AltcoinBlock.java @@ -0,0 +1,229 @@ +/** + * Copyright 2011 Google Inc. + * Copyright 2014 Andreas Schildbach + * + * 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 org.bitcoinj.core; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import static org.bitcoinj.core.Coin.FIFTY_COINS; +import static org.bitcoinj.core.Utils.doubleDigest; +import static org.bitcoinj.core.Utils.doubleDigestTwoBuffers; + +/** + *

A block is a group of transactions, and is one of the fundamental data structures of the Bitcoin system. + * It records a set of {@link Transaction}s together with some data that links it into a place in the global block + * chain, and proves that a difficult calculation was done over its contents. See + * the Bitcoin technical paper for + * more detail on blocks.

+ * + * To get a block, you can either build one from the raw bytes you can get from another implementation, or request one + * specifically using {@link Peer#getBlock(Sha256Hash)}, or grab one from a downloaded {@link BlockChain}. + */ +public class AltcoinBlock extends org.bitcoinj.core.Block { + /** Bit used to indicate that a block contains an AuxPoW section, where the network supports AuxPoW */ + public static final int BLOCK_FLAG_AUXPOW = (1 << 8); + + private boolean auxpowParsed = false; + private boolean auxpowBytesValid = false; + + /** AuxPoW header element, if applicable. */ + @Nullable private AuxPoW auxpow; + + /** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */ + AltcoinBlock(NetworkParameters params) { + super(params); + } + + /** Constructs a block object from the Bitcoin wire format. */ + public AltcoinBlock(NetworkParameters params, byte[] payloadBytes) throws ProtocolException { + super(params, payloadBytes, 0, false, false, payloadBytes.length); + } + + /** + * Contruct a block object from the Bitcoin wire format. + * @param params NetworkParameters object. + * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. + * @param parseRetain Whether to retain the backing byte array for quick reserialization. + * If true and the backing byte array is invalidated due to modification of a field then + * the cached bytes may be repopulated and retained if the message is serialized again in the future. + * @param length The length of message if known. Usually this is provided when deserializing of the wire + * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH + * @throws ProtocolException + */ + public AltcoinBlock(NetworkParameters params, byte[] payloadBytes, boolean parseLazy, boolean parseRetain, int length) + throws ProtocolException { + super(params, payloadBytes, 0, parseLazy, parseRetain, length); + } + + /** + * Contruct a block object from the Bitcoin wire format. Used in the case of a block + * contained within another message (i.e. for AuxPoW header). + * + * @param params NetworkParameters object. + * @param payloadBytes Bitcoin protocol formatted byte array containing message content. + * @param offset The location of the first payload byte within the array. + * @param parent The message element which contains this block, maybe null for no parent. + * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. + * @param parseRetain Whether to retain the backing byte array for quick reserialization. + * If true and the backing byte array is invalidated due to modification of a field then + * the cached bytes may be repopulated and retained if the message is serialized again in the future. + * @param length The length of message if known. Usually this is provided when deserializing of the wire + * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH + * @throws ProtocolException + */ + public AltcoinBlock(NetworkParameters params, byte[] payloadBytes, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) + throws ProtocolException { + // TODO: Keep the parent + super(params, payloadBytes, offset, parseLazy, parseRetain, length); + } + + + /** + * Construct a block initialized with all the given fields. + * @param params Which network the block is for. + * @param version This should usually be set to 1 or 2, depending on if the height is in the coinbase input. + * @param prevBlockHash Reference to previous block in the chain or {@link Sha256Hash#ZERO_HASH} if genesis. + * @param merkleRoot The root of the merkle tree formed by the transactions. + * @param time UNIX time when the block was mined. + * @param difficultyTarget Number which this block hashes lower than. + * @param nonce Arbitrary number to make the block hash lower than the target. + * @param transactions List of transactions including the coinbase. + */ + public AltcoinBlock(NetworkParameters params, long version, Sha256Hash prevBlockHash, Sha256Hash merkleRoot, long time, + long difficultyTarget, long nonce, List transactions) { + super(params, version, prevBlockHash, merkleRoot, time, difficultyTarget, nonce, transactions); + } + + @Override + protected void parseAuxPoW() throws ProtocolException { + if (this.auxpowParsed) + return; + + if (this.params.isAuxPoWBlockVersion(this.version)) { + // The following is used in dogecoinj, but I don't think we necessarily need it + // payload.length >= 160) { // We have at least 2 headers in an Aux block. Workaround for StoredBlocks + this.auxpow = new AuxPoW(params, payload, cursor, this, parseLazy, parseRetain); + } else { + this.auxpow = null; + } + + this.auxpowParsed = true; + this.auxpowBytesValid = parseRetain; + } + + @Override + void parse() throws ProtocolException { + parseHeader(); + parseAuxPoW(); + parseTransactions(); + length = cursor - offset; + } + + @Override + protected void parseLite() throws ProtocolException { + // Ignore the header since it has fixed length. If length is not provided we will have to + // invoke a light parse of transactions to calculate the length. + if (length == UNKNOWN_LENGTH) { + Preconditions.checkState(parseLazy, + "Performing lite parse of block transaction as block was initialised from byte array " + + "without providing length. This should never need to happen."); + parseTransactions(); + // TODO: Handle AuxPoW header space + length = cursor - offset; + } else { + transactionBytesValid = !transactionsParsed || parseRetain && length > HEADER_SIZE; + } + headerBytesValid = !headerParsed || parseRetain && length >= HEADER_SIZE; + } + + @Override + void writeHeader(OutputStream stream) throws IOException { + super.writeHeader(stream); + // TODO: Write the AuxPoW header + } + + /** Returns a copy of the block, but without any transactions. */ + @Override + public Block cloneAsHeader() { + maybeParseHeader(); + AltcoinBlock block = new AltcoinBlock(params); + block.nonce = nonce; + block.prevBlockHash = prevBlockHash; + block.merkleRoot = getMerkleRoot(); + block.version = version; + block.time = time; + block.difficultyTarget = difficultyTarget; + block.transactions = null; + block.hash = getHash(); + block.auxpow = auxpow; + return block; + } + + /** Returns true if the hash of the block is OK (lower than difficulty target). */ + protected boolean checkProofOfWork(boolean throwException) throws VerificationException { + // TODO: Add AuxPoW support + + // This part is key - it is what proves the block was as difficult to make as it claims + // to be. Note however that in the context of this function, the block can claim to be + // as difficult as it wants to be .... if somebody was able to take control of our network + // connection and fork us onto a different chain, they could send us valid blocks with + // ridiculously easy difficulty and this function would accept them. + // + // To prevent this attack from being possible, elsewhere we check that the difficultyTarget + // field is of the right value. This requires us to have the preceeding blocks. + BigInteger target = getDifficultyTargetAsInteger(); + + BigInteger h = getHash().toBigInteger(); + if (h.compareTo(target) > 0) { + // Proof of work check failed! + if (throwException) + throw new VerificationException("Hash is higher than target: " + getHashAsString() + " vs " + + target.toString(16)); + else + return false; + } + return true; + } + + /** + * Checks the block data to ensure it follows the rules laid out in the network parameters. Specifically, + * throws an exception if the proof of work is invalid, or if the timestamp is too far from what it should be. + * This is not everything that is required for a block to be valid, only what is checkable independent + * of the chain and without a transaction index. + * + * @throws VerificationException + */ + @Override + public void verifyHeader() throws VerificationException { + super.verifyHeader(); + } +} diff --git a/core/src/main/java/org/altcoinj/core/AuxPoW.java b/core/src/main/java/org/altcoinj/core/AuxPoW.java new file mode 100644 index 00000000..82484c49 --- /dev/null +++ b/core/src/main/java/org/altcoinj/core/AuxPoW.java @@ -0,0 +1,274 @@ +/** + * Copyright 2011 Google Inc. + * Copyright 2014 Andreas Schildbach + * Copyright 2015 J. Ross Nicoll + * + * 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 org.bitcoinj.core; + +import org.bitcoinj.crypto.TransactionSignature; +import org.bitcoinj.script.Script; +import org.bitcoinj.script.ScriptBuilder; +import org.bitcoinj.script.ScriptOpCodes; +import org.bitcoinj.utils.ExchangeRate; +import org.bitcoinj.wallet.WalletTransaction.Pool; +import com.google.common.collect.ImmutableMap; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.io.*; +import java.util.*; + +import static org.bitcoinj.core.Utils.*; +import static com.google.common.base.Preconditions.checkState; + +/** + *

An AuxPoW header wraps a block header from another coin, enabling the foreign + * chain's proof of work to be used for this chain as well.

+ */ +public class AuxPoW extends ChildMessage implements Serializable { + + private static final Logger log = LoggerFactory.getLogger(AuxPoW.class); + private static final long serialVersionUID = -8567546957352643140L; + + private Transaction transaction; + private Sha256Hash hashBlock; + private MerkleBranch coinbaseBranch; + private MerkleBranch blockchainBranch; + private Block parentBlockHeader; + + // Transactions can be encoded in a way that will use more bytes than is optimal + // (due to VarInts having multiple encodings) + // MAX_BLOCK_SIZE must be compared to the optimal encoding, not the actual encoding, so when parsing, we keep track + // of the size of the ideal encoding in addition to the actual message size (which Message needs) so that Blocks + // can properly keep track of optimal encoded size + private transient int optimalEncodingMessageSize; + + public AuxPoW(NetworkParameters params, @Nullable Message parent) { + super(params); + transaction = new Transaction(params); + hashBlock = Sha256Hash.ZERO_HASH; + coinbaseBranch = new MerkleBranch(params, this); + blockchainBranch = new MerkleBranch(params, this); + parentBlockHeader = null; + } + + /** + * Creates an AuxPoW header by reading payload starting from offset bytes in. Length of header is fixed. + * @param params NetworkParameters object.1 + * @param payload Bitcoin protocol formatted byte array containing message content. + * @param offset The location of the first payload byte within the array. + * @param parent The message element which contains this header. + * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. + * @param parseRetain Whether to retain the backing byte array for quick reserialization. + * If true and the backing byte array is invalidated due to modification of a field then + * the cached bytes may be repopulated and retained if the message is serialized again in the future. + * @throws ProtocolException + */ + public AuxPoW(NetworkParameters params, byte[] payload, int offset, Message parent, boolean parseLazy, boolean parseRetain) + throws ProtocolException { + super(params, payload, offset, parent, parseLazy, parseRetain, Message.UNKNOWN_LENGTH); + } + + /** + * Creates an AuxPoW header by reading payload starting from offset bytes in. Length of header is fixed. + */ + public AuxPoW(NetworkParameters params, byte[] payload, @Nullable Message parent, boolean parseLazy, boolean parseRetain) + throws ProtocolException { + super(params, payload, 0, parent, parseLazy, parseRetain, Message.UNKNOWN_LENGTH); + } + + @Override + protected void parseLite() throws ProtocolException { + length = calcLength(payload, offset); + cursor = offset + length; + } + + protected static int calcLength(byte[] buf, int offset) { + VarInt varint; + // jump past transaction + int cursor = offset + Transaction.calcLength(buf, offset); + + // jump past header hash + cursor += 4; + + // Coin base branch + cursor += MerkleBranch.calcLength(buf, offset); + + // Block chain branch + cursor += MerkleBranch.calcLength(buf, offset); + + // Block header + cursor += Block.HEADER_SIZE; + + return cursor - offset + 4; + } + + @Override + void parse() throws ProtocolException { + + if (parsed) + return; + + cursor = offset; + transaction = new Transaction(params, payload, cursor, this, parseLazy, parseRetain, Message.UNKNOWN_LENGTH); + cursor += transaction.getOptimalEncodingMessageSize(); + optimalEncodingMessageSize = transaction.getOptimalEncodingMessageSize(); + + hashBlock = readHash(); + optimalEncodingMessageSize += 32; // Add the hash size to the optimal encoding + + coinbaseBranch = new MerkleBranch(params, this, payload, cursor, parseLazy, parseRetain); + cursor += coinbaseBranch.getOptimalEncodingMessageSize(); + optimalEncodingMessageSize += coinbaseBranch.getOptimalEncodingMessageSize(); + + blockchainBranch = new MerkleBranch(params, this, payload, cursor, parseLazy, parseRetain); + cursor += blockchainBranch.getOptimalEncodingMessageSize(); + optimalEncodingMessageSize += blockchainBranch.getOptimalEncodingMessageSize(); + + // Make a copy of JUST the contained block header, so the block parser doesn't try reading + // transactions past the end + byte[] blockBytes = Arrays.copyOfRange(payload, cursor, cursor + Block.HEADER_SIZE); + cursor += Block.HEADER_SIZE; + parentBlockHeader = new AltcoinBlock(params, blockBytes, 0, this, parseLazy, parseRetain, Block.HEADER_SIZE); + + length = cursor - offset; + } + + public int getOptimalEncodingMessageSize() { + if (optimalEncodingMessageSize != 0) + return optimalEncodingMessageSize; + maybeParse(); + if (optimalEncodingMessageSize != 0) + return optimalEncodingMessageSize; + optimalEncodingMessageSize = getMessageSize(); + return optimalEncodingMessageSize; + } + + @Override + public String toString() { + return toString(null); + } + + /** + * A human readable version of the transaction useful for debugging. The format is not guaranteed to be stable. + * @param chain If provided, will be used to estimate lock times (if set). Can be null. + */ + public String toString(@Nullable AbstractBlockChain chain) { + return transaction.toString(chain); + } + + @Override + protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { + transaction.bitcoinSerialize(stream); + stream.write(Utils.reverseBytes(hashBlock.getBytes())); + + coinbaseBranch.bitcoinSerialize(stream); + blockchainBranch.bitcoinSerialize(stream); + + parentBlockHeader.bitcoinSerializeToStream(stream); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AuxPoW input = (AuxPoW) o; + if (!transaction.equals(input.transaction)) return false; + if (!hashBlock.equals(input.hashBlock)) return false; + if (!coinbaseBranch.equals(input.hashBlock)) return false; + if (!blockchainBranch.equals(input.hashBlock)) return false; + if (!parentBlockHeader.equals(input.hashBlock)) return false; + return getHash().equals(input.getHash()); + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + transaction.hashCode(); + result = 31 * result + hashBlock.hashCode(); + result = 31 * result + coinbaseBranch.hashCode(); + result = 31 * result + blockchainBranch.hashCode(); + result = 31 * result + parentBlockHeader.hashCode(); + return result; + } + + /** + * Ensure object is fully parsed before invoking java serialization. The backing byte array + * is transient so if the object has parseLazy = true and hasn't invoked checkParse yet + * then data will be lost during serialization. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + maybeParse(); + out.defaultWriteObject(); + } + + /** + * Get the block header from the parent blockchain. The hash of the header + * is the value which should match the difficulty target. Note that blocks are + * not necessarily part of the parent blockchain, they simply must be valid + * blocks at the difficulty of the child blockchain. + */ + public Block getParentBlockHeader() { + return parentBlockHeader; + } + + /** + * Get the coinbase transaction from the AuxPoW header. This should contain a + * reference back to the block hash in its input scripts, to prove that the + * transaction was created after the block. + */ + public Transaction getCoinbase() { + return transaction; + } + + /** + * Get the Merkle branch used to connect the AuXPow header with this blockchain. + */ + public MerkleBranch getBlockchainBranch() { + return blockchainBranch; + } + + /** + * Get the Merkle branch used to connect the coinbase transaction with this blockchain. + */ + public MerkleBranch getCoinbaseBranch() { + return coinbaseBranch; + } + + /** + *

Checks the transaction contents for sanity, in ways that can be done in a standalone manner. + * Does not perform all checks on a transaction such as whether the inputs are already spent. + * Specifically this method verifies:

+ * + * + * + * @throws VerificationException + */ + public void verify() throws VerificationException { + maybeParse(); + // TODO: Verify the AuxPoW data + } +} diff --git a/core/src/main/java/org/altcoinj/core/MerkleBranch.java b/core/src/main/java/org/altcoinj/core/MerkleBranch.java new file mode 100644 index 00000000..9c6131d1 --- /dev/null +++ b/core/src/main/java/org/altcoinj/core/MerkleBranch.java @@ -0,0 +1,223 @@ +/** + * Copyright 2011 Google Inc. + * Copyright 2014 Andreas Schildbach + * + * 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 org.bitcoinj.core; + +import static org.bitcoinj.core.Utils.doubleDigestTwoBuffers; +import static org.bitcoinj.core.Utils.reverseBytes; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A Merkle branch contains the hashes from a leaf of a Merkle tree + * up to its root, plus a bitset used to define how the hashes are applied. + * Given the hash of the leaf, this can be used to calculate the tree + * root. This is useful for proving that a leaf belongs to a given tree. + */ +public class MerkleBranch extends ChildMessage implements Serializable { + private static final long serialVersionUID = 2; + + // Merkle branches can be encoded in a way that will use more bytes than is optimal + // (due to VarInts having multiple encodings) + // MAX_BLOCK_SIZE must be compared to the optimal encoding, not the actual encoding, so when parsing, we keep track + // of the size of the ideal encoding in addition to the actual message size (which Message needs) so that Blocks + // can properly keep track of optimal encoded size + private transient int optimalEncodingMessageSize; + + private List branchHashes; + private long branchSideMask; + + public MerkleBranch(NetworkParameters params, @Nullable ChildMessage parent) { + super(params); + setParent(parent); + + this.branchHashes = new ArrayList(); + this.branchSideMask = 0; + } + + /** + * Deserializes an input message. This is usually part of a merkle branch message. + */ + public MerkleBranch(NetworkParameters params, @Nullable ChildMessage parent, byte[] payload, int offset) throws ProtocolException { + super(params, payload, offset); + setParent(parent); + } + + /** + * Deserializes an input message. This is usually part of a merkle branch message. + * @param params NetworkParameters object. + * @param payload Bitcoin protocol formatted byte array containing message content. + * @param offset The location of the first payload byte within the array. + * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. + * @param parseRetain Whether to retain the backing byte array for quick reserialization. + * If true and the backing byte array is invalidated due to modification of a field then + * the cached bytes may be repopulated and retained if the message is serialized again in the future. + * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH + * @throws ProtocolException + */ + public MerkleBranch(NetworkParameters params, ChildMessage parent, byte[] payload, int offset, + boolean parseLazy, boolean parseRetain) + throws ProtocolException { + super(params, payload, offset, parent, parseLazy, parseRetain, UNKNOWN_LENGTH); + } + + public MerkleBranch(NetworkParameters params, @Nullable ChildMessage parent, + final List hashes, final long branchSideMask) { + super(params); + setParent(parent); + + this.branchHashes = hashes; + this.branchSideMask = branchSideMask; + } + + @Override + protected void parseLite() throws ProtocolException { + length = calcLength(payload, offset); + cursor = offset + length; + } + + protected static int calcLength(byte[] buf, int offset) { + VarInt varint = new VarInt(buf, offset); + + return ((int) varint.value) * 4 + 4; + } + + @Override + void parse() throws ProtocolException { + if (parsed) + return; + + cursor = offset; + + final int hashCount = (int) readVarInt(); + optimalEncodingMessageSize += VarInt.sizeOf(hashCount); + branchHashes = new ArrayList(hashCount); + for (int hashIdx = 0; hashIdx < hashCount; hashIdx++) { + branchHashes.add(readHash()); + } + optimalEncodingMessageSize += 32 * hashCount; + branchSideMask = readUint32(); + optimalEncodingMessageSize += 4; + } + + @Override + protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { + stream.write(new VarInt(branchHashes.size()).encode()); + for (Sha256Hash hash: branchHashes) { + stream.write(Utils.reverseBytes(hash.getBytes())); + } + Utils.uint32ToByteStreamLE(branchSideMask, stream); + } + + /** + * Calculate the merkle branch root based on the supplied hashes and the given leaf hash. + * Used to verify that the given leaf and root are part of the same tree. + */ + public Sha256Hash calculateMerkleRoot(final Sha256Hash leaf) { + byte[] target = reverseBytes(leaf.getBytes()); + long mask = branchSideMask; + + for (Sha256Hash hash: branchHashes) { + target = (mask & 1) == 0 + ? doubleDigestTwoBuffers(target, 0, 32, reverseBytes(hash.getBytes()), 0, 32) + : doubleDigestTwoBuffers(reverseBytes(hash.getBytes()), 0, 32, target, 0, 32); + mask >>= 1; + } + return new Sha256Hash(reverseBytes(target)); + } + + /** + * Get the hashes which make up this branch. + */ + public List getHashes() { + return Collections.unmodifiableList(this.branchHashes); + } + + /** + * Get the number of hashes in this branch. + */ + public int getSize() { + return branchHashes.size(); + } + + public int getOptimalEncodingMessageSize() { + if (optimalEncodingMessageSize != 0) + return optimalEncodingMessageSize; + maybeParse(); + if (optimalEncodingMessageSize != 0) + return optimalEncodingMessageSize; + optimalEncodingMessageSize = getMessageSize(); + return optimalEncodingMessageSize; + } + + /** + * Returns a human readable debug string. + */ + @Override + public String toString() { + return "Merkle branch"; + } + + /** + * Ensure object is fully parsed before invoking java serialization. The backing byte array + * is transient so if the object has parseLazy = true and hasn't invoked checkParse yet + * then data will be lost during serialization. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + maybeParse(); + out.defaultWriteObject(); + } + + /** + * Should check that the merkle branch side bits are not wider than the + * provided hashes. + * @throws VerificationException If the branch is invalid. + */ + public void verify() throws VerificationException { + maybeParse(); + // TODO: Check the flags make sense for the inputs + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MerkleBranch input = (MerkleBranch) o; + + if (!branchHashes.equals(input.branchHashes)) return false; + if (branchSideMask != input.branchSideMask) return false; + + return true; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + branchHashes.hashCode(); + result = 31 * result + (int) branchSideMask; + return result; + } +} diff --git a/core/src/main/java/org/altcoinj/params/AbstractDogecoinParams.java b/core/src/main/java/org/altcoinj/params/AbstractDogecoinParams.java index 2b8168c4..5e70bbf4 100644 --- a/core/src/main/java/org/altcoinj/params/AbstractDogecoinParams.java +++ b/core/src/main/java/org/altcoinj/params/AbstractDogecoinParams.java @@ -16,9 +16,14 @@ package org.altcoinj.params; -import org.altcoinj.core.NetworkParameters; -import org.altcoinj.core.Sha256Hash; -import org.altcoinj.core.Utils; +import java.math.RoundingMode; +import java.util.HashMap; +import java.util.Map; + +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Utils; +import org.bitcoinj.utils.MonetaryFormat; import static com.google.common.base.Preconditions.checkState; @@ -26,27 +31,55 @@ import static com.google.common.base.Preconditions.checkState; * Parameters for the main Dogecoin production network on which people trade goods and services. */ public class AbstractDogecoinParams extends NetworkParameters { + /** Standard format for the DOGE denomination. */ + public static final MonetaryFormat DOGE; + /** Standard format for the mDOGE denomination. */ + public static final MonetaryFormat MDOGE; + /** Standard format for the Koinu denomination. */ + public static final MonetaryFormat KOINU; + public static final int DOGE_TARGET_TIMESPAN = 4 * 60 * 60; // 4 hours per difficulty cycle, on average. public static final int DOGE_TARGET_TIMESPAN_NEW = 60; // 60s per difficulty cycle, on average. Kicks in after block 145k. public static final int DOGE_TARGET_SPACING = 1 * 60; // 1 minute per block. - public static final int DOGE_INTERVAL = TARGET_TIMESPAN / TARGET_SPACING; - public static final int DOGE_INTERVAL_NEW = TARGET_TIMESPAN_NEW / TARGET_SPACING; + public static final int DOGE_INTERVAL = DOGE_TARGET_TIMESPAN / DOGE_TARGET_SPACING; + public static final int DOGE_INTERVAL_NEW = DOGE_TARGET_TIMESPAN_NEW / DOGE_TARGET_SPACING; + + /** Currency code for base 1 Dogecoin. */ + public static final String CODE_DOGE = "DOGE"; + /** Currency code for base 1/1,000 Dogecoin. */ + public static final String CODE_MDOGE = "mDOGE"; + /** Currency code for base 1/100,000,000 Dogecoin. */ + public static final String CODE_KOINU = "Koinu"; + + private static final Map CURRENCY_CODES = new HashMap(); + + static { + CURRENCY_CODES.put(0, CODE_DOGE); + CURRENCY_CODES.put(3, CODE_MDOGE); + CURRENCY_CODES.put(8, CODE_KOINU); + + DOGE = MonetaryFormat.BTC.replaceCodes(CURRENCY_CODES); + MDOGE = DOGE.shift(3).minDecimals(2).optionalDecimals(2); + KOINU = DOGE.shift(6).minDecimals(0).optionalDecimals(2); + } /** The string returned by getId() for the main, production network where people trade things. */ public static final String ID_DOGE_MAINNET = "org.dogecoin.production"; /** The string returned by getId() for the testnet. */ public static final String ID_DOGE_TESTNET = "org.dogecoin.test"; - protected int newInterval; - protected int newTargetTimespan; + protected final int newInterval; + protected final int newTargetTimespan; + protected final int diffChangeTarget; - public AbstractDogecoinParams() { + public AbstractDogecoinParams(final int setDiffChangeTarget) { super(); interval = DOGE_INTERVAL; newInterval = DOGE_INTERVAL_NEW; targetTimespan = DOGE_TARGET_TIMESPAN; newTargetTimespan = DOGE_TARGET_TIMESPAN_NEW; maxTarget = Utils.decodeCompactBits(0x1e0fffffL); + diffChangeTarget = setDiffChangeTarget; packetMagic = 0xc0c0c0c0; bip32HeaderPub = 0x0488C42E; //The 4 byte header that serializes in base58 to "xpub". (?) @@ -67,5 +100,7 @@ public class AbstractDogecoinParams extends NetworkParameters { return newTargetTimespan; } - public MonetaryFormat + public MonetaryFormat getMonetaryFormat() { + return DOGE; + } } diff --git a/core/src/main/java/org/altcoinj/params/DogecoinMainNetParams.java b/core/src/main/java/org/altcoinj/params/DogecoinMainNetParams.java index 41766852..149d9902 100644 --- a/core/src/main/java/org/altcoinj/params/DogecoinMainNetParams.java +++ b/core/src/main/java/org/altcoinj/params/DogecoinMainNetParams.java @@ -16,9 +16,12 @@ package org.altcoinj.params; -import org.altcoinj.core.NetworkParameters; -import org.altcoinj.core.Sha256Hash; -import org.altcoinj.core.Utils; +import java.util.Map; + +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Utils; +import org.bitcoinj.utils.MonetaryFormat; import static com.google.common.base.Preconditions.checkState; @@ -26,8 +29,10 @@ import static com.google.common.base.Preconditions.checkState; * Parameters for the main production network on which people trade goods and services. */ public class DogecoinMainNetParams extends AbstractDogecoinParams { + protected static final int DIFFICULTY_CHANGE_TARGET = 145000; + public DogecoinMainNetParams() { - super(); + super(DIFFICULTY_CHANGE_TARGET); dumpedPrivateKeyHeader = 158; //This is always addressHeader + 128 addressHeader = 30; p2shHeader = 22; @@ -43,8 +48,6 @@ public class DogecoinMainNetParams extends AbstractDogecoinParams { subsidyDecreaseBlockCount = 100000; spendableCoinbaseDepth = 100; - diffChangeTarget = 145000; - String genesisHash = genesisBlock.getHashAsString(); checkState(genesisHash.equals("1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"), genesisHash); diff --git a/core/src/main/java/org/altcoinj/params/DogecoinTestNet3Params.java b/core/src/main/java/org/altcoinj/params/DogecoinTestNet3Params.java index 60de54ca..6b654a19 100644 --- a/core/src/main/java/org/altcoinj/params/DogecoinTestNet3Params.java +++ b/core/src/main/java/org/altcoinj/params/DogecoinTestNet3Params.java @@ -17,8 +17,8 @@ package org.altcoinj.params; -import org.altcoinj.core.NetworkParameters; -import org.altcoinj.core.Utils; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Utils; import org.spongycastle.util.encoders.Hex; import static com.google.common.base.Preconditions.checkState; @@ -28,8 +28,10 @@ import static com.google.common.base.Preconditions.checkState; * and testing of applications and new Bitcoin versions. */ public class DogecoinTestNet3Params extends AbstractDogecoinParams { + protected static final int DIFFICULTY_CHANGE_TARGET = 145000; + public DogecoinTestNet3Params() { - super(); + super(DIFFICULTY_CHANGE_TARGET); id = ID_DOGE_TESTNET; // Genesis hash is bb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e packetMagic = 0xfcc1b7dc; @@ -49,8 +51,6 @@ public class DogecoinTestNet3Params extends AbstractDogecoinParams { checkState(genesisHash.equals("bb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e")); alertSigningKey = Hex.decode("042756726da3c7ef515d89212ee1705023d14be389e25fe15611585661b9a20021908b2b80a3c7200a0139dd2b26946606aab0eef9aa7689a6dc2c7eee237fa834"); - diffChangeTarget = 145000; - dnsSeeds = new String[] { "testnets.chain.so" // Chain.so }; @@ -61,7 +61,7 @@ public class DogecoinTestNet3Params extends AbstractDogecoinParams { private static DogecoinTestNet3Params instance; public static synchronized DogecoinTestNet3Params get() { if (instance == null) { - instance = new TestNet3Params(); + instance = new DogecoinTestNet3Params(); } return instance; } diff --git a/core/src/main/java/org/altcoinj/protocols/payments/PaymentProtocol.java b/core/src/main/java/org/altcoinj/protocols/payments/PaymentProtocol.java deleted file mode 100644 index dfc1ce02..00000000 --- a/core/src/main/java/org/altcoinj/protocols/payments/PaymentProtocol.java +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Copyright 2013 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.protocols.payments; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.crypto.X509Utils; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import org.bitcoin.protocols.payments.Protos; - -import javax.annotation.Nullable; -import java.io.Serializable; -import java.security.*; -import java.security.cert.*; -import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.List; - -/** - *

Utility methods and constants for working with - * BIP 70 aka the payment protocol. These are low level wrappers around the protocol buffers. If you're implementing - * a wallet app, look at {@link PaymentSession} for a higher level API that should simplify working with the protocol.

- * - *

BIP 70 defines a binary, protobuf based protocol that runs directly between sender and receiver of funds. Payment - * protocol data does not flow over the Bitcoin P2P network or enter the block chain. It's instead for data that is only - * of interest to the parties involved but isn't otherwise needed for consensus.

- */ -public class PaymentProtocol { - - // MIME types as defined in DIP71. - public static final String MIMETYPE_PAYMENTREQUEST = "application/vnd.doge.payment.request"; - public static final String MIMETYPE_PAYMENT = "application/vnd.doge.payment.payment"; - public static final String MIMETYPE_PAYMENTACK = "application/vnd.doge.payment.ack"; - - /** - * Create a payment request with one standard pay to address output. You may want to sign the request using - * {@link #signPaymentRequest}. Use {@link Protos.PaymentRequest.Builder#build} to get the actual payment - * request. - * - * @param params network parameters - * @param amount amount of coins to request, or null - * @param toAddress address to request coins to - * @param memo arbitrary, user readable memo, or null if none - * @param paymentUrl URL to send payment message to, or null if none - * @param merchantData arbitrary merchant data, or null if none - * @return created payment request, in its builder form - */ - public static Protos.PaymentRequest.Builder createPaymentRequest(NetworkParameters params, - @Nullable Coin amount, Address toAddress, @Nullable String memo, @Nullable String paymentUrl, - @Nullable byte[] merchantData) { - return createPaymentRequest(params, ImmutableList.of(createPayToAddressOutput(amount, toAddress)), memo, - paymentUrl, merchantData); - } - - /** - * Create a payment request. You may want to sign the request using {@link #signPaymentRequest}. Use - * {@link Protos.PaymentRequest.Builder#build} to get the actual payment request. - * - * @param params network parameters - * @param outputs list of outputs to request coins to - * @param memo arbitrary, user readable memo, or null if none - * @param paymentUrl URL to send payment message to, or null if none - * @param merchantData arbitrary merchant data, or null if none - * @return created payment request, in its builder form - */ - public static Protos.PaymentRequest.Builder createPaymentRequest(NetworkParameters params, - List outputs, @Nullable String memo, @Nullable String paymentUrl, - @Nullable byte[] merchantData) { - final Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder(); - paymentDetails.setNetwork(params.getPaymentProtocolId()); - for (Protos.Output output : outputs) - paymentDetails.addOutputs(output); - if (memo != null) - paymentDetails.setMemo(memo); - if (paymentUrl != null) - paymentDetails.setPaymentUrl(paymentUrl); - if (merchantData != null) - paymentDetails.setMerchantData(ByteString.copyFrom(merchantData)); - paymentDetails.setTime(Utils.currentTimeSeconds()); - - final Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder(); - paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString()); - return paymentRequest; - } - - /** - * Parse a payment request. - * - * @param paymentRequest payment request to parse - * @return instance of {@link PaymentSession}, used as a value object - * @throws PaymentProtocolException - */ - public static PaymentSession parsePaymentRequest(Protos.PaymentRequest paymentRequest) - throws PaymentProtocolException { - return new PaymentSession(paymentRequest, false, null); - } - - /** - * Sign the provided payment request. - * - * @param paymentRequest Payment request to sign, in its builder form. - * @param certificateChain Certificate chain to send with the payment request, ordered from client certificate to root - * certificate. The root certificate itself may be omitted. - * @param privateKey The key to sign with. Must match the public key from the first certificate of the certificate chain. - */ - public static void signPaymentRequest(Protos.PaymentRequest.Builder paymentRequest, - X509Certificate[] certificateChain, PrivateKey privateKey) { - try { - final Protos.X509Certificates.Builder certificates = Protos.X509Certificates.newBuilder(); - for (final Certificate certificate : certificateChain) - certificates.addCertificate(ByteString.copyFrom(certificate.getEncoded())); - - paymentRequest.setPkiType("x509+sha256"); - paymentRequest.setPkiData(certificates.build().toByteString()); - paymentRequest.setSignature(ByteString.EMPTY); - final Protos.PaymentRequest paymentRequestToSign = paymentRequest.build(); - - final String algorithm; - if (privateKey.getAlgorithm().equalsIgnoreCase("RSA")) - algorithm = "SHA256withRSA"; - else - throw new IllegalStateException(privateKey.getAlgorithm()); - - final Signature signature = Signature.getInstance(algorithm); - signature.initSign(privateKey); - signature.update(paymentRequestToSign.toByteArray()); - - paymentRequest.setSignature(ByteString.copyFrom(signature.sign())); - } catch (final GeneralSecurityException x) { - // Should never happen so don't make users have to think about it. - throw new RuntimeException(x); - } - } - - /** - * Uses the provided PKI method to find the corresponding public key and verify the provided signature. - * - * @param paymentRequest Payment request to verify. - * @param trustStore KeyStore of trusted root certificate authorities. - * @return verification data, or null if no PKI method was specified in the {@link Protos.PaymentRequest}. - * @throws PaymentProtocolException if payment request could not be verified. - */ - public static @Nullable PkiVerificationData verifyPaymentRequestPki(Protos.PaymentRequest paymentRequest, KeyStore trustStore) - throws PaymentProtocolException { - List certs = null; - try { - final String pkiType = paymentRequest.getPkiType(); - if (pkiType.equals("none")) - // Nothing to verify. Everything is fine. Move along. - return null; - - String algorithm; - if (pkiType.equals("x509+sha256")) - algorithm = "SHA256withRSA"; - else if (pkiType.equals("x509+sha1")) - algorithm = "SHA1withRSA"; - else - throw new PaymentProtocolException.InvalidPkiType("Unsupported PKI type: " + pkiType); - - Protos.X509Certificates protoCerts = Protos.X509Certificates.parseFrom(paymentRequest.getPkiData()); - if (protoCerts.getCertificateCount() == 0) - throw new PaymentProtocolException.InvalidPkiData("No certificates provided in message: server config error"); - - // Parse the certs and turn into a certificate chain object. Cert factories can parse both DER and base64. - // The ordering of certificates is defined by the payment protocol spec to be the same as what the Java - // crypto API requires - convenient! - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - certs = Lists.newArrayList(); - for (ByteString bytes : protoCerts.getCertificateList()) - certs.add((X509Certificate) certificateFactory.generateCertificate(bytes.newInput())); - CertPath path = certificateFactory.generateCertPath(certs); - - // Retrieves the most-trusted CAs from keystore. - PKIXParameters params = new PKIXParameters(trustStore); - // Revocation not supported in the current version. - params.setRevocationEnabled(false); - - // Now verify the certificate chain is correct and trusted. This let's us get an identity linked pubkey. - CertPathValidator validator = CertPathValidator.getInstance("PKIX"); - PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(path, params); - PublicKey publicKey = result.getPublicKey(); - // OK, we got an identity, now check it was used to sign this message. - Signature signature = Signature.getInstance(algorithm); - // Note that we don't use signature.initVerify(certs.get(0)) here despite it being the most obvious - // way to set it up, because we don't care about the constraints specified on the certificates: any - // cert that links a key to a domain name or other identity will do for us. - signature.initVerify(publicKey); - Protos.PaymentRequest.Builder reqToCheck = paymentRequest.toBuilder(); - reqToCheck.setSignature(ByteString.EMPTY); - signature.update(reqToCheck.build().toByteArray()); - if (!signature.verify(paymentRequest.getSignature().toByteArray())) - throw new PaymentProtocolException.PkiVerificationException("Invalid signature, this payment request is not valid."); - - // Signature verifies, get the names from the identity we just verified for presentation to the user. - final X509Certificate cert = certs.get(0); - String displayName = X509Utils.getDisplayNameFromCertificate(cert, true); - if (displayName == null) - throw new PaymentProtocolException.PkiVerificationException("Could not extract name from certificate"); - // Everything is peachy. Return some useful data to the caller. - return new PkiVerificationData(displayName, publicKey, result.getTrustAnchor()); - } catch (InvalidProtocolBufferException e) { - // Data structures are malformed. - throw new PaymentProtocolException.InvalidPkiData(e); - } catch (CertificateException e) { - // The X.509 certificate data didn't parse correctly. - throw new PaymentProtocolException.PkiVerificationException(e); - } catch (NoSuchAlgorithmException e) { - // Should never happen so don't make users have to think about it. PKIX is always present. - throw new RuntimeException(e); - } catch (InvalidAlgorithmParameterException e) { - throw new RuntimeException(e); - } catch (CertPathValidatorException e) { - // The certificate chain isn't known or trusted, probably, the server is using an SSL root we don't - // know about and the user needs to upgrade to a new version of the software (or import a root cert). - throw new PaymentProtocolException.PkiVerificationException(e, certs); - } catch (InvalidKeyException e) { - // Shouldn't happen if the certs verified correctly. - throw new PaymentProtocolException.PkiVerificationException(e); - } catch (SignatureException e) { - // Something went wrong during hashing (yes, despite the name, this does not mean the sig was invalid). - throw new PaymentProtocolException.PkiVerificationException(e); - } catch (KeyStoreException e) { - throw new RuntimeException(e); - } - } - - /** - * Information about the X.509 signature's issuer and subject. - */ - public static class PkiVerificationData { - /** Display name of the payment requestor, could be a domain name, email address, legal name, etc */ - public final String displayName; - /** SSL public key that was used to sign. */ - public final PublicKey merchantSigningKey; - /** Object representing the CA that verified the merchant's ID */ - public final TrustAnchor rootAuthority; - /** String representing the display name of the CA that verified the merchant's ID */ - public final String rootAuthorityName; - - private PkiVerificationData(@Nullable String displayName, PublicKey merchantSigningKey, - TrustAnchor rootAuthority) throws PaymentProtocolException.PkiVerificationException { - try { - this.displayName = displayName; - this.merchantSigningKey = merchantSigningKey; - this.rootAuthority = rootAuthority; - this.rootAuthorityName = X509Utils.getDisplayNameFromCertificate(rootAuthority.getTrustedCert(), true); - } catch (CertificateParsingException x) { - throw new PaymentProtocolException.PkiVerificationException(x); - } - } - - @Override - public String toString() { - return Objects.toStringHelper(this) - .add("displayName", displayName) - .add("rootAuthorityName", rootAuthorityName) - .add("merchantSigningKey", merchantSigningKey) - .add("rootAuthority", rootAuthority) - .toString(); - } - } - - /** - * Create a payment message with one standard pay to address output. - * - * @param transactions one or more transactions that satisfy the requested outputs. - * @param refundAmount amount of coins to request as a refund, or null if no refund. - * @param refundAddress address to refund coins to - * @param memo arbitrary, user readable memo, or null if none - * @param merchantData arbitrary merchant data, or null if none - * @return created payment message - */ - public static Protos.Payment createPaymentMessage(List transactions, - @Nullable Coin refundAmount, @Nullable Address refundAddress, @Nullable String memo, - @Nullable byte[] merchantData) { - if (refundAddress != null) { - if (refundAmount == null) - throw new IllegalArgumentException("Specify refund amount if refund address is specified."); - return createPaymentMessage(transactions, - ImmutableList.of(createPayToAddressOutput(refundAmount, refundAddress)), memo, merchantData); - } else { - return createPaymentMessage(transactions, null, memo, merchantData); - } - } - - /** - * Create a payment message. This wraps up transaction data along with anything else useful for making a payment. - * - * @param transactions transactions to include with the payment message - * @param refundOutputs list of outputs to refund coins to, or null - * @param memo arbitrary, user readable memo, or null if none - * @param merchantData arbitrary merchant data, or null if none - * @return created payment message - */ - public static Protos.Payment createPaymentMessage(List transactions, - @Nullable List refundOutputs, @Nullable String memo, @Nullable byte[] merchantData) { - Protos.Payment.Builder builder = Protos.Payment.newBuilder(); - for (Transaction transaction : transactions) { - transaction.verify(); - builder.addTransactions(ByteString.copyFrom(transaction.unsafeBitcoinSerialize())); - } - if (refundOutputs != null) { - for (Protos.Output output : refundOutputs) - builder.addRefundTo(output); - } - if (memo != null) - builder.setMemo(memo); - if (merchantData != null) - builder.setMerchantData(ByteString.copyFrom(merchantData)); - return builder.build(); - } - - /** - * Parse transactions from payment message. - * - * @param params network parameters (needed for transaction deserialization) - * @param paymentMessage payment message to parse - * @return list of transactions - */ - public static List parseTransactionsFromPaymentMessage(NetworkParameters params, - Protos.Payment paymentMessage) { - final List transactions = new ArrayList(paymentMessage.getTransactionsCount()); - for (final ByteString transaction : paymentMessage.getTransactionsList()) - transactions.add(new Transaction(params, transaction.toByteArray())); - return transactions; - } - - /** - * Message returned by the merchant in response to a Payment message. - */ - public static class Ack { - @Nullable private final String memo; - - Ack(@Nullable String memo) { - this.memo = memo; - } - - /** - * Returns the memo included by the merchant in the payment ack. This message is typically displayed to the user - * as a notification (e.g. "Your payment was received and is being processed"). If none was provided, returns - * null. - */ - @Nullable public String getMemo() { - return memo; - } - } - - /** - * Create a payment ack. - * - * @param paymentMessage payment message to send with the ack - * @param memo arbitrary, user readable memo, or null if none - * @return created payment ack - */ - public static Protos.PaymentACK createPaymentAck(Protos.Payment paymentMessage, @Nullable String memo) { - final Protos.PaymentACK.Builder builder = Protos.PaymentACK.newBuilder(); - builder.setPayment(paymentMessage); - if (memo != null) - builder.setMemo(memo); - return builder.build(); - } - - /** - * Parse payment ack into an object. - */ - public static Ack parsePaymentAck(Protos.PaymentACK paymentAck) { - final String memo = paymentAck.hasMemo() ? paymentAck.getMemo() : null; - return new Ack(memo); - } - - /** - * Create a standard pay to address output for usage in {@link #createPaymentRequest} and - * {@link #createPaymentMessage}. - * - * @param amount amount to pay, or null - * @param address address to pay to - * @return output - */ - public static Protos.Output createPayToAddressOutput(@Nullable Coin amount, Address address) { - Protos.Output.Builder output = Protos.Output.newBuilder(); - if (amount != null) { - if (amount.compareTo(NetworkParameters.MAX_MONEY) > 0) - throw new IllegalArgumentException("Amount too big: " + amount); - output.setAmount(amount.value); - } else { - output.setAmount(0); - } - output.setScript(ByteString.copyFrom(ScriptBuilder.createOutputScript(address).getProgram())); - return output.build(); - } - - /** - * Value object to hold amount/script pairs. - */ - public static class Output implements Serializable { - public final @Nullable Coin amount; - public final byte[] scriptData; - - public Output(@Nullable Coin amount, byte[] scriptData) { - this.amount = amount; - this.scriptData = scriptData; - } - } -} diff --git a/core/src/main/java/org/altcoinj/protocols/payments/package-info.java b/core/src/main/java/org/altcoinj/protocols/payments/package-info.java deleted file mode 100644 index fb419735..00000000 --- a/core/src/main/java/org/altcoinj/protocols/payments/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * The BIP70 payment protocol wraps Bitcoin transactions and adds various useful features like memos, refund addresses - * and authentication. - */ -package com.dogecoin.dogecoinj.protocols.payments; \ No newline at end of file diff --git a/core/src/main/java/org/altcoinj/utils/MonetaryFormat.java b/core/src/main/java/org/altcoinj/utils/MonetaryFormat.java deleted file mode 100644 index 1b090c1b..00000000 --- a/core/src/main/java/org/altcoinj/utils/MonetaryFormat.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * 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 org.altcoinj.utils; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.math.LongMath.checkedMultiply; -import static com.google.common.math.LongMath.checkedPow; -import static com.google.common.math.LongMath.divide; - -import java.math.RoundingMode; -import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import org.altcoinj.core.Coin; -import org.altcoinj.core.Monetary; - -/** - *

- * Utility for formatting and parsing coin values to and from human readable form. - *

- * - *

- * MonetaryFormat instances are immutable. Invoking a configuration method has no effect on the receiving instance; you - * must store and use the new instance it returns, instead. Instances are thread safe, so they may be stored safely as - * static constants. - *

- */ -public class DogecoinMonetaryFormat extends org.bitcoinj.utils.MonetaryFormat { - - /** Standard format for the BTC denomination. */ - public static final MonetaryFormat BTC = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 3); - /** Standard format for the mBTC denomination. */ - public static final MonetaryFormat MBTC = new MonetaryFormat().shift(3).minDecimals(2).optionalDecimals(2); - /** Standard format for the µBTC denomination. */ - public static final MonetaryFormat UBTC = new MonetaryFormat().shift(6).minDecimals(0).optionalDecimals(2); - /** Standard format for fiat amounts. */ - public static final MonetaryFormat FIAT = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 1); - /** Currency code for base 1 Dogecoin. */ - public static final String CODE_BTC = "DOGE"; - /** Currency code for base 1/1000 Dogecoin. */ - public static final String CODE_MBTC = "mDOGE"; - /** Currency code for base 1/1000000 Dogecoin. */ - public static final String CODE_UBTC = "µDOGE"; - - private final char negativeSign; - private final char positiveSign; - private final char zeroDigit; - private final char decimalMark; - private final int minDecimals; - private final List decimalGroups; - private final int shift; - private final RoundingMode roundingMode; - private final Map codes; - private final char codeSeparator; - private final boolean codePrefixed; - - private static final String DECIMALS_PADDING = "0000000000000000"; // a few more than necessary for Bitcoin - - /** - * Set character to prefix negative values. - */ - public MonetaryFormat negativeSign(char negativeSign) { - checkArgument(!Character.isDigit(negativeSign)); - checkArgument(negativeSign > 0); - if (negativeSign == this.negativeSign) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Set character to prefix positive values. A zero value means no sign is used in this case. For parsing, a missing - * sign will always be interpreted as if the positive sign was used. - */ - public MonetaryFormat positiveSign(char positiveSign) { - checkArgument(!Character.isDigit(positiveSign)); - if (positiveSign == this.positiveSign) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Set character range to use for representing digits. It starts with the specified character representing zero. - */ - public MonetaryFormat digits(char zeroDigit) { - if (zeroDigit == this.zeroDigit) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Set character to use as the decimal mark. If the formatted value does not have any decimals, no decimal mark is - * used either. - */ - public MonetaryFormat decimalMark(char decimalMark) { - checkArgument(!Character.isDigit(decimalMark)); - checkArgument(decimalMark > 0); - if (decimalMark == this.decimalMark) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Set minimum number of decimals to use for formatting. If the value precision exceeds all decimals specified - * (including additional decimals specified by {@link #optionalDecimals(int...)} or - * {@link #repeatOptionalDecimals(int, int)}), the value will be rounded. This configuration is not relevant for - * parsing. - */ - public MonetaryFormat minDecimals(int minDecimals) { - if (minDecimals == this.minDecimals) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - *

- * Set additional groups of decimals to use after the minimum decimals, if they are useful for expressing precision. - * Each value is a number of decimals in that group. If the value precision exceeds all decimals specified - * (including minimum decimals), the value will be rounded. This configuration is not relevant for parsing. - *

- * - *

- * For example, if you pass 4,2 it will add four decimals to your formatted string if needed, and then add - * another two decimals if needed. At this point, rather than adding further decimals the value will be rounded. - *

- * - * @param groups - * any number numbers of decimals, one for each group - */ - public MonetaryFormat optionalDecimals(int... groups) { - List decimalGroups = new ArrayList(groups.length); - for (int group : groups) - decimalGroups.add(group); - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - *

- * Set repeated additional groups of decimals to use after the minimum decimals, if they are useful for expressing - * precision. If the value precision exceeds all decimals specified (including minimum decimals), the value will be - * rounded. This configuration is not relevant for parsing. - *

- * - *

- * For example, if you pass 1,8 it will up to eight decimals to your formatted string if needed. After - * these have been used up, rather than adding further decimals the value will be rounded. - *

- * - * @param decimals - * value of the group to be repeated - * @param repetitions - * number of repetitions - */ - public MonetaryFormat repeatOptionalDecimals(int decimals, int repetitions) { - checkArgument(repetitions >= 0); - List decimalGroups = new ArrayList(repetitions); - for (int i = 0; i < repetitions; i++) - decimalGroups.add(decimals); - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Set number of digits to shift the decimal separator to the right, coming from the standard BTC notation that was - * common pre-2014. Note this will change the currency code if enabled. - */ - public MonetaryFormat shift(int shift) { - if (shift == this.shift) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Set rounding mode to use when it becomes necessary. - */ - public MonetaryFormat roundingMode(RoundingMode roundingMode) { - if (roundingMode == this.roundingMode) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Don't display currency code when formatting. This configuration is not relevant for parsing. - */ - public MonetaryFormat noCode() { - if (codes == null) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, null, codeSeparator, codePrefixed); - } - - /** - * Configure currency code for given decimal separator shift. This configuration is not relevant for parsing. - * - * @param codeShift - * decimal separator shift, see {@link #shift()} - * @param code - * currency code - */ - public MonetaryFormat code(int codeShift, String code) { - checkArgument(codeShift >= 0); - Map codes = new HashMap(); - if (this.codes != null) - codes.putAll(this.codes); - codes.put(codeShift, code); - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Separator between currency code and formatted value. This configuration is not relevant for parsing. - */ - public MonetaryFormat codeSeparator(char codeSeparator) { - checkArgument(!Character.isDigit(codeSeparator)); - checkArgument(codeSeparator > 0); - if (codeSeparator == this.codeSeparator) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - /** - * Prefix formatted output by currency code. This configuration is not relevant for parsing. - */ - public MonetaryFormat prefixCode() { - if (codePrefixed) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, true); - } - - /** - * Postfix formatted output with currency code. This configuration is not relevant for parsing. - */ - public MonetaryFormat postfixCode() { - if (!codePrefixed) - return this; - else - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, false); - } - - /** - * Configure this instance with values from a {@link Locale}. - */ - public MonetaryFormat withLocale(Locale locale) { - DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); - char negativeSign = dfs.getMinusSign(); - char zeroDigit = dfs.getZeroDigit(); - char decimalMark = dfs.getMonetaryDecimalSeparator(); - return new MonetaryFormat(negativeSign, positiveSign, zeroDigit, decimalMark, minDecimals, decimalGroups, - shift, roundingMode, codes, codeSeparator, codePrefixed); - } - - public MonetaryFormat() { - // defaults - this.negativeSign = '-'; - this.positiveSign = 0; // none - this.zeroDigit = '0'; - this.decimalMark = '.'; - this.minDecimals = 2; - this.decimalGroups = null; - this.shift = 0; - this.roundingMode = RoundingMode.HALF_UP; - this.codes = new HashMap(); - this.codes.put(0, CODE_BTC); - this.codes.put(3, CODE_MBTC); - this.codes.put(6, CODE_UBTC); - this.codeSeparator = ' '; - this.codePrefixed = true; - } - - private MonetaryFormat(char negativeSign, char positiveSign, char zeroDigit, char decimalMark, int minDecimals, - List decimalGroups, int shift, RoundingMode roundingMode, Map codes, - char codeSeparator, boolean codePrefixed) { - this.negativeSign = negativeSign; - this.positiveSign = positiveSign; - this.zeroDigit = zeroDigit; - this.decimalMark = decimalMark; - this.minDecimals = minDecimals; - this.decimalGroups = decimalGroups; - this.shift = shift; - this.roundingMode = roundingMode; - this.codes = codes; - this.codeSeparator = codeSeparator; - this.codePrefixed = codePrefixed; - } - - /** - * Format the given monetary value to a human readable form. - */ - public CharSequence format(Monetary monetary) { - // preparation - int maxDecimals = minDecimals; - if (decimalGroups != null) - for (int group : decimalGroups) - maxDecimals += group; - checkState(maxDecimals <= monetary.smallestUnitExponent()); - - // rounding - long satoshis = Math.abs(monetary.getValue()); - long precisionDivisor = checkedPow(10, monetary.smallestUnitExponent() - shift - maxDecimals); - satoshis = checkedMultiply(divide(satoshis, precisionDivisor, roundingMode), precisionDivisor); - - // shifting - long shiftDivisor = checkedPow(10, monetary.smallestUnitExponent() - shift); - long numbers = satoshis / shiftDivisor; - long decimals = satoshis % shiftDivisor; - - // formatting - String decimalsStr = String.format(Locale.US, "%0" + (monetary.smallestUnitExponent() - shift) + "d", decimals); - StringBuilder str = new StringBuilder(decimalsStr); - while (str.length() > minDecimals && str.charAt(str.length() - 1) == '0') - str.setLength(str.length() - 1); // trim trailing zero - int i = minDecimals; - if (decimalGroups != null) { - for (int group : decimalGroups) { - if (str.length() > i && str.length() < i + group) { - while (str.length() < i + group) - str.append('0'); - break; - } - i += group; - } - } - if (str.length() > 0) - str.insert(0, decimalMark); - str.insert(0, numbers); - if (monetary.getValue() < 0) - str.insert(0, negativeSign); - else if (positiveSign != 0) - str.insert(0, positiveSign); - if (codes != null) { - if (codePrefixed) { - str.insert(0, codeSeparator); - str.insert(0, code()); - } else { - str.append(codeSeparator); - str.append(code()); - } - } - - // Convert to non-arabic digits. - if (zeroDigit != '0') { - int offset = zeroDigit - '0'; - for (int d = 0; d < str.length(); d++) { - char c = str.charAt(d); - if (Character.isDigit(c)) - str.setCharAt(d, (char) (c + offset)); - } - } - return str; - } - - /** - * Parse a human readable coin value to a {@link org.altcoinj.core.Coin} instance. - * - * @throws NumberFormatException - * if the string cannot be parsed for some reason - */ - public Coin parse(String str) throws NumberFormatException { - return Coin.valueOf(parseValue(str, Coin.SMALLEST_UNIT_EXPONENT)); - } - - /** - * Parse a human readable fiat value to a {@link org.altcoinj.core.Fiat} instance. - * - * @throws NumberFormatException - * if the string cannot be parsed for some reason - */ - public Fiat parseFiat(String currencyCode, String str) throws NumberFormatException { - return Fiat.valueOf(currencyCode, parseValue(str, Fiat.SMALLEST_UNIT_EXPONENT)); - } - - private long parseValue(String str, int smallestUnitExponent) { - checkState(DECIMALS_PADDING.length() >= smallestUnitExponent); - if (str.isEmpty()) - throw new NumberFormatException("empty string"); - char first = str.charAt(0); - if (first == negativeSign || first == positiveSign) - str = str.substring(1); - String numbers; - String decimals; - int decimalMarkIndex = str.indexOf(decimalMark); - if (decimalMarkIndex != -1) { - numbers = str.substring(0, decimalMarkIndex); - decimals = (str + DECIMALS_PADDING).substring(decimalMarkIndex + 1); - if (decimals.indexOf(decimalMark) != -1) - throw new NumberFormatException("more than one decimal mark"); - } else { - numbers = str; - decimals = DECIMALS_PADDING; - } - String satoshis = numbers + decimals.substring(0, smallestUnitExponent - shift); - for (char c : satoshis.toCharArray()) - if (!Character.isDigit(c)) - throw new NumberFormatException("illegal character: " + c); - long value = Long.parseLong(satoshis); // Non-arabic digits allowed here. - if (first == negativeSign) - value = -value; - return value; - } - - /** - * Get currency code that will be used for current shift. - */ - public String code() { - if (codes == null) - return null; - String code = codes.get(shift); - if (code == null) - throw new NumberFormatException("missing code for shift: " + shift); - return code; - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/AbstractFullPrunedBlockChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/AbstractFullPrunedBlockChainTest.java deleted file mode 100644 index 9b70fd76..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/AbstractFullPrunedBlockChainTest.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * Copyright 2012 Matt Corallo. - * - * 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.dogecoin.dogecoinj.core; - -import com.google.common.collect.Lists; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.script.Script; -import com.dogecoin.dogecoinj.store.BlockStoreException; -import com.dogecoin.dogecoinj.store.FullPrunedBlockStore; -import com.dogecoin.dogecoinj.utils.BlockFileLoader; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.dogecoin.dogecoinj.wallet.WalletTransaction; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.List; - -import static com.dogecoin.dogecoinj.core.Coin.FIFTY_COINS; -import static org.junit.Assert.*; - -/** - * We don't do any wallet tests here, we leave that to {@link ChainSplitTest} - */ - -public abstract class AbstractFullPrunedBlockChainTest -{ - private static final Logger log = LoggerFactory.getLogger(AbstractFullPrunedBlockChainTest.class); - - protected NetworkParameters params; - protected FullPrunedBlockChain chain; - protected FullPrunedBlockStore store; - - @Before - public void setUp() throws Exception { - BriefLogFormatter.init(); - params = new UnitTestParams() { - @Override public int getInterval() { - return 10000; - } - }; - } - - public abstract FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) - throws BlockStoreException; - - public abstract void resetStore(FullPrunedBlockStore store) throws BlockStoreException; - - @Test - public void testGeneratedChain() throws Exception { - // Tests various test cases from FullBlockTestGenerator - FullBlockTestGenerator generator = new FullBlockTestGenerator(params); - RuleList blockList = generator.getBlocksToTest(false, false, null); - - store = createStore(params, blockList.maximumReorgBlockCount); - chain = new FullPrunedBlockChain(params, store); - - for (Rule rule : blockList.list) { - if (!(rule instanceof FullBlockTestGenerator.BlockAndValidity)) - continue; - FullBlockTestGenerator.BlockAndValidity block = (FullBlockTestGenerator.BlockAndValidity) rule; - log.info("Testing rule " + block.ruleName + " with block hash " + block.block.getHash()); - boolean threw = false; - try { - if (chain.add(block.block) != block.connects) { - log.error("Block didn't match connects flag on block " + block.ruleName); - fail(); - } - } catch (VerificationException e) { - threw = true; - if (!block.throwsException) { - log.error("Block didn't match throws flag on block " + block.ruleName); - throw e; - } - if (block.connects) { - log.error("Block didn't match connects flag on block " + block.ruleName); - fail(); - } - } - if (!threw && block.throwsException) { - log.error("Block didn't match throws flag on block " + block.ruleName); - fail(); - } - if (!chain.getChainHead().getHeader().getHash().equals(block.hashChainTipAfterBlock)) { - log.error("New block head didn't match the correct value after block " + block.ruleName); - fail(); - } - if (chain.getChainHead().getHeight() != block.heightAfterBlock) { - log.error("New block head didn't match the correct height after block " + block.ruleName); - fail(); - } - } - try { - store.close(); - } catch (Exception e) {} - } - - @Test - public void skipScripts() throws Exception { - store = createStore(params, 10); - chain = new FullPrunedBlockChain(params, store); - - // Check that we aren't accidentally leaving any references - // to the full StoredUndoableBlock's lying around (ie memory leaks) - - ECKey outKey = new ECKey(); - - // Build some blocks on genesis block to create a spendable output - Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0); - for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { - rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - } - - rollingBlock = rollingBlock.createNextBlock(null); - Transaction t = new Transaction(params); - t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {})); - TransactionInput input = t.addInput(spendableOutput); - // Invalid script. - input.setScriptBytes(new byte[]{}); - rollingBlock.addTransaction(t); - rollingBlock.solve(); - chain.setRunScripts(false); - try { - chain.add(rollingBlock); - } catch (VerificationException e) { - fail(); - } - try { - store.close(); - } catch (Exception e) {} - } - - @Test - public void testFinalizedBlocks() throws Exception { - final int UNDOABLE_BLOCKS_STORED = 10; - store = createStore(params, UNDOABLE_BLOCKS_STORED); - chain = new FullPrunedBlockChain(params, store); - - // Check that we aren't accidentally leaving any references - // to the full StoredUndoableBlock's lying around (ie memory leaks) - - ECKey outKey = new ECKey(); - - // Build some blocks on genesis block to create a spendable output - Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, rollingBlock.getTransactions().get(0).getHash()); - byte[] spendableOutputScriptPubKey = rollingBlock.getTransactions().get(0).getOutputs().get(0).getScriptBytes(); - for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { - rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - } - - WeakReference out = new WeakReference - (store.getTransactionOutput(spendableOutput.getHash(), spendableOutput.getIndex())); - rollingBlock = rollingBlock.createNextBlock(null); - - Transaction t = new Transaction(params); - // Entirely invalid scriptPubKey - t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[]{})); - t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey); - rollingBlock.addTransaction(t); - rollingBlock.solve(); - - chain.add(rollingBlock); - WeakReference undoBlock = new WeakReference(store.getUndoBlock(rollingBlock.getHash())); - - StoredUndoableBlock storedUndoableBlock = undoBlock.get(); - assertNotNull(storedUndoableBlock); - assertNull(storedUndoableBlock.getTransactions()); - WeakReference changes = new WeakReference(storedUndoableBlock.getTxOutChanges()); - assertNotNull(changes.get()); - storedUndoableBlock = null; // Blank the reference so it can be GCd. - - // Create a chain longer than UNDOABLE_BLOCKS_STORED - for (int i = 0; i < UNDOABLE_BLOCKS_STORED; i++) { - rollingBlock = rollingBlock.createNextBlock(null); - chain.add(rollingBlock); - } - // Try to get the garbage collector to run - System.gc(); - assertNull(undoBlock.get()); - assertNull(changes.get()); - assertNull(out.get()); - try { - store.close(); - } catch (Exception e) {} - } - - @Test - public void testFirst100KBlocks() throws Exception { - NetworkParameters params = MainNetParams.get(); - File blockFile = new File(getClass().getResource("first-100k-blocks.dat").getFile()); - BlockFileLoader loader = new BlockFileLoader(params, Arrays.asList(blockFile)); - - store = createStore(params, 10); - resetStore(store); - chain = new FullPrunedBlockChain(params, store); - for (Block block : loader) - chain.add(block); - try { - store.close(); - } catch (Exception e) {} - } - - @Test - public void testGetOpenTransactionOutputs() throws Exception { - final int UNDOABLE_BLOCKS_STORED = 10; - store = createStore(params, UNDOABLE_BLOCKS_STORED); - chain = new FullPrunedBlockChain(params, store); - - // Check that we aren't accidentally leaving any references - // to the full StoredUndoableBlock's lying around (ie memory leaks) - ECKey outKey = new ECKey(); - - // Build some blocks on genesis block to create a spendable output - Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - Transaction transaction = rollingBlock.getTransactions().get(0); - TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash()); - byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes(); - for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { - rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - } - rollingBlock = rollingBlock.createNextBlock(null); - - // Create bitcoin spend of 1 BTC. - ECKey toKey = new ECKey(); - Coin amount = Coin.valueOf(100000000); - Address address = new Address(params, toKey.getPubKeyHash()); - Coin totalAmount = Coin.ZERO; - - Transaction t = new Transaction(params); - t.addOutput(new TransactionOutput(params, t, amount, toKey)); - t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey); - rollingBlock.addTransaction(t); - rollingBlock.solve(); - chain.add(rollingBlock); - totalAmount = totalAmount.add(amount); - - List outputs = store.getOpenTransactionOutputs(Lists.newArrayList(address)); - assertNotNull(outputs); - assertEquals("Wrong Number of Outputs", 1, outputs.size()); - UTXO output = outputs.get(0); - assertEquals("The address is not equal", address.toString(), output.getAddress()); - assertEquals("The amount is not equal", totalAmount, output.getValue()); - - outputs = null; - output = null; - try { - store.close(); - } catch (Exception e) {} - } - - @Test - public void testUTXOProviderWithWallet() throws Exception { - final int UNDOABLE_BLOCKS_STORED = 10; - store = createStore(params, UNDOABLE_BLOCKS_STORED); - chain = new FullPrunedBlockChain(params, store); - - // Check that we aren't accidentally leaving any references - // to the full StoredUndoableBlock's lying around (ie memory leaks) - ECKey outKey = new ECKey(); - - // Build some blocks on genesis block to create a spendable output. - Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - Transaction transaction = rollingBlock.getTransactions().get(0); - TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash()); - byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes(); - for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { - rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey()); - chain.add(rollingBlock); - } - rollingBlock = rollingBlock.createNextBlock(null); - - // Create 1 BTC spend to a key in this wallet (to ourselves). - Wallet wallet = new Wallet(params); - assertEquals("Available balance is incorrect", Coin.ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE)); - assertEquals("Estimated balance is incorrect", Coin.ZERO, wallet.getBalance(Wallet.BalanceType.ESTIMATED)); - - wallet.setUTXOProvider(store); - ECKey toKey = wallet.freshReceiveKey(); - Coin amount = Coin.valueOf(100000000); - - Transaction t = new Transaction(params); - t.addOutput(new TransactionOutput(params, t, amount, toKey)); - t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey); - rollingBlock.addTransaction(t); - rollingBlock.solve(); - chain.add(rollingBlock); - - // Create another spend of 1/2 the value of BTC we have available using the wallet (store coin selector). - ECKey toKey2 = new ECKey(); - Coin amount2 = amount.divide(2); - Address address2 = new Address(params, toKey2.getPubKeyHash()); - Wallet.SendRequest req = Wallet.SendRequest.to(address2, amount2); - wallet.completeTx(req); - wallet.commitTx(req.tx); - Coin fee = req.fee; - - // There should be one pending tx (our spend). - assertEquals("Wrong number of PENDING.4", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); - Coin totalPendingTxAmount = Coin.ZERO; - for (Transaction tx : wallet.getPendingTransactions()) { - totalPendingTxAmount = totalPendingTxAmount.add(tx.getValueSentToMe(wallet)); - } - - // The availbale balance should be the 0 (as we spent the 1 BTC that's pending) and estimated should be 1/2 - fee BTC - assertEquals("Available balance is incorrect", Coin.ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE)); - assertEquals("Estimated balance is incorrect", amount2.subtract(fee), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); - assertEquals("Pending tx amount is incorrect", amount2.subtract(fee), totalPendingTxAmount); - try { - store.close(); - } catch (Exception e) {} - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/AlertMessageTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/AlertMessageTest.java deleted file mode 100644 index 000d8263..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/AlertMessageTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.UnitTestParams; -import org.junit.Before; -import org.junit.Test; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class AlertMessageTest { - private static final byte[] TEST_KEY_PRIV = HEX.decode("6421e091445ade4b24658e96aa60959ce800d8ea9e7bd8613335aa65ba8d840b"); - private NetworkParameters params; - - @Before - public void setUp() throws Exception { - final ECKey key = ECKey.fromPrivate(TEST_KEY_PRIV); - params = new UnitTestParams() { - @Override - public byte[] getAlertSigningKey() { - return key.getPubKey(); - } - }; - } - - @Test - public void deserialize() throws Exception { - // A CAlert taken from the reference implementation. - // TODO: This does not check the subVer or set fields. Support proper version matching. - final byte[] payload = HEX.decode("5c010000004544eb4e000000004192ec4e00000000eb030000e9030000000000000048ee00000088130000002f43416c6572742073797374656d20746573743a2020202020202020207665722e302e352e3120617661696c61626c6500473045022100ec799908c008b272d5e5cd5a824abaaac53d210cc1fa517d8e22a701ecdb9e7002206fa1e7e7c251d5ba0d7c1fe428fc1870662f2927531d1cad8d4581b45bc4f8a7"); - AlertMessage alert = new AlertMessage(params, payload); - assertEquals(1324041285, alert.getRelayUntil().getTime() / 1000); - assertEquals(1324126785, alert.getExpiration().getTime() / 1000); - assertEquals(1003, alert.getId()); - assertEquals(1001, alert.getCancel()); - assertEquals(0, alert.getMinVer()); - assertEquals(61000, alert.getMaxVer()); - assertEquals(5000, alert.getPriority()); - assertEquals("CAlert system test: ver.0.5.1 available", alert.getStatusBar()); - assertTrue(alert.isSignatureValid()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/Base58Test.java b/core/src/test/java/com/dogecoin/dogecoinj/core/Base58Test.java deleted file mode 100644 index aa752479..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/Base58Test.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * 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.dogecoin.dogecoinj.core; - -import junit.framework.TestCase; -import org.junit.Test; - -import java.math.BigInteger; -import java.util.Arrays; - -public class Base58Test extends TestCase { - @Test - public void testEncode() throws Exception { - byte[] testbytes = "Hello World".getBytes(); - assertEquals("JxF12TrwUP45BMd", Base58.encode(testbytes)); - - BigInteger bi = BigInteger.valueOf(3471844090L); - assertEquals("16Ho7Hs", Base58.encode(bi.toByteArray())); - - byte[] zeroBytes1 = new byte[1]; - assertEquals("1", Base58.encode(zeroBytes1)); - - byte[] zeroBytes7 = new byte[7]; - assertEquals("1111111", Base58.encode(zeroBytes7)); - - // test empty encode - assertEquals("", Base58.encode(new byte[0])); - } - - @Test - public void testDecode() throws Exception { - byte[] testbytes = "Hello World".getBytes(); - byte[] actualbytes = Base58.decode("JxF12TrwUP45BMd"); - assertTrue(new String(actualbytes), Arrays.equals(testbytes, actualbytes)); - - assertTrue("1", Arrays.equals(Base58.decode("1"), new byte[1])); - assertTrue("1111", Arrays.equals(Base58.decode("1111"), new byte[4])); - - try { - Base58.decode("This isn't valid base58"); - fail(); - } catch (AddressFormatException e) { - // expected - } - - Base58.decodeChecked("4stwEBjT6FYyVV"); - - // Checksum should fail. - try { - Base58.decodeChecked("4stwEBjT6FYyVW"); - fail(); - } catch (AddressFormatException e) { - // expected - } - - // Input is too short. - try { - Base58.decodeChecked("4s"); - fail(); - } catch (AddressFormatException e) { - // expected - } - - // Test decode of empty String. - assertEquals(0, Base58.decode("").length); - - // Now check we can correctly decode the case where the high bit of the first byte is not zero, so BigInteger - // sign extends. Fix for a bug that stopped us parsing keys exported using sipas patch. - Base58.decodeChecked("93VYUMzRG9DdbRP72uQXjaWibbQwygnvaCu9DumcqDjGybD864T"); - } - - @Test - public void testDecodeToBigInteger() throws AddressFormatException { - byte[] input = Base58.decode("129"); - assertEquals(new BigInteger(1, input), Base58.decodeToBigInteger("129")); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/BitcoinSerializerTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/BitcoinSerializerTest.java deleted file mode 100644 index b8c5e2e0..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/BitcoinSerializerTest.java +++ /dev/null @@ -1,288 +0,0 @@ -/** - * Copyright 2011 Noa Resare - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.MainNetParams; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; -import java.net.InetAddress; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.Arrays; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.*; - -public class BitcoinSerializerTest { - private final byte[] addrMessage = HEX.decode("f9beb4d96164647200000000000000001f000000" + - "ed52399b01e215104d010000000000000000000000000000000000ffff0a000001208d"); - - private final byte[] txMessage = HEX.withSeparator(" ", 2).decode( - "f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" + - "02 01 00 00 e2 93 cd be 01 00 00 00 01 6d bd db" + - "08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + - "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + - "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + - "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a" + - "59 23 3f 45 bc 57 83 80 02 20 59 af 01 ca 17 d0" + - "0e 41 83 7a 1d 58 e9 7a a3 1b ae 58 4e de c2 8d" + - "35 bd 96 92 36 90 91 3b ae 9a 01 41 04 9c 02 bf" + - "c9 7e f2 36 ce 6d 8f e5 d9 40 13 c7 21 e9 15 98" + - "2a cd 2b 12 b6 5d 9b 7d 59 e2 0a 84 20 05 f8 fc" + - "4e 02 53 2e 87 3d 37 b9 6f 09 d6 d4 51 1a da 8f" + - "14 04 2f 46 61 4a 4c 70 c0 f1 4b ef f5 ff ff ff" + - "ff 02 40 4b 4c 00 00 00 00 00 19 76 a9 14 1a a0" + - "cd 1c be a6 e7 45 8a 7a ba d5 12 a9 d9 ea 1a fb" + - "22 5e 88 ac 80 fa e9 c7 00 00 00 00 19 76 a9 14" + - "0e ab 5b ea 43 6a 04 84 cf ab 12 48 5e fd a0 b7" + - "8b 4e cc 52 88 ac 00 00 00 00"); - - @Test - public void testAddr() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); - // the actual data from https://en.bitcoin.it/wiki/Protocol_specification#addr - AddressMessage a = (AddressMessage)bs.deserialize(ByteBuffer.wrap(addrMessage)); - assertEquals(1, a.getAddresses().size()); - PeerAddress pa = a.getAddresses().get(0); - assertEquals(8333, pa.getPort()); - assertEquals("10.0.0.1", pa.getAddr().getHostAddress()); - ByteArrayOutputStream bos = new ByteArrayOutputStream(addrMessage.length); - bs.serialize(a, bos); - - assertEquals(31, a.getMessageSize()); - a.addAddress(new PeerAddress(InetAddress.getLocalHost())); - assertEquals(61, a.getMessageSize()); - a.removeAddress(0); - assertEquals(31, a.getMessageSize()); - - //this wont be true due to dynamic timestamps. - //assertTrue(LazyParseByteCacheTest.arrayContains(bos.toByteArray(), addrMessage)); - } - - @Test - public void testLazyParsing() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get(), true, false); - - Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); - assertNotNull(tx); - assertEquals(false, tx.isParsed()); - assertEquals(true, tx.isCached()); - tx.getInputs(); - assertEquals(true, tx.isParsed()); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bs.serialize(tx, bos); - assertEquals(true, Arrays.equals(txMessage, bos.toByteArray())); - } - - @Test - public void testCachedParsing() throws Exception { - testCachedParsing(true); - testCachedParsing(false); - } - - private void testCachedParsing(boolean lazy) throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get(), lazy, true); - - //first try writing to a fields to ensure uncaching and children are not affected - Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); - assertNotNull(tx); - assertEquals(!lazy, tx.isParsed()); - assertEquals(true, tx.isCached()); - - tx.setLockTime(1); - //parent should have been uncached - assertEquals(false, tx.isCached()); - //child should remain cached. - assertEquals(true, tx.getInputs().get(0).isCached()); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bs.serialize(tx, bos); - assertEquals(true, !Arrays.equals(txMessage, bos.toByteArray())); - - //now try writing to a child to ensure uncaching is propagated up to parent but not to siblings - tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); - assertNotNull(tx); - assertEquals(!lazy, tx.isParsed()); - assertEquals(true, tx.isCached()); - - tx.getInputs().get(0).setSequenceNumber(1); - //parent should have been uncached - assertEquals(false, tx.isCached()); - //so should child - assertEquals(false, tx.getInputs().get(0).isCached()); - - bos = new ByteArrayOutputStream(); - bs.serialize(tx, bos); - assertEquals(true, !Arrays.equals(txMessage, bos.toByteArray())); - - //deserialize/reserialize to check for equals. - tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); - assertNotNull(tx); - assertEquals(!lazy, tx.isParsed()); - assertEquals(true, tx.isCached()); - bos = new ByteArrayOutputStream(); - bs.serialize(tx, bos); - assertEquals(true, Arrays.equals(txMessage, bos.toByteArray())); - - //deserialize/reserialize to check for equals. Set a field to it's existing value to trigger uncache - tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage)); - assertNotNull(tx); - assertEquals(!lazy, tx.isParsed()); - assertEquals(true, tx.isCached()); - - tx.getInputs().get(0).setSequenceNumber(tx.getInputs().get(0).getSequenceNumber()); - - bos = new ByteArrayOutputStream(); - bs.serialize(tx, bos); - assertEquals(true, Arrays.equals(txMessage, bos.toByteArray())); - - } - - - /** - * Get 1 header of the block number 1 (the first one is 0) in the chain - */ - @Test - public void testHeaders1() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); - - HeadersMessage hm = (HeadersMessage) bs.deserialize(ByteBuffer.wrap(HEX.decode("f9beb4d9686561" + - "646572730000000000520000005d4fab8101010000006fe28c0ab6f1b372c1a6a246ae6" + - "3f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677b" + - "a1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e3629900"))); - - // The first block after the genesis - // http://blockexplorer.com/b/1 - Block block = hm.getBlockHeaders().get(0); - String hash = block.getHashAsString(); - assertEquals(hash, "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"); - - assertNull(block.transactions); - - assertEquals(Utils.HEX.encode(block.getMerkleRoot().getBytes()), - "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"); - } - - - @Test - /** - * Get 6 headers of blocks 1-6 in the chain - */ - public void testHeaders2() throws Exception { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); - - HeadersMessage hm = (HeadersMessage) bs.deserialize(ByteBuffer.wrap(HEX.decode("f9beb4d96865616465" + - "72730000000000e701000085acd4ea06010000006fe28c0ab6f1b372c1a6a246ae63f74f931e" + - "8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1c" + - "db606e857233e0e61bc6649ffff001d01e3629900010000004860eb18bf1b1620e37e9490fc8a" + - "427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36" + - "ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610001000000bddd99ccfda39da1b108ce1" + - "a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387" + - "af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d00010000004944469562ae1c2c74" + - "d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec" + - "5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a9000100000085144a84488e" + - "a88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023" + - "370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e4770001000000fc33f5" + - "96f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4" + - "ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c9700"))); - - int nBlocks = hm.getBlockHeaders().size(); - assertEquals(nBlocks, 6); - - // index 0 block is the number 1 block in the block chain - // http://blockexplorer.com/b/1 - Block zeroBlock = hm.getBlockHeaders().get(0); - String zeroBlockHash = zeroBlock.getHashAsString(); - - assertEquals("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048", - zeroBlockHash); - assertEquals(zeroBlock.getNonce(), 2573394689L); - - - Block thirdBlock = hm.getBlockHeaders().get(3); - String thirdBlockHash = thirdBlock.getHashAsString(); - - // index 3 block is the number 4 block in the block chain - // http://blockexplorer.com/b/4 - assertEquals("000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485", - thirdBlockHash); - assertEquals(thirdBlock.getNonce(), 2850094635L); - } - - @Test - public void testBitcoinPacketHeader() { - try { - new BitcoinSerializer.BitcoinPacketHeader(ByteBuffer.wrap(new byte[]{0})); - fail(); - } catch (BufferUnderflowException e) { - } - - // Message with a Message size which is 1 too big, in little endian format. - byte[] wrongMessageLength = HEX.decode("000000000000000000000000010000020000000000"); - try { - new BitcoinSerializer.BitcoinPacketHeader(ByteBuffer.wrap(wrongMessageLength)); - fail(); - } catch (ProtocolException e) { - // expected - } - } - - @Test - public void testSeekPastMagicBytes() { - // Fail in another way, there is data in the stream but no magic bytes. - byte[] brokenMessage = HEX.decode("000000"); - try { - new BitcoinSerializer(MainNetParams.get()).seekPastMagicBytes(ByteBuffer.wrap(brokenMessage)); - fail(); - } catch (BufferUnderflowException e) { - // expected - } - } - - @Test - /** - * Tests serialization of an unknown message. - */ - public void testSerializeUnknownMessage() { - BitcoinSerializer bs = new BitcoinSerializer(MainNetParams.get()); - - UnknownMessage a = new UnknownMessage(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(addrMessage.length); - try { - bs.serialize(a, bos); - fail(); - } catch (Throwable e) { - } - } - - /** - * Unknown message for testSerializeUnknownMessage. - */ - class UnknownMessage extends Message { - @Override - void parse() throws ProtocolException { - } - - @Override - protected void parseLite() throws ProtocolException { - } - } - -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/BitcoindComparisonTool.java b/core/src/test/java/com/dogecoin/dogecoinj/core/BitcoindComparisonTool.java deleted file mode 100644 index 5c65ffd4..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/BitcoindComparisonTool.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2012 Matt Corallo. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.google.common.base.Preconditions; -import com.google.common.collect.Iterables; -import com.google.common.util.concurrent.SettableFuture; -import com.google.common.util.concurrent.Uninterruptibles; -import com.dogecoin.dogecoinj.net.NioClient; -import com.dogecoin.dogecoinj.params.RegTestParams; -import com.dogecoin.dogecoinj.store.BlockStoreException; -import com.dogecoin.dogecoinj.store.FullPrunedBlockStore; -import com.dogecoin.dogecoinj.store.H2FullPrunedBlockStore; -import com.dogecoin.dogecoinj.store.MemoryBlockStore; -import com.dogecoin.dogecoinj.utils.BlockFileLoader; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.dogecoin.dogecoinj.utils.Threading; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A tool for comparing the blocks which are accepted/rejected by bitcoind/dogecoinj - * It is designed to run as a testnet-in-a-box network between a single bitcoind node and dogecoinj - * It is not an automated unit-test because it requires a bit more set-up...read comments below - */ -public class BitcoindComparisonTool { - private static final Logger log = LoggerFactory.getLogger(BitcoindComparisonTool.class); - - private static NetworkParameters params; - private static FullPrunedBlockStore store; - private static FullPrunedBlockChain chain; - private static Sha256Hash bitcoindChainHead; - private static volatile InventoryMessage mostRecentInv = null; - - static class BlockWrapper { - public Block block; - } - - public static void main(String[] args) throws Exception { - BriefLogFormatter.init(); - System.out.println("USAGE: bitcoinjBlockStoreLocation runExpensiveTests(1/0) [port=18444]"); - boolean runExpensiveTests = args.length > 1 && Integer.parseInt(args[1]) == 1; - - params = RegTestParams.get(); - - File blockFile = File.createTempFile("testBlocks", ".dat"); - blockFile.deleteOnExit(); - - FullBlockTestGenerator generator = new FullBlockTestGenerator(params); - final RuleList blockList = generator.getBlocksToTest(false, runExpensiveTests, blockFile); - final Map preloadedBlocks = new HashMap(); - final Iterator blocks = new BlockFileLoader(params, Arrays.asList(blockFile)); - - try { - store = new H2FullPrunedBlockStore(params, args.length > 0 ? args[0] : "BitcoindComparisonTool", blockList.maximumReorgBlockCount); - ((H2FullPrunedBlockStore)store).resetStore(); - //store = new MemoryFullPrunedBlockStore(params, blockList.maximumReorgBlockCount); - chain = new FullPrunedBlockChain(params, store); - } catch (BlockStoreException e) { - e.printStackTrace(); - System.exit(1); - } - - VersionMessage ver = new VersionMessage(params, 42); - ver.appendToSubVer("BlockAcceptanceComparisonTool", "1.1", null); - ver.localServices = VersionMessage.NODE_NETWORK; - final Peer bitcoind = new Peer(params, ver, new BlockChain(params, new MemoryBlockStore(params)), new PeerAddress(InetAddress.getLocalHost())); - Preconditions.checkState(bitcoind.getVersionMessage().hasBlockChain()); - - final BlockWrapper currentBlock = new BlockWrapper(); - - final Set blocksRequested = Collections.synchronizedSet(new HashSet()); - final Set blocksPendingSend = Collections.synchronizedSet(new HashSet()); - final AtomicInteger unexpectedInvs = new AtomicInteger(0); - final SettableFuture connectedFuture = SettableFuture.create(); - bitcoind.addEventListener(new AbstractPeerEventListener() { - @Override - public void onPeerConnected(Peer peer, int peerCount) { - if (!peer.getPeerVersionMessage().subVer.contains("Satoshi")) { - System.out.println(); - System.out.println("************************************************************************************************************************\n" + - "WARNING: You appear to be using this to test an alternative implementation with full validation rules. You should go\n" + - "think hard about what you're doing. Seriously, no one has gotten even close to correctly reimplementing Bitcoin\n" + - "consensus rules, despite serious investment in trying. It is a huge task and the slightest difference is a huge bug.\n" + - "Instead, go work on making Bitcoin Core consensus rules a shared library and use that. Seriously, you wont get it right,\n" + - "and starting with this tester as a way to try to do so will simply end in pain and lost coins.\n" + - "************************************************************************************************************************"); - System.out.println(); - System.out.println("Giving you 30 seconds to think about the above warning..."); - Uninterruptibles.sleepUninterruptibly(30, TimeUnit.SECONDS); - } - log.info("bitcoind connected"); - // Make sure bitcoind has no blocks - bitcoind.setDownloadParameters(0, false); - bitcoind.startBlockChainDownload(); - connectedFuture.set(null); - } - - @Override - public void onPeerDisconnected(Peer peer, int peerCount) { - log.error("bitcoind node disconnected!"); - System.exit(1); - } - - @Override - public Message onPreMessageReceived(Peer peer, Message m) { - if (m instanceof HeadersMessage) { - if (!((HeadersMessage) m).getBlockHeaders().isEmpty()) { - Block b = Iterables.getLast(((HeadersMessage) m).getBlockHeaders()); - log.info("Got header from bitcoind " + b.getHashAsString()); - bitcoindChainHead = b.getHash(); - } else - log.info("Got empty header message from bitcoind"); - return null; - } else if (m instanceof Block) { - log.error("bitcoind sent us a block it already had, make sure bitcoind has no blocks!"); - System.exit(1); - } else if (m instanceof GetDataMessage) { - for (InventoryItem item : ((GetDataMessage) m).items) - if (item.type == InventoryItem.Type.Block) { - log.info("Requested " + item.hash); - if (currentBlock.block.getHash().equals(item.hash)) - bitcoind.sendMessage(currentBlock.block); - else { - Block nextBlock = preloadedBlocks.get(item.hash); - if (nextBlock != null) - bitcoind.sendMessage(nextBlock); - else { - blocksPendingSend.add(item.hash); - log.info("...which we will not provide yet"); - } - } - blocksRequested.add(item.hash); - } - return null; - } else if (m instanceof GetHeadersMessage) { - try { - if (currentBlock.block == null) { - log.info("Got a request for a header before we had even begun processing blocks!"); - return null; - } - LinkedList headers = new LinkedList(); - Block it = blockList.hashHeaderMap.get(currentBlock.block.getHash()); - while (it != null) { - headers.addFirst(it); - it = blockList.hashHeaderMap.get(it.getPrevBlockHash()); - } - LinkedList sendHeaders = new LinkedList(); - boolean found = false; - for (Sha256Hash hash : ((GetHeadersMessage) m).getLocator()) { - for (Block b : headers) { - if (found) { - sendHeaders.addLast(b); - log.info("Sending header (" + b.getPrevBlockHash() + ") -> " + b.getHash()); - if (b.getHash().equals(((GetHeadersMessage) m).getStopHash())) - break; - } else if (b.getHash().equals(hash)) { - log.info("Found header " + b.getHashAsString()); - found = true; - } - } - if (found) - break; - } - if (!found) - sendHeaders = headers; - bitcoind.sendMessage(new HeadersMessage(params, sendHeaders)); - InventoryMessage i = new InventoryMessage(params); - for (Block b : sendHeaders) - i.addBlock(b); - bitcoind.sendMessage(i); - } catch (Exception e) { - throw new RuntimeException(e); - } - return null; - } else if (m instanceof InventoryMessage) { - if (mostRecentInv != null) { - log.error("Got an inv when we weren't expecting one"); - unexpectedInvs.incrementAndGet(); - } - mostRecentInv = (InventoryMessage) m; - } - return m; - } - }, Threading.SAME_THREAD); - - - bitcoindChainHead = params.getGenesisBlock().getHash(); - - // bitcoind MUST be on localhost or we will get banned as a DoSer - new NioClient(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), args.length > 2 ? Integer.parseInt(args[2]) : params.getPort()), bitcoind, 1000); - - connectedFuture.get(); - - ArrayList locator = new ArrayList(1); - locator.add(params.getGenesisBlock().getHash()); - Sha256Hash hashTo = new Sha256Hash("0000000000000000000000000000000000000000000000000000000000000000"); - - int rulesSinceFirstFail = 0; - for (Rule rule : blockList.list) { - if (rule instanceof FullBlockTestGenerator.BlockAndValidity) { - FullBlockTestGenerator.BlockAndValidity block = (FullBlockTestGenerator.BlockAndValidity) rule; - boolean threw = false; - Block nextBlock = preloadedBlocks.get(((FullBlockTestGenerator.BlockAndValidity) rule).blockHash); - // Often load at least one block because sometimes we have duplicates with the same hash (b56/57) - for (int i = 0; i < 1 - || nextBlock == null || !nextBlock.getHash().equals(block.blockHash); - i++) { - try { - Block b = blocks.next(); - Block oldBlockWithSameHash = preloadedBlocks.put(b.getHash(), b); - if (oldBlockWithSameHash != null && oldBlockWithSameHash.getTransactions().size() != b.getTransactions().size()) - blocksRequested.remove(b.getHash()); - nextBlock = preloadedBlocks.get(block.blockHash); - } catch (NoSuchElementException e) { - if (nextBlock == null || !nextBlock.getHash().equals(block.blockHash)) - throw e; - } - } - currentBlock.block = nextBlock; - log.info("Testing block {} {}", block.ruleName, currentBlock.block.getHash()); - try { - if (chain.add(nextBlock) != block.connects) { - log.error("ERROR: Block didn't match connects flag on block \"" + block.ruleName + "\""); - rulesSinceFirstFail++; - } - } catch (VerificationException e) { - threw = true; - if (!block.throwsException) { - log.error("ERROR: Block didn't match throws flag on block \"" + block.ruleName + "\""); - e.printStackTrace(); - rulesSinceFirstFail++; - } else if (block.connects) { - log.error("ERROR: Block didn't match connects flag on block \"" + block.ruleName + "\""); - e.printStackTrace(); - rulesSinceFirstFail++; - } - } - if (!threw && block.throwsException) { - log.error("ERROR: Block didn't match throws flag on block \"" + block.ruleName + "\""); - rulesSinceFirstFail++; - } else if (!chain.getChainHead().getHeader().getHash().equals(block.hashChainTipAfterBlock)) { - log.error("ERROR: New block head didn't match the correct value after block \"" + block.ruleName + "\""); - rulesSinceFirstFail++; - } else if (chain.getChainHead().getHeight() != block.heightAfterBlock) { - log.error("ERROR: New block head didn't match the correct height after block " + block.ruleName); - rulesSinceFirstFail++; - } - - // Shouldnt double-request - boolean shouldntRequest = blocksRequested.contains(nextBlock.getHash()); - if (shouldntRequest) - blocksRequested.remove(nextBlock.getHash()); - InventoryMessage message = new InventoryMessage(params); - message.addBlock(nextBlock); - bitcoind.sendMessage(message); - log.info("Sent inv with block " + nextBlock.getHashAsString()); - if (blocksPendingSend.contains(nextBlock.getHash())) { - bitcoind.sendMessage(nextBlock); - log.info("Sent full block " + nextBlock.getHashAsString()); - } - // bitcoind doesn't request blocks inline so we can't rely on a ping for synchronization - for (int i = 0; !shouldntRequest && !blocksRequested.contains(nextBlock.getHash()); i++) { - int SLEEP_TIME = 1; - if (i % 1000/SLEEP_TIME == 1000/SLEEP_TIME - 1) - log.error("bitcoind still hasn't requested block " + block.ruleName + " with hash " + nextBlock.getHash()); - Thread.sleep(SLEEP_TIME); - if (i > 60000/SLEEP_TIME) { - log.error("bitcoind failed to request block " + block.ruleName); - System.exit(1); - } - } - if (shouldntRequest) { - Thread.sleep(100); - if (blocksRequested.contains(nextBlock.getHash())) { - log.error("ERROR: bitcoind re-requested block " + block.ruleName + " with hash " + nextBlock.getHash()); - rulesSinceFirstFail++; - } - } - // If the block throws, we may want to get bitcoind to request the same block again - if (block.throwsException) - blocksRequested.remove(nextBlock.getHash()); - //bitcoind.sendMessage(nextBlock); - locator.clear(); - locator.add(bitcoindChainHead); - bitcoind.sendMessage(new GetHeadersMessage(params, locator, hashTo)); - bitcoind.ping().get(); - if (!chain.getChainHead().getHeader().getHash().equals(bitcoindChainHead)) { - rulesSinceFirstFail++; - log.error("ERROR: bitcoind and bitcoinj acceptance differs on block \"" + block.ruleName + "\""); - } - if (block.sendOnce) - preloadedBlocks.remove(nextBlock.getHash()); - log.info("Block \"" + block.ruleName + "\" completed processing"); - } else if (rule instanceof MemoryPoolState) { - MemoryPoolMessage message = new MemoryPoolMessage(); - bitcoind.sendMessage(message); - bitcoind.ping().get(); - if (mostRecentInv == null && !((MemoryPoolState) rule).mempool.isEmpty()) { - log.error("ERROR: bitcoind had an empty mempool, but we expected some transactions on rule " + rule.ruleName); - rulesSinceFirstFail++; - } else if (mostRecentInv != null && ((MemoryPoolState) rule).mempool.isEmpty()) { - log.error("ERROR: bitcoind had a non-empty mempool, but we expected an empty one on rule " + rule.ruleName); - rulesSinceFirstFail++; - } else if (mostRecentInv != null) { - Set originalRuleSet = new HashSet(((MemoryPoolState)rule).mempool); - boolean matches = mostRecentInv.items.size() == ((MemoryPoolState)rule).mempool.size(); - for (InventoryItem item : mostRecentInv.items) - if (!((MemoryPoolState) rule).mempool.remove(item)) - matches = false; - if (matches) - continue; - log.error("bitcoind's mempool didn't match what we were expecting on rule " + rule.ruleName); - log.info(" bitcoind's mempool was: "); - for (InventoryItem item : mostRecentInv.items) - log.info(" " + item.hash); - log.info(" The expected mempool was: "); - for (InventoryItem item : originalRuleSet) - log.info(" " + item.hash); - rulesSinceFirstFail++; - } - mostRecentInv = null; - } else if (rule instanceof UTXORule) { - if (bitcoind.getPeerVersionMessage().isGetUTXOsSupported()) { - UTXORule r = (UTXORule) rule; - UTXOsMessage result = bitcoind.getUTXOs(r.query).get(); - if (!result.equals(r.result)) { - log.error("utxo result was not what we expected."); - log.error("Wanted {}", r.result); - log.error("but got {}", result); - rulesSinceFirstFail++; - } else { - log.info("Successful utxo query {}: {}", r.ruleName, result); - } - } - } else { - throw new RuntimeException("Unknown rule"); - } - if (rulesSinceFirstFail > 0) - rulesSinceFirstFail++; - if (rulesSinceFirstFail > 6) - System.exit(1); - } - - if (unexpectedInvs.get() > 0) - log.error("ERROR: Got " + unexpectedInvs.get() + " unexpected invs from bitcoind"); - log.info("Done testing."); - System.exit(rulesSinceFirstFail > 0 || unexpectedInvs.get() > 0 ? 1 : 0); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/BlockChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/BlockChainTest.java deleted file mode 100644 index 028579e0..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/BlockChainTest.java +++ /dev/null @@ -1,458 +0,0 @@ -/** - * Copyright 2011 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.core.Wallet.BalanceType; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet2Params; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.store.BlockStore; -import com.dogecoin.dogecoinj.store.MemoryBlockStore; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.google.common.util.concurrent.ListenableFuture; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.math.BigInteger; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeBlock; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeTx; -import static org.junit.Assert.*; - -// Handling of chain splits/reorgs are in ChainSplitTests. - -public class BlockChainTest { - private BlockChain testNetChain; - - private Wallet wallet; - private BlockChain chain; - private BlockStore blockStore; - private Address coinbaseTo; - private NetworkParameters unitTestParams; - private final StoredBlock[] block = new StoredBlock[1]; - private Transaction coinbaseTransaction; - - private static class TweakableTestNet2Params extends TestNet2Params { - public void setMaxTarget(BigInteger limit) { - maxTarget = limit; - } - } - private static final TweakableTestNet2Params testNet = new TweakableTestNet2Params(); - - private void resetBlockStore() { - blockStore = new MemoryBlockStore(unitTestParams); - } - - @Before - public void setUp() throws Exception { - BriefLogFormatter.initVerbose(); - testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet)); - Wallet.SendRequest.DEFAULT_FEE_PER_KB = Coin.ZERO; - - unitTestParams = UnitTestParams.get(); - wallet = new Wallet(unitTestParams) { - @Override - public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, - int relativityOffset) throws VerificationException { - super.receiveFromBlock(tx, block, blockType, relativityOffset); - BlockChainTest.this.block[0] = block; - if (tx.isCoinBase()) { - BlockChainTest.this.coinbaseTransaction = tx; - } - } - }; - wallet.freshReceiveKey(); - - resetBlockStore(); - chain = new BlockChain(unitTestParams, wallet, blockStore); - - coinbaseTo = wallet.currentReceiveKey().toAddress(unitTestParams); - } - - @After - public void tearDown() { - Wallet.SendRequest.DEFAULT_FEE_PER_KB = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; - } - - @Test - public void testBasicChaining() throws Exception { - // Check that we can plug a few blocks together and the futures work. - ListenableFuture future = testNetChain.getHeightFuture(2); - // Block 1 from the testnet. - Block b1 = getBlock1(); - assertTrue(testNetChain.add(b1)); - assertFalse(future.isDone()); - // Block 2 from the testnet. - Block b2 = getBlock2(); - - // Let's try adding an invalid block. - long n = b2.getNonce(); - try { - b2.setNonce(12345); - testNetChain.add(b2); - fail(); - } catch (VerificationException e) { - b2.setNonce(n); - } - - // Now it works because we reset the nonce. - assertTrue(testNetChain.add(b2)); - assertTrue(future.isDone()); - assertEquals(2, future.get().getHeight()); - } - - @Test - public void receiveCoins() throws Exception { - // Quick check that we can actually receive coins. - Transaction tx1 = createFakeTx(unitTestParams, - COIN, - wallet.currentReceiveKey().toAddress(unitTestParams)); - Block b1 = createFakeBlock(blockStore, tx1).block; - chain.add(b1); - assertTrue(wallet.getBalance().signum() > 0); - } - - @Test - public void merkleRoots() throws Exception { - // Test that merkle root verification takes place when a relevant transaction is present and doesn't when - // there isn't any such tx present (as an optimization). - Transaction tx1 = createFakeTx(unitTestParams, - COIN, - wallet.currentReceiveKey().toAddress(unitTestParams)); - Block b1 = createFakeBlock(blockStore, tx1).block; - chain.add(b1); - resetBlockStore(); - Sha256Hash hash = b1.getMerkleRoot(); - b1.setMerkleRoot(Sha256Hash.ZERO_HASH); - try { - chain.add(b1); - fail(); - } catch (VerificationException e) { - // Expected. - b1.setMerkleRoot(hash); - } - // Now add a second block with no relevant transactions and then break it. - Transaction tx2 = createFakeTx(unitTestParams, COIN, - new ECKey().toAddress(unitTestParams)); - Block b2 = createFakeBlock(blockStore, tx2).block; - b2.getMerkleRoot(); - b2.setMerkleRoot(Sha256Hash.ZERO_HASH); - b2.solve(); - chain.add(b2); // Broken block is accepted because its contents don't matter to us. - } - - @Test - public void unconnectedBlocks() throws Exception { - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinbaseTo); - Block b2 = b1.createNextBlock(coinbaseTo); - Block b3 = b2.createNextBlock(coinbaseTo); - // Connected. - assertTrue(chain.add(b1)); - // Unconnected but stored. The head of the chain is still b1. - assertFalse(chain.add(b3)); - assertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader()); - // Add in the middle block. - assertTrue(chain.add(b2)); - assertEquals(chain.getChainHead().getHeader(), b3.cloneAsHeader()); - } - - @Test - public void difficultyTransitions() throws Exception { - // Add a bunch of blocks in a loop until we reach a difficulty transition point. The unit test params have an - // artificially shortened period. - Block prev = unitTestParams.getGenesisBlock(); - Utils.setMockClock(System.currentTimeMillis()/1000); - for (int i = 0; i < unitTestParams.getInterval() - 1; i++) { - Block newBlock = prev.createNextBlock(coinbaseTo, Utils.currentTimeSeconds()); - assertTrue(chain.add(newBlock)); - prev = newBlock; - // The fake chain should seem to be "fast" for the purposes of difficulty calculations. - Utils.rollMockClock(2); - } - // Now add another block that has no difficulty adjustment, it should be rejected. - try { - chain.add(prev.createNextBlock(coinbaseTo, Utils.currentTimeSeconds())); - fail(); - } catch (VerificationException e) { - } - // Create a new block with the right difficulty target given our blistering speed relative to the huge amount - // of time it's supposed to take (set in the unit test network parameters). - Block b = prev.createNextBlock(coinbaseTo, Utils.currentTimeSeconds()); - b.setDifficultyTarget(0x201fFFFFL); - b.solve(); - assertTrue(chain.add(b)); - // Successfully traversed a difficulty transition period. - } - - @Test - public void badDifficulty() throws Exception { - assertTrue(testNetChain.add(getBlock1())); - Block b2 = getBlock2(); - assertTrue(testNetChain.add(b2)); - Block bad = new Block(testNet); - // Merkle root can be anything here, doesn't matter. - bad.setMerkleRoot(new Sha256Hash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); - // Nonce was just some number that made the hash < difficulty limit set below, it can be anything. - bad.setNonce(140548933); - bad.setTime(1279242649); - bad.setPrevBlockHash(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.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); - try { - testNetChain.add(bad); - // The difficulty target above should be rejected on the grounds of being easier than the networks - // allowable difficulty. - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage(), e.getCause().getMessage().contains("Difficulty target is bad")); - } - - // Accept any level of difficulty now. - BigInteger oldVal = testNet.getMaxTarget(); - testNet.setMaxTarget(new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)); - try { - testNetChain.add(bad); - // We should not get here as the difficulty target should not be changing at this point. - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage(), e.getCause().getMessage().contains("Unexpected change in difficulty")); - } - testNet.setMaxTarget(oldVal); - - // TODO: Test difficulty change is not out of range when a transition period becomes valid. - } - - @Test - public void duplicates() throws Exception { - // Adding a block twice should not have any effect, in particular it should not send the block to the wallet. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinbaseTo); - Block b2 = b1.createNextBlock(coinbaseTo); - Block b3 = b2.createNextBlock(coinbaseTo); - assertTrue(chain.add(b1)); - assertEquals(b1, block[0].getHeader()); - assertTrue(chain.add(b2)); - assertEquals(b2, block[0].getHeader()); - assertTrue(chain.add(b3)); - assertEquals(b3, block[0].getHeader()); - assertEquals(b3, chain.getChainHead().getHeader()); - assertTrue(chain.add(b2)); - assertEquals(b3, chain.getChainHead().getHeader()); - // Wallet was NOT called with the new block because the duplicate add was spotted. - assertEquals(b3, block[0].getHeader()); - } - - @Test - public void intraBlockDependencies() throws Exception { - // Covers issue 166 in which transactions that depend on each other inside a block were not always being - // considered relevant. - Address somebodyElse = new ECKey().toAddress(unitTestParams); - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(somebodyElse); - ECKey key = wallet.freshReceiveKey(); - Address addr = key.toAddress(unitTestParams); - // Create a tx that gives us some coins, and another that spends it to someone else in the same block. - Transaction t1 = FakeTxBuilder.createFakeTx(unitTestParams, COIN, addr); - Transaction t2 = new Transaction(unitTestParams); - t2.addInput(t1.getOutputs().get(0)); - t2.addOutput(valueOf(2, 0), somebodyElse); - b1.addTransaction(t1); - b1.addTransaction(t2); - b1.solve(); - chain.add(b1); - assertEquals(Coin.ZERO, wallet.getBalance()); - } - - @Test - public void coinbaseTransactionAvailability() throws Exception { - // Check that a coinbase transaction is only available to spend after NetworkParameters.getSpendableCoinbaseDepth() blocks. - - // Create a second wallet to receive the coinbase spend. - Wallet wallet2 = new Wallet(unitTestParams); - ECKey receiveKey = wallet2.freshReceiveKey(); - chain.addWallet(wallet2); - - Address addressToSendTo = receiveKey.toAddress(unitTestParams); - - // Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet). - Block b1 = unitTestParams.getGenesisBlock().createNextBlockWithCoinbase(wallet.currentReceiveKey().getPubKey()); - chain.add(b1); - - // Check a transaction has been received. - assertNotNull(coinbaseTransaction); - - // The coinbase tx is not yet available to spend. - assertEquals(Coin.ZERO, wallet.getBalance()); - assertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS); - assertTrue(!coinbaseTransaction.isMature()); - - // Attempt to spend the coinbase - this should fail as the coinbase is not mature yet. - try { - wallet.createSend(addressToSendTo, valueOf(49, 0)); - fail(); - } catch (InsufficientMoneyException e) { - } - - // Check that the coinbase is unavailable to spend for the next spendableCoinbaseDepth - 2 blocks. - for (int i = 0; i < unitTestParams.getSpendableCoinbaseDepth() - 2; i++) { - // Non relevant tx - just for fake block creation. - Transaction tx2 = createFakeTx(unitTestParams, COIN, - new ECKey().toAddress(unitTestParams)); - - Block b2 = createFakeBlock(blockStore, tx2).block; - chain.add(b2); - - // Wallet still does not have the coinbase transaction available for spend. - assertEquals(Coin.ZERO, wallet.getBalance()); - assertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS); - - // The coinbase transaction is still not mature. - assertTrue(!coinbaseTransaction.isMature()); - - // Attempt to spend the coinbase - this should fail. - try { - wallet.createSend(addressToSendTo, valueOf(49, 0)); - fail(); - } catch (InsufficientMoneyException e) { - } - } - - // Give it one more block - should now be able to spend coinbase transaction. Non relevant tx. - Transaction tx3 = createFakeTx(unitTestParams, COIN, new ECKey().toAddress(unitTestParams)); - Block b3 = createFakeBlock(blockStore, tx3).block; - chain.add(b3); - - // Wallet now has the coinbase transaction available for spend. - assertEquals(wallet.getBalance(), FIFTY_COINS); - assertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS); - assertTrue(coinbaseTransaction.isMature()); - - // Create a spend with the coinbase BTC to the address in the second wallet - this should now succeed. - Transaction coinbaseSend2 = wallet.createSend(addressToSendTo, valueOf(49, 0)); - assertNotNull(coinbaseSend2); - - // Commit the coinbaseSpend to the first wallet and check the balances decrement. - wallet.commitTx(coinbaseSend2); - assertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN); - // Available balance is zero as change has not been received from a block yet. - assertEquals(wallet.getBalance(BalanceType.AVAILABLE), ZERO); - - // Give it one more block - change from coinbaseSpend should now be available in the first wallet. - Block b4 = createFakeBlock(blockStore, coinbaseSend2).block; - chain.add(b4); - assertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN); - - // Check the balances in the second wallet. - assertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0)); - assertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0)); - } - - // Some blocks from the test net. - private static Block getBlock2() throws Exception { - Block b2 = new Block(testNet); - b2.setMerkleRoot(new Sha256Hash("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b")); - b2.setNonce(2642058077L); - b2.setTime(1296734343L); - b2.setPrevBlockHash(new Sha256Hash("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")); - assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString()); - b2.verifyHeader(); - return b2; - } - - private static Block getBlock1() throws Exception { - Block b1 = new Block(testNet); - b1.setMerkleRoot(new Sha256Hash("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10")); - b1.setNonce(236038445); - b1.setTime(1296734340); - b1.setPrevBlockHash(new Sha256Hash("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")); - assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString()); - b1.verifyHeader(); - return b1; - } - - @Test - public void estimatedBlockTime() throws Exception { - NetworkParameters params = MainNetParams.get(); - BlockChain prod = new BlockChain(params, new MemoryBlockStore(params)); - Date d = prod.estimateBlockTime(200000); - // The actual date of block 200,000 was 2012-09-22 10:47:00 - assertEquals(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US).parse("2012-10-23T08:35:05.000-0700"), d); - } - - @Test - public void falsePositives() throws Exception { - double decay = AbstractBlockChain.FP_ESTIMATOR_ALPHA; - assertTrue(0 == chain.getFalsePositiveRate()); // Exactly - chain.trackFalsePositives(55); - assertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4); - chain.trackFilteredTransactions(550); - double rate1 = chain.getFalsePositiveRate(); - // Run this scenario a few more time for the filter to converge - for (int i = 1 ; i < 10 ; i++) { - chain.trackFalsePositives(55); - chain.trackFilteredTransactions(550); - } - - // Ensure we are within 10% - assertEquals(0.1, chain.getFalsePositiveRate(), 0.01); - - // Check that we get repeatable results after a reset - chain.resetFalsePositiveEstimate(); - assertTrue(0 == chain.getFalsePositiveRate()); // Exactly - - chain.trackFalsePositives(55); - assertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4); - chain.trackFilteredTransactions(550); - assertEquals(rate1, chain.getFalsePositiveRate(), 1e-4); - } - - @Test - public void rollbackBlockStore() throws Exception { - // This test simulates an issue on Android, that causes the VM to crash while receiving a block, so that the - // block store is persisted but the wallet is not. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinbaseTo); - Block b2 = b1.createNextBlock(coinbaseTo); - // Add block 1, no frills. - assertTrue(chain.add(b1)); - assertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader()); - assertEquals(1, chain.getBestChainHeight()); - assertEquals(1, wallet.getLastBlockSeenHeight()); - // Add block 2 while wallet is disconnected, to simulate crash. - chain.removeWallet(wallet); - assertTrue(chain.add(b2)); - assertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader()); - assertEquals(2, chain.getBestChainHeight()); - assertEquals(1, wallet.getLastBlockSeenHeight()); - // Add wallet back. This will detect the height mismatch and repair the damage done. - chain.addWallet(wallet); - assertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader()); - assertEquals(1, chain.getBestChainHeight()); - assertEquals(1, wallet.getLastBlockSeenHeight()); - // Now add block 2 correctly. - assertTrue(chain.add(b2)); - assertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader()); - assertEquals(2, chain.getBestChainHeight()); - assertEquals(2, wallet.getLastBlockSeenHeight()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/BlockTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/BlockTest.java deleted file mode 100644 index 0ae3bfc2..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/BlockTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright 2011 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.TestNet2Params; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.script.ScriptOpCodes; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.math.BigInteger; -import java.util.Arrays; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.*; - -public class BlockTest { - static final NetworkParameters params = TestNet2Params.get(); - - public static final byte[] blockBytes; - - static { - // Block 00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935 - // One with lots of transactions in, so a good test of the merkle tree hashing. - blockBytes = HEX.decode("0100000040f11b68435988807d64dff20261f7d9827825fbb37542601fb94d45000000000f28f7c69e2669981f92ff081c129e196200c60f4fad7911d93a682de0b49ea2ecd9d24c1844011d00d361050c01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07041844011d0142ffffffff0100f2052a01000000434104a313febd5f91b6a13bd9c5317030518fee96d1319a0eb10076917294933d09c17dc1588a06953a264738f2acea0c66b99e796caa4f28158e0dd5f6fed69a185bac000000000100000001aa18a952c3f73e5d7440bc570b2aa78f72059887b25b6a1790514b7feedec090000000008b483045022100a970ee6e96fa8bea1cf76d3bda3fb70441a6ec50014d4ea3adcdeae9fbfb5129022025ce9e090366dd6175071a0a5b4a4727571b9bd7bdd5a74d3d3bad7f63eb5dd4014104ac44bdf511477465cb70fef1d06b9241e74d26047ccbdfa641ec9a0115ad35594cbb58a61a6fd56893a405bcffbf6555995ddedc7e6cd4e5ceb83a37e1cf8f98ffffffff02004d92d86a0000001976a914b8083945473bc8289efb681f94de7b07a5b851ad88ac00743ba40b0000001976a914ef01911c9efec6799d1ee5f7c6fb072d9669da8088ac000000000100000001438bd97cb2172e0dd6f341e455e00b7d089747bd4e7f54bd802afe6a6d006c7c000000008a47304402207db94026c96572519101a08e2c864bbe51c987eda6266079a35286df68f123ca02202d7d24c616776a70cce6cb2f97a424e47c30d466e96b750ca03564810249073c014104880286646dab4c894a5ff1bf62bd80047a50b86446b326f2155de94a54d01f9058d4cbc7452563a7c18b2bfb353262fc5adac6307a9446e8c4669daa58e97071ffffffff0200743ba40b0000001976a914fce443c743b456606d1e70ff0d98c4609addc10688ac00ba1dd2050000001976a91411e3e67c08e5d791c97b3d49a8d52025d3f78d3a88ac000000000100000001dc4a6300b6eca8d7ab8e119e9fc4b18890c0e26ec950e681b8d5e46c214aee24010000008b48304502202bcf8632a11192f6b4998343c13589771e6715a080236087dcb1771cbab01809022100edcc38488dd70cd38c058994f143ca5d259071b8fe54c66bf67e55d4468dcacb01410475106e33e14e9cf35bc359dd4120b580ecf5412bb8803f2a927aecd4218d1346e242c7056dca2e4c114fcf2f60799bc5e79107bd1a8b8d5135c92f02bdb59834ffffffff0200f2052a010000001976a9146c9715e09fb00ba84af1ff916ff409b4a5dc9ae288ac00c817a8040000001976a914f7be161206700eb7be1bca5768232c61e4694f4788ac000000000100000001b6cc12ff76247895cb7a604d888012136f06bba64654262044ecb93ff7762c2f000000008b48304502206d795045622c7cdfb4a211c5b41d477920437c21e69214ab4a14f10fe0306b78022100840e55114d6922f3c5e44c7cdcf85dc800d1caef64e7846998423e4ba86714e6014104f88ae9067bc05136cb53a8c18f8549f544ff55ab87ada8f3ba7e2aea773ec73585b61f18ade1c0ddd6c447788578be5fb785c245a64d29b7ff5d28b85cbec58cffffffff0200743ba40b0000001976a914c8081083a8b741da2da260bc0656b88c7bfa6fbf88ac00743ba40b0000001976a914fce443c743b456606d1e70ff0d98c4609addc10688ac0000000001000000019a8d70c7a27560b28dfe778db9ce7f2ff235faf98d5123c07991682be90a4c16000000008b483045022100a118c34f63854ee03d15cca2918d592c295035c42e03be7b0c7e86e66d40ea790220558336d2583a1da00ed5bcad2de5d3b9d485431f702bf2f002267b35ab0b41a0014104f88ae9067bc05136cb53a8c18f8549f544ff55ab87ada8f3ba7e2aea773ec73585b61f18ade1c0ddd6c447788578be5fb785c245a64d29b7ff5d28b85cbec58cffffffff0200743ba40b0000001976a914a440ef00c2e1d39be93607da66568caa26e0501888ac00743ba40b0000001976a914e1d3e65f78f962c4e9dfd04db2119aeefa4e111088ac000000000100000001883acd4bff920f19c4e570e6b3e2d7503d1072d3ca098a124e23534ecdc879d5000000008a473044022040677305de69fd8c18e2c54d5b3c67c5c05735cf6b73d420ccd306762c4bfda2022032cd32ac15ac1820265ffce82654a6008cda22a79fb619ebb65e0af806e14f9b0141044423ef78a2859eb57c4a59dc0878141cf5a4b1fdef71d649d3fb5cf8ea6b1114f4086e5d684a0999d4435db99217a994cc3cf7ad435c8f4e44613d9d160916c4ffffffff0100743ba40b0000001976a914fce443c743b456606d1e70ff0d98c4609addc10688ac000000000100000001ceb27fb142ce3bf9a1f263653dc3971332c71dd10e0e83d647037f608c459f12000000008b4830450220389218287e87d0d7b7113eb20cc1cbf1a00d7acdca32bba7f184cd066db74d6a022100b0998058e5a242699a48f931004cf5550f4e8802b866ce1baf1a0b2616861f27014104255a048d416984101c17514a89289a7d5d3dc8c562850c7a3599f0c7c39bcf9c3a43df75e1e614e51d70c5f85212c99298a21f087be93ecba7ef3900d02c0e8bffffffff0200743ba40b0000001976a914211fd13b614521ed566ddd42738381e42c3c2b2088ac00d956345f0000001976a914d3cc345ba8bdf51d7097955f0f259731f4c34f4388ac000000000100000001703701493f08e82bf6d8cb7c517070eee9f62d14904e14636a7b4af4f34180c7010000008a4730440220061a61eae90ffcf13c10c88a88c085b02954f488823c2f5c81e83a5a833e9f3b02204a61498a9668b2793e77fe3b68585f2daff4dd5daf6097a82615035325ada4730141040db6308d6170333e2c50dee4c9f18f0ab84a7a5c4c88a6836a91f39cb8f4712e08bd72979c542d4b3b60e8dc2021c1b3cc45ffaa83f36a9dec3c4473ea2aa2f3ffffffff0200f2052a010000001976a9143e7e087b9b09149e0266b7a416da2709b4ccf58788ac00d6117e030000001976a914777af71a3b2a48e48f2e467f65028d85c1b5eb5288ac0000000001000000014bdc82abc7db9c06613a712e488685c6feb4522d25017b856222171c17d144e0000000008b4830450221009eb7edcbf8d6be63529264b07bb9f40cf1a0ca779235999e40f5311d70706f1102207f65c5f66982519e6d82e13ca3e61f4f071c73da6c5830b3c4461252012b474e0141045af9665878e6696fd069669951acc54a87c5e3b256a9e20cd8858e0dc5a8c53624e0c979096c00af8a8c60136eef9ffa3d511309417b8315b7f9e3e41e805e8fffffffff0100743ba40b0000001976a914e1d3e65f78f962c4e9dfd04db2119aeefa4e111088ac000000000100000001a854b2b84a76e43de59db647121cdfe481bd8ae9623a345c2188369775b533f7010000008c493046022100c4db6ecf679264c9b525628ec5a983710ff45a1d2d4aa0b54ee218ca9a1ad4df022100dc2e0077cfdd3cbeb28f7463632902ad5306f6d5c77c8149e5b9249bfea8060e014104f9a476b612bb9788c64b9b1e4c9d2deaae1ef0baf6eb593a95d00e2ef8a2beb897ea1fb7c3832e842dd6307fd162816c19c8f458fd8dae331dbc9062fb02e5d8ffffffff0200651b90530000001976a914d5c7c9aec292a807005f013c4d2122f7126e257788ac00743ba40b0000001976a914211fd13b614521ed566ddd42738381e42c3c2b2088ac0000000001000000012908482e9f7d31e9dd392bb6e788a329458a3bc95230b468e4b8c578d27a63b3000000008a4730440220549a7b422fc2020671acabfb937349bd87d985b2e4b9698e4ccacc985f61aee102204dc272322079e9114746db2f8d035d82b64523a69cd7be674173e063090cc8ac014104011a6c220a5549ff112c92c6c38dec93f66ef1f0a21d1409b92f0ccf0fb159aa8173a5b2413a45140fc02b45d63775bae03691d9dc87fd7a10d709a04922900cffffffff0200743ba40b0000001976a914211fd13b614521ed566ddd42738381e42c3c2b2088ac00f1dfeb470000001976a9140adcb4e90cc87f53d7618294222a8a4e193ae9f088ac00000000"); - } - - @Test - public void testWork() throws Exception { - BigInteger work = params.getGenesisBlock().getWork(); - // This number is printed by the official client at startup as the calculated value of chainWork on testnet: - // - // SetBestChain: new best=00000007199508e34a9f height=0 work=536879104 - assertEquals(BigInteger.valueOf(536879104L), work); - } - - @Test - public void testBlockVerification() throws Exception { - Block block = new Block(params, blockBytes); - block.verify(); - assertEquals("00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.getHashAsString()); - } - - @SuppressWarnings("deprecation") - @Test - public void testDate() throws Exception { - Block block = new Block(params, blockBytes); - assertEquals("4 Nov 2010 16:06:04 GMT", block.getTime().toGMTString()); - } - - @Test - public void testProofOfWork() throws Exception { - // This params accepts any difficulty target. - NetworkParameters params = UnitTestParams.get(); - Block block = new Block(params, blockBytes); - block.setNonce(12346); - try { - block.verify(); - fail(); - } catch (VerificationException e) { - // Expected. - } - // Blocks contain their own difficulty target. The BlockChain verification mechanism is what stops real blocks - // from containing artificially weak difficulties. - block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); - // Now it should pass. - block.verify(); - // Break the nonce again at the lower difficulty level so we can try solving for it. - block.setNonce(1); - try { - block.verify(); - fail(); - } catch (VerificationException e) { - // Expected to fail as the nonce is no longer correct. - } - // Should find an acceptable nonce. - block.solve(); - block.verify(); - assertEquals(block.getNonce(), 2); - } - - @Test - public void testBadTransactions() throws Exception { - Block block = new Block(params, blockBytes); - // Re-arrange so the coinbase transaction is not first. - Transaction tx1 = block.transactions.get(0); - Transaction tx2 = block.transactions.get(1); - block.transactions.set(0, tx2); - block.transactions.set(1, tx1); - try { - block.verify(); - fail(); - } catch (VerificationException e) { - // We should get here. - } - } - - @Test - public void testHeaderParse() throws Exception { - Block block = new Block(params, blockBytes); - Block header = block.cloneAsHeader(); - Block reparsed = new Block(params, header.bitcoinSerialize()); - assertEquals(reparsed, header); - } - - @Test - public void testBitCoinSerialization() throws Exception { - // We have to be able to reserialize everything exactly as we found it for hashing to work. This test also - // proves that transaction serialization works, along with all its subobjects like scripts and in/outpoints. - // - // NB: This tests the BITCOIN proprietary serialization protocol. A different test checks Java serialization - // of transactions. - Block block = new Block(params, blockBytes); - assertTrue(Arrays.equals(blockBytes, block.bitcoinSerialize())); - } - - @Test - public void testJavaSerialiazation() throws Exception { - Block block = new Block(params, blockBytes); - Transaction tx = block.transactions.get(1); - - // Serialize using Java. - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(tx); - oos.close(); - byte[] javaBits = bos.toByteArray(); - // Deserialize again. - ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(javaBits)); - Transaction tx2 = (Transaction) ois.readObject(); - ois.close(); - - // Note that this will actually check the transactions are equal by doing bitcoin serialization and checking - // the bytestreams are the same! A true "deep equals" is not implemented for Transaction. The primary purpose - // of this test is to ensure no errors occur during the Java serialization/deserialization process. - assertEquals(tx, tx2); - } - - @Test - public void testUpdateLength() { - NetworkParameters params = UnitTestParams.get(); - Block block = params.getGenesisBlock().createNextBlockWithCoinbase(new ECKey().getPubKey()); - assertEquals(block.bitcoinSerialize().length, block.length); - final int origBlockLen = block.length; - Transaction tx = new Transaction(params); - // this is broken until the transaction has > 1 input + output (which is required anyway...) - //assertTrue(tx.length == tx.bitcoinSerialize().length && tx.length == 8); - byte[] outputScript = new byte[10]; - Arrays.fill(outputScript, (byte) ScriptOpCodes.OP_FALSE); - tx.addOutput(new TransactionOutput(params, null, Coin.SATOSHI, outputScript)); - tx.addInput(new TransactionInput(params, null, new byte[] {(byte) ScriptOpCodes.OP_FALSE}, - new TransactionOutPoint(params, 0, Sha256Hash.create(new byte[] {1})))); - int origTxLength = 8 + 2 + 8 + 1 + 10 + 40 + 1 + 1; - assertEquals(tx.bitcoinSerialize().length, tx.length); - assertEquals(origTxLength, tx.length); - block.addTransaction(tx); - assertEquals(block.bitcoinSerialize().length, block.length); - assertEquals(origBlockLen + tx.length, block.length); - block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {(byte) ScriptOpCodes.OP_FALSE, (byte) ScriptOpCodes.OP_FALSE}); - assertEquals(block.length, origBlockLen + tx.length); - assertEquals(tx.length, origTxLength + 1); - block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {}); - assertEquals(block.length, block.bitcoinSerialize().length); - assertEquals(block.length, origBlockLen + tx.length); - assertEquals(tx.length, origTxLength - 1); - block.getTransactions().get(1).addInput(new TransactionInput(params, null, new byte[] {(byte) ScriptOpCodes.OP_FALSE}, - new TransactionOutPoint(params, 0, Sha256Hash.create(new byte[] {1})))); - assertEquals(block.length, origBlockLen + tx.length); - assertEquals(tx.length, origTxLength + 41); // - 1 + 40 + 1 + 1 - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/BloomFilterTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/BloomFilterTest.java deleted file mode 100644 index 69180ed7..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/BloomFilterTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2012 Matt Corallo - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.wallet.KeyChainGroup; -import org.junit.Test; - -import java.util.Arrays; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.*; - -public class BloomFilterTest { - @Test - public void insertSerializeTest() { - BloomFilter filter = new BloomFilter(3, 0.01, 0, BloomFilter.BloomUpdate.UPDATE_ALL); - - filter.insert(HEX.decode("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); - assertTrue (filter.contains(HEX.decode("99108ad8ed9bb6274d3980bab5a85c048f0950c8"))); - // One bit different in first byte - assertFalse(filter.contains(HEX.decode("19108ad8ed9bb6274d3980bab5a85c048f0950c8"))); - - filter.insert(HEX.decode("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")); - assertTrue(filter.contains(HEX.decode("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"))); - - filter.insert(HEX.decode("b9300670b4c5366e95b2699e8b18bc75e5f729c5")); - assertTrue(filter.contains(HEX.decode("b9300670b4c5366e95b2699e8b18bc75e5f729c5"))); - - // Value generated by the reference client - assertTrue(Arrays.equals(HEX.decode("03614e9b050000000000000001"), filter.bitcoinSerialize())); - } - - @Test - public void insertSerializeTestWithTweak() { - BloomFilter filter = new BloomFilter(3, 0.01, 2147483649L); - - filter.insert(HEX.decode("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); - assertTrue (filter.contains(HEX.decode("99108ad8ed9bb6274d3980bab5a85c048f0950c8"))); - // One bit different in first byte - assertFalse(filter.contains(HEX.decode("19108ad8ed9bb6274d3980bab5a85c048f0950c8"))); - - filter.insert(HEX.decode("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")); - assertTrue(filter.contains(HEX.decode("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"))); - - filter.insert(HEX.decode("b9300670b4c5366e95b2699e8b18bc75e5f729c5")); - assertTrue(filter.contains(HEX.decode("b9300670b4c5366e95b2699e8b18bc75e5f729c5"))); - - // Value generated by the reference client - assertTrue(Arrays.equals(HEX.decode("03ce4299050000000100008002"), filter.bitcoinSerialize())); - } - - @Test - public void walletTest() throws Exception { - NetworkParameters params = MainNetParams.get(); - - DumpedPrivateKey privKey = new DumpedPrivateKey(params, "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"); - - Address addr = privKey.getKey().toAddress(params); - assertTrue(addr.toString().equals("17Wx1GQfyPTNWpQMHrTwRSMTCAonSiZx9e")); - - KeyChainGroup group = new KeyChainGroup(params); - // Add a random key which happens to have been used in a recent generation - group.importKeys(privKey.getKey(), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99"))); - Wallet wallet = new Wallet(params, group); - wallet.commitTx(new Transaction(params, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038754030114062f503253482fffffffff01c05e559500000000232103cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99ac00000000"))); - - // We should have 2 per pubkey, and one for the pay-2-pubkey output we have - assertEquals(5, wallet.getBloomFilterElementCount()); - - BloomFilter filter = wallet.getBloomFilter(wallet.getBloomFilterElementCount(), 0.001, 0); - - // Value generated by the reference client - assertTrue(Arrays.equals(HEX.decode("082ae5edc8e51d4a03080000000000000002"), filter.bitcoinSerialize())); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/ChainSplitTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/ChainSplitTest.java deleted file mode 100644 index bd42bd85..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/ChainSplitTest.java +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.core.TransactionConfidence.ConfidenceType; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.store.MemoryBlockStore; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.dogecoin.dogecoinj.utils.Threading; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.math.BigInteger; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.*; - -public class ChainSplitTest { - private static final Logger log = LoggerFactory.getLogger(ChainSplitTest.class); - - private NetworkParameters unitTestParams; - private Wallet wallet; - private BlockChain chain; - private Address coinsTo; - private Address coinsTo2; - private Address someOtherGuy; - private MemoryBlockStore blockStore; - - @Before - public void setUp() throws Exception { - BriefLogFormatter.init(); - Utils.setMockClock(); // Use mock clock - Wallet.SendRequest.DEFAULT_FEE_PER_KB = Coin.ZERO; - unitTestParams = UnitTestParams.get(); - wallet = new Wallet(unitTestParams); - ECKey key1 = wallet.freshReceiveKey(); - ECKey key2 = wallet.freshReceiveKey(); - blockStore = new MemoryBlockStore(unitTestParams); - chain = new BlockChain(unitTestParams, wallet, blockStore); - coinsTo = key1.toAddress(unitTestParams); - coinsTo2 = key2.toAddress(unitTestParams); - someOtherGuy = new ECKey().toAddress(unitTestParams); - } - - @Test - public void testForking1() throws Exception { - // Check that if the block chain forks, we end up using the right chain. Only tests inbound transactions - // (receiving coins). Checking that we understand reversed spends is in testForking2. - final AtomicBoolean reorgHappened = new AtomicBoolean(); - final AtomicInteger walletChanged = new AtomicInteger(); - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onReorganize(Wallet wallet) { - reorgHappened.set(true); - } - - @Override - public void onWalletChanged(Wallet wallet) { - walletChanged.incrementAndGet(); - } - }); - - // Start by building a couple of blocks on top of the genesis block. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - Block b2 = b1.createNextBlock(coinsTo); - assertTrue(chain.add(b1)); - assertTrue(chain.add(b2)); - Threading.waitForUserCode(); - assertFalse(reorgHappened.get()); - assertEquals(2, walletChanged.get()); - // We got two blocks which sent 50 coins each to us. - assertEquals(Coin.valueOf(100, 0), wallet.getBalance()); - // We now have the following chain: - // genesis -> b1 -> b2 - // - // so fork like this: - // - // genesis -> b1 -> b2 - // \-> b3 - // - // Nothing should happen at this point. We saw b2 first so it takes priority. - Block b3 = b1.createNextBlock(someOtherGuy); - assertTrue(chain.add(b3)); - Threading.waitForUserCode(); - assertFalse(reorgHappened.get()); // No re-org took place. - assertEquals(2, walletChanged.get()); - assertEquals(Coin.valueOf(100, 0), wallet.getBalance()); - // Check we can handle multi-way splits: this is almost certainly going to be extremely rare, but we have to - // handle it anyway. The same transaction appears in b7/b8 (side chain) but not b2 or b3. - // genesis -> b1--> b2 - // |-> b3 - // |-> b7 (x) - // \-> b8 (x) - Block b7 = b1.createNextBlock(coinsTo); - assertTrue(chain.add(b7)); - Block b8 = b1.createNextBlock(coinsTo); - final Transaction t = b7.getTransactions().get(1); - final Sha256Hash tHash = t.getHash(); - b8.addTransaction(t); - b8.solve(); - assertTrue(chain.add(roundtrip(b8))); - Threading.waitForUserCode(); - assertEquals(2, wallet.getTransaction(tHash).getAppearsInHashes().size()); - assertFalse(reorgHappened.get()); // No re-org took place. - assertEquals(5, walletChanged.get()); - assertEquals(Coin.valueOf(100, 0), wallet.getBalance()); - // Now we add another block to make the alternative chain longer. - assertTrue(chain.add(b3.createNextBlock(someOtherGuy))); - Threading.waitForUserCode(); - assertTrue(reorgHappened.get()); // Re-org took place. - assertEquals(6, walletChanged.get()); - reorgHappened.set(false); - // - // genesis -> b1 -> b2 - // \-> b3 -> b4 - // We lost some coins! b2 is no longer a part of the best chain so our available balance should drop to 50. - // It's now pending reconfirmation. - assertEquals(FIFTY_COINS, wallet.getBalance()); - // ... and back to the first chain. - Block b5 = b2.createNextBlock(coinsTo); - Block b6 = b5.createNextBlock(coinsTo); - assertTrue(chain.add(b5)); - assertTrue(chain.add(b6)); - // - // genesis -> b1 -> b2 -> b5 -> b6 - // \-> b3 -> b4 - // - Threading.waitForUserCode(); - assertTrue(reorgHappened.get()); - assertEquals(9, walletChanged.get()); - assertEquals(Coin.valueOf(200, 0), wallet.getBalance()); - } - - @Test - public void testForking2() throws Exception { - // Check that if the chain forks and new coins are received in the alternate chain our balance goes up - // after the re-org takes place. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(someOtherGuy); - Block b2 = b1.createNextBlock(someOtherGuy); - assertTrue(chain.add(b1)); - assertTrue(chain.add(b2)); - // genesis -> b1 -> b2 - // \-> b3 -> b4 - assertEquals(Coin.ZERO, wallet.getBalance()); - Block b3 = b1.createNextBlock(coinsTo); - Block b4 = b3.createNextBlock(someOtherGuy); - assertTrue(chain.add(b3)); - assertEquals(Coin.ZERO, wallet.getBalance()); - assertTrue(chain.add(b4)); - assertEquals(FIFTY_COINS, wallet.getBalance()); - } - - @Test - public void testForking3() throws Exception { - // Check that we can handle our own spends being rolled back by a fork. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - chain.add(b1); - assertEquals(FIFTY_COINS, wallet.getBalance()); - Address dest = new ECKey().toAddress(unitTestParams); - Transaction spend = wallet.createSend(dest, valueOf(10, 0)); - wallet.commitTx(spend); - // Waiting for confirmation ... make it eligible for selection. - assertEquals(Coin.ZERO, wallet.getBalance()); - spend.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByAddress(new byte[]{1, 2, 3, 4}))); - spend.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByAddress(new byte[]{5,6,7,8}))); - assertEquals(ConfidenceType.PENDING, spend.getConfidence().getConfidenceType()); - assertEquals(valueOf(40, 0), wallet.getBalance()); - Block b2 = b1.createNextBlock(someOtherGuy); - b2.addTransaction(spend); - b2.solve(); - chain.add(roundtrip(b2)); - // We have 40 coins in change. - assertEquals(ConfidenceType.BUILDING, spend.getConfidence().getConfidenceType()); - // genesis -> b1 (receive coins) -> b2 (spend coins) - // \-> b3 -> b4 - Block b3 = b1.createNextBlock(someOtherGuy); - Block b4 = b3.createNextBlock(someOtherGuy); - chain.add(b3); - chain.add(b4); - // b4 causes a re-org that should make our spend go pending again. - assertEquals(valueOf(40, 0), wallet.getBalance()); - assertEquals(ConfidenceType.PENDING, spend.getConfidence().getConfidenceType()); - } - - @Test - public void testForking4() throws Exception { - // Check that we can handle external spends on an inactive chain becoming active. An external spend is where - // we see a transaction that spends our own coins but we did not broadcast it ourselves. This happens when - // keys are being shared between wallets. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - chain.add(b1); - assertEquals(FIFTY_COINS, wallet.getBalance()); - Address dest = new ECKey().toAddress(unitTestParams); - Transaction spend = wallet.createSend(dest, FIFTY_COINS); - // We do NOT confirm the spend here. That means it's not considered to be pending because createSend is - // stateless. For our purposes it is as if some other program with our keys created the tx. - // - // genesis -> b1 (receive 50) --> b2 - // \-> b3 (external spend) -> b4 - Block b2 = b1.createNextBlock(someOtherGuy); - chain.add(b2); - Block b3 = b1.createNextBlock(someOtherGuy); - b3.addTransaction(spend); - b3.solve(); - chain.add(roundtrip(b3)); - // The external spend is now pending. - assertEquals(ZERO, wallet.getBalance()); - Transaction tx = wallet.getTransaction(spend.getHash()); - assertEquals(ConfidenceType.PENDING, tx.getConfidence().getConfidenceType()); - Block b4 = b3.createNextBlock(someOtherGuy); - chain.add(b4); - // The external spend is now active. - assertEquals(ZERO, wallet.getBalance()); - assertEquals(ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType()); - } - - @Test - public void testForking5() throws Exception { - // Test the standard case in which a block containing identical transactions appears on a side chain. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - chain.add(b1); - final Transaction t = b1.transactions.get(1); - assertEquals(FIFTY_COINS, wallet.getBalance()); - // genesis -> b1 - // -> b2 - Block b2 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - Transaction b2coinbase = b2.transactions.get(0); - b2.transactions.clear(); - b2.addTransaction(b2coinbase); - b2.addTransaction(t); - b2.solve(); - chain.add(roundtrip(b2)); - assertEquals(FIFTY_COINS, wallet.getBalance()); - assertTrue(wallet.isConsistent()); - assertEquals(2, wallet.getTransaction(t.getHash()).getAppearsInHashes().size()); - // -> b2 -> b3 - Block b3 = b2.createNextBlock(someOtherGuy); - chain.add(b3); - assertEquals(FIFTY_COINS, wallet.getBalance()); - - } - - private Block roundtrip(Block b2) throws ProtocolException { - return new Block(unitTestParams, b2.bitcoinSerialize()); - } - - @Test - public void testForking6() throws Exception { - // Test the case in which a side chain block contains a tx, and then it appears in the main chain too. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(someOtherGuy); - chain.add(b1); - // genesis -> b1 - // -> b2 - Block b2 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - chain.add(b2); - assertEquals(Coin.ZERO, wallet.getBalance()); - // genesis -> b1 -> b3 - // -> b2 - Block b3 = b1.createNextBlock(someOtherGuy); - b3.addTransaction(b2.transactions.get(1)); - b3.solve(); - chain.add(roundtrip(b3)); - assertEquals(FIFTY_COINS, wallet.getBalance()); - } - - @Test - public void testDoubleSpendOnFork() throws Exception { - // Check what happens when a re-org happens and one of our confirmed transactions becomes invalidated by a - // double spend on the new best chain. - - final boolean[] eventCalled = new boolean[1]; - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) { - super.onTransactionConfidenceChanged(wallet, tx); - if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.DEAD) - eventCalled[0] = true; - } - }); - - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - chain.add(b1); - - Transaction t1 = wallet.createSend(someOtherGuy, valueOf(10, 0)); - Address yetAnotherGuy = new ECKey().toAddress(unitTestParams); - Transaction t2 = wallet.createSend(yetAnotherGuy, valueOf(20, 0)); - wallet.commitTx(t1); - // Receive t1 as confirmed by the network. - Block b2 = b1.createNextBlock(new ECKey().toAddress(unitTestParams)); - b2.addTransaction(t1); - b2.solve(); - chain.add(roundtrip(b2)); - - // Now we make a double spend become active after a re-org. - Block b3 = b1.createNextBlock(new ECKey().toAddress(unitTestParams)); - b3.addTransaction(t2); - b3.solve(); - chain.add(roundtrip(b3)); // Side chain. - Block b4 = b3.createNextBlock(new ECKey().toAddress(unitTestParams)); - chain.add(b4); // New best chain. - Threading.waitForUserCode(); - // Should have seen a double spend. - assertTrue(eventCalled[0]); - assertEquals(valueOf(30, 0), wallet.getBalance()); - } - - @Test - public void testDoubleSpendOnForkPending() throws Exception { - // Check what happens when a re-org happens and one of our unconfirmed transactions becomes invalidated by a - // double spend on the new best chain. - final Transaction[] eventDead = new Transaction[1]; - final Transaction[] eventReplacement = new Transaction[1]; - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) { - super.onTransactionConfidenceChanged(wallet, tx); - if (tx.getConfidence().getConfidenceType() == - TransactionConfidence.ConfidenceType.DEAD) { - eventDead[0] = tx; - eventReplacement[0] = tx.getConfidence().getOverridingTransaction(); - } - } - }); - - // Start with 50 coins. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - chain.add(b1); - - Transaction t1 = checkNotNull(wallet.createSend(someOtherGuy, valueOf(10, 0))); - Address yetAnotherGuy = new ECKey().toAddress(unitTestParams); - Transaction t2 = checkNotNull(wallet.createSend(yetAnotherGuy, valueOf(20, 0))); - wallet.commitTx(t1); - // t1 is still pending ... - Block b2 = b1.createNextBlock(new ECKey().toAddress(unitTestParams)); - chain.add(b2); - assertEquals(ZERO, wallet.getBalance()); - assertEquals(valueOf(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); - - // Now we make a double spend become active after a re-org. - // genesis -> b1 -> b2 [t1 pending] - // \-> b3 (t2) -> b4 - Block b3 = b1.createNextBlock(new ECKey().toAddress(unitTestParams)); - b3.addTransaction(t2); - b3.solve(); - chain.add(roundtrip(b3)); // Side chain. - Block b4 = b3.createNextBlock(new ECKey().toAddress(unitTestParams)); - chain.add(b4); // New best chain. - Threading.waitForUserCode(); - // Should have seen a double spend against the pending pool. - // genesis -> b1 -> b2 [t1 dead and exited the miners mempools] - // \-> b3 (t2) -> b4 - assertEquals(t1, eventDead[0]); - assertEquals(t2, eventReplacement[0]); - assertEquals(valueOf(30, 0), wallet.getBalance()); - - // ... and back to our own parallel universe. - Block b5 = b2.createNextBlock(new ECKey().toAddress(unitTestParams)); - chain.add(b5); - Block b6 = b5.createNextBlock(new ECKey().toAddress(unitTestParams)); - chain.add(b6); - // genesis -> b1 -> b2 -> b5 -> b6 [t1 still dead] - // \-> b3 [t2 resurrected and now pending] -> b4 - assertEquals(ZERO, wallet.getBalance()); - // t2 is pending - resurrected double spends take precedence over our dead transactions (which are in nobodies - // mempool by this point). - t1 = checkNotNull(wallet.getTransaction(t1.getHash())); - t2 = checkNotNull(wallet.getTransaction(t2.getHash())); - assertEquals(ConfidenceType.DEAD, t1.getConfidence().getConfidenceType()); - assertEquals(ConfidenceType.PENDING, t2.getConfidence().getConfidenceType()); - } - - @Test - public void txConfidenceLevels() throws Exception { - // Check that as the chain forks and re-orgs, the confidence data associated with each transaction is - // maintained correctly. - final ArrayList txns = new ArrayList(3); - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - txns.add(tx); - } - }); - - // Start by building three blocks on top of the genesis block. All send to us. - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); - BigInteger work1 = b1.getWork(); - Block b2 = b1.createNextBlock(coinsTo2); - BigInteger work2 = b2.getWork(); - Block b3 = b2.createNextBlock(coinsTo2); - BigInteger work3 = b3.getWork(); - - assertTrue(chain.add(b1)); - assertTrue(chain.add(b2)); - assertTrue(chain.add(b3)); - Threading.waitForUserCode(); - // Check the transaction confidence levels are correct. - assertEquals(3, txns.size()); - - assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight()); - assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight()); - assertEquals(3, txns.get(2).getConfidence().getAppearedAtChainHeight()); - - assertEquals(3, txns.get(0).getConfidence().getDepthInBlocks()); - assertEquals(2, txns.get(1).getConfidence().getDepthInBlocks()); - assertEquals(1, txns.get(2).getConfidence().getDepthInBlocks()); - - // We now have the following chain: - // genesis -> b1 -> b2 -> b3 - // - // so fork like this: - // - // genesis -> b1 -> b2 -> b3 - // \-> b4 -> b5 - // - // Nothing should happen at this point. We saw b2 and b3 first so it takes priority. - Block b4 = b1.createNextBlock(someOtherGuy); - BigInteger work4 = b4.getWork(); - - Block b5 = b4.createNextBlock(someOtherGuy); - BigInteger work5 = b5.getWork(); - - assertTrue(chain.add(b4)); - assertTrue(chain.add(b5)); - Threading.waitForUserCode(); - assertEquals(3, txns.size()); - - assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight()); - assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight()); - assertEquals(3, txns.get(2).getConfidence().getAppearedAtChainHeight()); - - assertEquals(3, txns.get(0).getConfidence().getDepthInBlocks()); - assertEquals(2, txns.get(1).getConfidence().getDepthInBlocks()); - assertEquals(1, txns.get(2).getConfidence().getDepthInBlocks()); - - // Now we add another block to make the alternative chain longer. - Block b6 = b5.createNextBlock(someOtherGuy); - BigInteger work6 = b6.getWork(); - assertTrue(chain.add(b6)); - // - // genesis -> b1 -> b2 -> b3 - // \-> b4 -> b5 -> b6 - // - - assertEquals(3, txns.size()); - assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight()); - assertEquals(4, txns.get(0).getConfidence().getDepthInBlocks()); - - // Transaction 1 (in block b2) is now on a side chain, so it goes pending (not see in chain). - assertEquals(ConfidenceType.PENDING, txns.get(1).getConfidence().getConfidenceType()); - try { - txns.get(1).getConfidence().getAppearedAtChainHeight(); - fail(); - } catch (IllegalStateException e) {} - assertEquals(0, txns.get(1).getConfidence().getDepthInBlocks()); - - // ... and back to the first chain. - Block b7 = b3.createNextBlock(coinsTo); - BigInteger work7 = b7.getWork(); - Block b8 = b7.createNextBlock(coinsTo); - BigInteger work8 = b7.getWork(); - - assertTrue(chain.add(b7)); - assertTrue(chain.add(b8)); - // - // genesis -> b1 -> b2 -> b3 -> b7 -> b8 - // \-> b4 -> b5 -> b6 - // - - // This should be enabled, once we figure out the best way to inform the user of how the wallet is changing - // during the re-org. - //assertEquals(5, txns.size()); - - assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight()); - assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight()); - assertEquals(3, txns.get(2).getConfidence().getAppearedAtChainHeight()); - - assertEquals(5, txns.get(0).getConfidence().getDepthInBlocks()); - assertEquals(4, txns.get(1).getConfidence().getDepthInBlocks()); - assertEquals(3, txns.get(2).getConfidence().getDepthInBlocks()); - - assertEquals(Coin.valueOf(250, 0), wallet.getBalance()); - - // Now add two more blocks that don't send coins to us. Despite being irrelevant the wallet should still update. - Block b9 = b8.createNextBlock(someOtherGuy); - Block b10 = b9.createNextBlock(someOtherGuy); - chain.add(b9); - chain.add(b10); - BigInteger extraWork = b9.getWork().add(b10.getWork()); - assertEquals(7, txns.get(0).getConfidence().getDepthInBlocks()); - assertEquals(6, txns.get(1).getConfidence().getDepthInBlocks()); - assertEquals(5, txns.get(2).getConfidence().getDepthInBlocks()); - } - - @Test - public void orderingInsideBlock() throws Exception { - // Test that transactions received in the same block have their ordering preserved when reorganising. - // This covers issue 468. - - // Receive some money to the wallet. - Transaction t1 = FakeTxBuilder.createFakeTx(unitTestParams, COIN, coinsTo); - final Block b1 = FakeTxBuilder.makeSolvedTestBlock(unitTestParams.genesisBlock, t1); - chain.add(b1); - - // Send a couple of payments one after the other (so the second depends on the change output of the first). - wallet.allowSpendingUnconfirmedTransactions(); - Transaction t2 = checkNotNull(wallet.createSend(new ECKey().toAddress(unitTestParams), CENT)); - wallet.commitTx(t2); - Transaction t3 = checkNotNull(wallet.createSend(new ECKey().toAddress(unitTestParams), CENT)); - wallet.commitTx(t3); - chain.add(FakeTxBuilder.makeSolvedTestBlock(b1, t2, t3)); - - final Coin coins0point98 = COIN.subtract(CENT).subtract(CENT); - assertEquals(coins0point98, wallet.getBalance()); - - // Now round trip the wallet and force a re-org. - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - wallet.saveToFileStream(bos); - wallet = Wallet.loadFromFileStream(new ByteArrayInputStream(bos.toByteArray())); - final Block b2 = FakeTxBuilder.makeSolvedTestBlock(b1, t2, t3); - final Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2); - chain.add(b2); - chain.add(b3); - - // And verify that the balance is as expected. Because signatures are currently non-deterministic if the order - // isn't being stored correctly this should fail 50% of the time. - assertEquals(coins0point98, wallet.getBalance()); - } - - @Test - public void coinbaseDeath() throws Exception { - // Check that a coinbase tx is marked as dead after a reorg rather than pending as normal non-double-spent - // transactions would be. Also check that a dead coinbase on a sidechain is resurrected if the sidechain - // becomes the best chain once more. Finally, check that dependent transactions are killed recursively. - final ArrayList txns = new ArrayList(3); - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - txns.add(tx); - } - }, Threading.SAME_THREAD); - - Block b1 = unitTestParams.getGenesisBlock().createNextBlock(someOtherGuy); - final ECKey coinsTo2 = wallet.freshReceiveKey(); - Block b2 = b1.createNextBlockWithCoinbase(coinsTo2.getPubKey()); - Block b3 = b2.createNextBlock(someOtherGuy); - - log.debug("Adding block b1"); - assertTrue(chain.add(b1)); - log.debug("Adding block b2"); - assertTrue(chain.add(b2)); - log.debug("Adding block b3"); - assertTrue(chain.add(b3)); - - // We now have the following chain: - // genesis -> b1 -> b2 -> b3 - // - - // Check we have seen the coinbase. - assertEquals(1, txns.size()); - - // Check the coinbase transaction is building and in the unspent pool only. - final Transaction coinbase = txns.get(0); - assertEquals(ConfidenceType.BUILDING, coinbase.getConfidence().getConfidenceType()); - assertTrue(!wallet.pending.containsKey(coinbase.getHash())); - assertTrue(wallet.unspent.containsKey(coinbase.getHash())); - assertTrue(!wallet.spent.containsKey(coinbase.getHash())); - assertTrue(!wallet.dead.containsKey(coinbase.getHash())); - - // Add blocks to b3 until we can spend the coinbase. - Block firstTip = b3; - for (int i = 0; i < unitTestParams.getSpendableCoinbaseDepth() - 2; i++) { - firstTip = firstTip.createNextBlock(someOtherGuy); - chain.add(firstTip); - } - // ... and spend. - Transaction fodder = wallet.createSend(new ECKey().toAddress(unitTestParams), FIFTY_COINS); - wallet.commitTx(fodder); - final AtomicBoolean fodderIsDead = new AtomicBoolean(false); - fodder.getConfidence().addEventListener(new TransactionConfidence.Listener() { - @Override - public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason) { - fodderIsDead.set(confidence.getConfidenceType() == ConfidenceType.DEAD); - } - }, Threading.SAME_THREAD); - - // Fork like this: - // - // genesis -> b1 -> b2 -> b3 -> [...] - // \-> b4 -> b5 -> b6 -> [...] - // - // The b4/ b5/ b6 is now the best chain - Block b4 = b1.createNextBlock(someOtherGuy); - Block b5 = b4.createNextBlock(someOtherGuy); - Block b6 = b5.createNextBlock(someOtherGuy); - - log.debug("Adding block b4"); - assertTrue(chain.add(b4)); - log.debug("Adding block b5"); - assertTrue(chain.add(b5)); - log.debug("Adding block b6"); - assertTrue(chain.add(b6)); - - Block secondTip = b6; - for (int i = 0; i < unitTestParams.getSpendableCoinbaseDepth() - 2; i++) { - secondTip = secondTip.createNextBlock(someOtherGuy); - chain.add(secondTip); - } - - // Transaction 1 (in block b2) is now on a side chain and should have confidence type of dead and be in - // the dead pool only. - assertEquals(TransactionConfidence.ConfidenceType.DEAD, coinbase.getConfidence().getConfidenceType()); - assertTrue(!wallet.pending.containsKey(coinbase.getHash())); - assertTrue(!wallet.unspent.containsKey(coinbase.getHash())); - assertTrue(!wallet.spent.containsKey(coinbase.getHash())); - assertTrue(wallet.dead.containsKey(coinbase.getHash())); - assertTrue(fodderIsDead.get()); - - // ... and back to the first chain. - Block b7 = firstTip.createNextBlock(someOtherGuy); - Block b8 = b7.createNextBlock(someOtherGuy); - - log.debug("Adding block b7"); - assertTrue(chain.add(b7)); - log.debug("Adding block b8"); - assertTrue(chain.add(b8)); - - // - // genesis -> b1 -> b2 -> b3 -> [...] -> b7 -> b8 - // \-> b4 -> b5 -> b6 -> [...] - // - - // The coinbase transaction should now have confidence type of building once more and in the unspent pool only. - assertEquals(TransactionConfidence.ConfidenceType.BUILDING, coinbase.getConfidence().getConfidenceType()); - assertTrue(!wallet.pending.containsKey(coinbase.getHash())); - assertTrue(wallet.unspent.containsKey(coinbase.getHash())); - assertTrue(!wallet.spent.containsKey(coinbase.getHash())); - assertTrue(!wallet.dead.containsKey(coinbase.getHash())); - // However, fodder is still dead. Bitcoin Core doesn't keep killed transactions around in case they become - // valid again later. They are just deleted from the mempool for good. - - // ... make the side chain dominant again. - Block b9 = secondTip.createNextBlock(someOtherGuy); - Block b10 = b9.createNextBlock(someOtherGuy); - - log.debug("Adding block b9"); - assertTrue(chain.add(b9)); - log.debug("Adding block b10"); - assertTrue(chain.add(b10)); - - // - // genesis -> b1 -> b2 -> b3 -> [...] -> b7 -> b8 - // \-> b4 -> b5 -> b6 -> [...] -> b9 -> b10 - // - - // The coinbase transaction should now have the confidence type of dead and be in the dead pool only. - assertEquals(TransactionConfidence.ConfidenceType.DEAD, coinbase.getConfidence().getConfidenceType()); - assertTrue(!wallet.pending.containsKey(coinbase.getHash())); - assertTrue(!wallet.unspent.containsKey(coinbase.getHash())); - assertTrue(!wallet.spent.containsKey(coinbase.getHash())); - assertTrue(wallet.dead.containsKey(coinbase.getHash())); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/CoinTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/CoinTest.java deleted file mode 100644 index d8adf36b..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/CoinTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.core.NetworkParameters.MAX_MONEY; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import org.junit.Assert; -import org.junit.Test; - -public class CoinTest { - - @Test - public void testParseCoin() { - // String version - assertEquals(CENT, parseCoin("0.01")); - assertEquals(CENT, parseCoin("1E-2")); - assertEquals(COIN.add(CENT), parseCoin("1.01")); - assertEquals(COIN.negate(), parseCoin("-1")); - try { - parseCoin("2E-20"); - org.junit.Assert.fail("should not have accepted fractional satoshis"); - } catch (IllegalArgumentException expected) { - } catch (Exception e) { - org.junit.Assert.fail("should throw IllegalArgumentException"); - } - } - - @Test - public void testValueOf() { - // int version - assertEquals(CENT, valueOf(0, 1)); - assertEquals(SATOSHI, valueOf(1)); - assertEquals(NEGATIVE_SATOSHI, valueOf(-1)); - assertEquals(MAX_MONEY, valueOf(MAX_MONEY.value)); - assertEquals(MAX_MONEY.negate(), valueOf(MAX_MONEY.value * -1)); - try { - valueOf(MAX_MONEY.value + 1); - org.junit.Assert.fail("should not have accepted too-great a monetary value"); - } catch (IllegalArgumentException e) { - } - try { - valueOf( (MAX_MONEY.value * -1) - 1); - org.junit.Assert.fail("should not have accepted too-little a monetary value"); - } catch (IllegalArgumentException e) { - } - - try { - valueOf(Long.MIN_VALUE); - fail(); - } catch (IllegalArgumentException e) {} - - try { - valueOf(1, -1); - fail(); - } catch (IllegalArgumentException e) {} - try { - valueOf(-1, 0); - fail(); - } catch (IllegalArgumentException e) {} - } - - @Test - public void testOperators() { - assertTrue(SATOSHI.isPositive()); - assertFalse(SATOSHI.isNegative()); - assertFalse(SATOSHI.isZero()); - assertFalse(NEGATIVE_SATOSHI.isPositive()); - assertTrue(NEGATIVE_SATOSHI.isNegative()); - assertFalse(NEGATIVE_SATOSHI.isZero()); - assertFalse(ZERO.isPositive()); - assertFalse(ZERO.isNegative()); - assertTrue(ZERO.isZero()); - - assertTrue(valueOf(2).isGreaterThan(valueOf(1))); - assertFalse(valueOf(2).isGreaterThan(valueOf(2))); - assertFalse(valueOf(1).isGreaterThan(valueOf(2))); - assertTrue(valueOf(1).isLessThan(valueOf(2))); - assertFalse(valueOf(2).isLessThan(valueOf(2))); - assertFalse(valueOf(2).isLessThan(valueOf(1))); - } - - @Test - public void testToFriendlyString() { - assertEquals("1.00 DOGE", COIN.toFriendlyString()); - assertEquals("1.23 DOGE", valueOf(1, 23).toFriendlyString()); - assertEquals("0.001 DOGE", COIN.divide(1000).toFriendlyString()); - assertEquals("-1.23 DOGE", valueOf(1, 23).negate().toFriendlyString()); - } - - /** - * Test the bitcoinValueToPlainString amount formatter - */ - @Test - public void testToPlainString() { - assertEquals("0.0015", Coin.valueOf(150000).toPlainString()); - assertEquals("1.23", parseCoin("1.23").toPlainString()); - - assertEquals("0.1", parseCoin("0.1").toPlainString()); - assertEquals("1.1", parseCoin("1.1").toPlainString()); - assertEquals("21.12", parseCoin("21.12").toPlainString()); - assertEquals("321.123", parseCoin("321.123").toPlainString()); - assertEquals("4321.1234", parseCoin("4321.1234").toPlainString()); - assertEquals("54321.12345", parseCoin("54321.12345").toPlainString()); - assertEquals("654321.123456", parseCoin("654321.123456").toPlainString()); - assertEquals("7654321.1234567", parseCoin("7654321.1234567").toPlainString()); - - // More than MAX_MONEY. This will overlow to 0 - assertEquals("0", parseCoin(String.valueOf(Long.MAX_VALUE + 1)).toPlainString()); - - // check there are no trailing zeros - assertEquals("1", parseCoin("1.0").toPlainString()); - assertEquals("2", parseCoin("2.00").toPlainString()); - assertEquals("3", parseCoin("3.000").toPlainString()); - assertEquals("4", parseCoin("4.0000").toPlainString()); - assertEquals("5", parseCoin("5.00000").toPlainString()); - assertEquals("6", parseCoin("6.000000").toPlainString()); - assertEquals("7", parseCoin("7.0000000").toPlainString()); - assertEquals("8", parseCoin("8.00000000").toPlainString()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/CoinbaseBlockTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/CoinbaseBlockTest.java deleted file mode 100644 index 92823753..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/CoinbaseBlockTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2012 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.core.AbstractBlockChain.NewBlockType; -import com.dogecoin.dogecoinj.core.Wallet.BalanceType; -import com.dogecoin.dogecoinj.params.MainNetParams; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * Test that an example mainnet coinbase transactions can be added to a wallet ok. - */ -public class CoinbaseBlockTest { - static final NetworkParameters params = MainNetParams.get(); - - // The address for this private key is 1GqtGtn4fctXuKxsVzRPSLmYWN1YioLi9y. - private static final String MINING_PRIVATE_KEY = "5JDxPrBRghF1EvSBjDigywqfmAjpHPmTJxYtQTYJxJRHLLQA4mG"; - - private static final int BLOCK_OF_INTEREST = 169482; - private static final int BLOCK_LENGTH_AS_HEX = 37357; - private static final long BLOCK_NONCE = 3973947400L; - private static final Coin BALANCE_AFTER_BLOCK = Coin.valueOf(22223642); - - @Test - public void testReceiveCoinbaseTransaction() throws Exception { - // Block 169482 (hash 0000000000000756935f1ee9d5987857b604046f846d3df56d024cdb5f368665) - // contains coinbase transactions that are mining pool shares. - // The private key MINERS_KEY is used to check transactions are received by a wallet correctly. - - byte[] blockAsBytes = getBytes(getClass().getResourceAsStream("block169482.dat")); - - // Create block 169482. - Block block = new Block(params, blockAsBytes); - - // Check block. - assertNotNull(block); - block.verify(); - assertEquals(BLOCK_NONCE, block.getNonce()); - - StoredBlock storedBlock = new StoredBlock(block, BigInteger.ONE, BLOCK_OF_INTEREST); // Nonsense work - not used in test. - - // Create a wallet contain the miner's key that receives a spend from a coinbase. - ECKey miningKey = (new DumpedPrivateKey(params, MINING_PRIVATE_KEY)).getKey(); - assertNotNull(miningKey); - - Wallet wallet = new Wallet(params); - wallet.importKey(miningKey); - - // Initial balance should be zero by construction. - assertEquals(Coin.ZERO, wallet.getBalance()); - - // Give the wallet the first transaction in the block - this is the coinbase tx. - List transactions = block.getTransactions(); - assertNotNull(transactions); - wallet.receiveFromBlock(transactions.get(0), storedBlock, NewBlockType.BEST_CHAIN, 0); - - // Coinbase transaction should have been received successfully but be unavailable to spend (too young). - assertEquals(BALANCE_AFTER_BLOCK, wallet.getBalance(BalanceType.ESTIMATED)); - assertEquals(Coin.ZERO, wallet.getBalance(BalanceType.AVAILABLE)); - } - - /** - * Returns the contents of the InputStream as a byte array. - */ - private byte[] getBytes(InputStream inputStream) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - int numberRead; - byte[] data = new byte[BLOCK_LENGTH_AS_HEX]; - - while ((numberRead = inputStream.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, numberRead); - } - - buffer.flush(); - - return buffer.toByteArray(); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/ECKeyTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/ECKeyTest.java deleted file mode 100644 index 09d765af..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/ECKeyTest.java +++ /dev/null @@ -1,464 +0,0 @@ -/** - * Copyright 2011 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.core.ECKey.ECDSASignature; -import com.dogecoin.dogecoinj.crypto.EncryptedData; -import com.dogecoin.dogecoinj.crypto.KeyCrypter; -import com.dogecoin.dogecoinj.crypto.KeyCrypterScrypt; -import com.dogecoin.dogecoinj.crypto.TransactionSignature; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.ByteString; -import com.dogecoin.dogecoinj.wallet.Protos; -import com.dogecoin.dogecoinj.wallet.Protos.ScryptParameters; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.spongycastle.crypto.params.KeyParameter; - -import java.io.InputStream; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.security.SignatureException; -import java.util.Arrays; -import java.util.List; -import java.util.Random; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static com.dogecoin.dogecoinj.core.Utils.reverseBytes; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.*; - -public class ECKeyTest { - private static final Logger log = LoggerFactory.getLogger(ECKeyTest.class); - - private SecureRandom secureRandom; - - private KeyCrypter keyCrypter; - - private static CharSequence PASSWORD1 = "my hovercraft has eels"; - private static CharSequence WRONG_PASSWORD = "it is a snowy day today"; - - @Before - public void setUp() throws Exception { - secureRandom = new SecureRandom(); - - byte[] salt = new byte[KeyCrypterScrypt.SALT_LENGTH]; - secureRandom.nextBytes(salt); - Protos.ScryptParameters.Builder scryptParametersBuilder = Protos.ScryptParameters.newBuilder().setSalt(ByteString.copyFrom(salt)); - ScryptParameters scryptParameters = scryptParametersBuilder.build(); - keyCrypter = new KeyCrypterScrypt(scryptParameters); - - BriefLogFormatter.init(); - } - - @Test - public void sValue() throws Exception { - // Check that we never generate an S value that is larger than half the curve order. This avoids a malleability - // issue that can allow someone to change a transaction [hash] without invalidating the signature. - final int ITERATIONS = 10; - ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ITERATIONS)); - List> sigFutures = Lists.newArrayList(); - final ECKey key = new ECKey(); - for (byte i = 0; i < ITERATIONS; i++) { - final Sha256Hash hash = Sha256Hash.create(new byte[]{i}); - sigFutures.add(executor.submit(new Callable() { - @Override - public ECKey.ECDSASignature call() throws Exception { - return key.sign(hash); - } - })); - } - List sigs = Futures.allAsList(sigFutures).get(); - for (ECKey.ECDSASignature signature : sigs) { - assertTrue(signature.isCanonical()); - } - final ECDSASignature first = sigs.get(0); - final ECKey.ECDSASignature duplicate = new ECKey.ECDSASignature(first.r, first.s); - assertEquals(first, duplicate); - assertEquals(first.hashCode(), duplicate.hashCode()); - - final ECKey.ECDSASignature highS = new ECKey.ECDSASignature(first.r, ECKey.CURVE.getN().subtract(first.s)); - assertFalse(highS.isCanonical()); - } - - @Test - public void testSignatures() throws Exception { - // 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 = ECKey.fromPrivate(privkey); - byte[] output = key.sign(Sha256Hash.ZERO_HASH).encodeToDER(); - assertTrue(key.verify(Sha256Hash.ZERO_HASH.getBytes(), output)); - - // Test interop with a signature from elsewhere. - byte[] sig = HEX.decode( - "3046022100dffbc26774fc841bbe1c1362fd643609c6e42dcb274763476d87af2c0597e89e022100c59e3c13b96b316cae9fa0ab0260612c7a133a6fe2b3445b6bf80b3123bf274d"); - assertTrue(key.verify(Sha256Hash.ZERO_HASH.getBytes(), sig)); - } - - @Test - public void testASN1Roundtrip() throws Exception { - byte[] privkeyASN1 = HEX.decode( - "3082011302010104205c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4aa081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200042af7a2aafe8dafd7dc7f9cfb58ce09bda7dce28653ab229b98d1d3d759660c672dd0db18c8c2d76aa470448e876fc2089ab1354c01a6e72cefc50915f4a963ee"); - ECKey decodedKey = ECKey.fromASN1(privkeyASN1); - - // Now re-encode and decode the ASN.1 to see if it is equivalent (it does not produce the exact same byte - // sequence, some integers are padded now). - ECKey roundtripKey = ECKey.fromASN1(decodedKey.toASN1()); - - assertArrayEquals(decodedKey.getPrivKeyBytes(), roundtripKey.getPrivKeyBytes()); - - for (ECKey key : new ECKey[] {decodedKey, roundtripKey}) { - byte[] message = reverseBytes(HEX.decode( - "11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a")); - byte[] output = key.sign(new Sha256Hash(message)).encodeToDER(); - assertTrue(key.verify(message, output)); - - output = HEX.decode( - "304502206faa2ebc614bf4a0b31f0ce4ed9012eb193302ec2bcaccc7ae8bb40577f47549022100c73a1a1acc209f3f860bf9b9f5e13e9433db6f8b7bd527a088a0e0cd0a4c83e9"); - assertTrue(key.verify(message, output)); - } - - // Try to sign with one key and verify with the other. - byte[] message = reverseBytes(HEX.decode( - "11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a")); - assertTrue(roundtripKey.verify(message, decodedKey.sign(new Sha256Hash(message)).encodeToDER())); - assertTrue(decodedKey.verify(message, roundtripKey.sign(new Sha256Hash(message)).encodeToDER())); - } - - @Test - public void testKeyPairRoundtrip() throws Exception { - byte[] privkeyASN1 = HEX.decode( - "3082011302010104205c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4aa081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200042af7a2aafe8dafd7dc7f9cfb58ce09bda7dce28653ab229b98d1d3d759660c672dd0db18c8c2d76aa470448e876fc2089ab1354c01a6e72cefc50915f4a963ee"); - ECKey decodedKey = ECKey.fromASN1(privkeyASN1); - - // Now re-encode and decode the ASN.1 to see if it is equivalent (it does not produce the exact same byte - // sequence, some integers are padded now). - ECKey roundtripKey = - ECKey.fromPrivateAndPrecalculatedPublic(decodedKey.getPrivKey(), decodedKey.getPubKeyPoint()); - - for (ECKey key : new ECKey[] {decodedKey, roundtripKey}) { - byte[] message = reverseBytes(HEX.decode( - "11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a")); - byte[] output = key.sign(new Sha256Hash(message)).encodeToDER(); - assertTrue(key.verify(message, output)); - - output = HEX.decode( - "304502206faa2ebc614bf4a0b31f0ce4ed9012eb193302ec2bcaccc7ae8bb40577f47549022100c73a1a1acc209f3f860bf9b9f5e13e9433db6f8b7bd527a088a0e0cd0a4c83e9"); - assertTrue(key.verify(message, output)); - } - - // Try to sign with one key and verify with the other. - byte[] message = reverseBytes(HEX.decode( - "11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a")); - assertTrue(roundtripKey.verify(message, decodedKey.sign(new Sha256Hash(message)).encodeToDER())); - assertTrue(decodedKey.verify(message, roundtripKey.sign(new Sha256Hash(message)).encodeToDER())); - - // Verify bytewise equivalence of public keys (i.e. compression state is preserved) - ECKey key = new ECKey(); - ECKey key2 = ECKey.fromASN1(key.toASN1()); - assertArrayEquals(key.getPubKey(), key2.getPubKey()); - } - - @Test - public void base58Encoding() throws Exception { - String addr = "mqAJmaxMcG5pPHHc3H3NtyXzY7kGbJLuMF"; - String privkey = "92shANodC6Y4evT5kFzjNFQAdjqTtHAnDTLzqBBq4BbKUPyx6CD"; - ECKey key = new DumpedPrivateKey(TestNet3Params.get(), privkey).getKey(); - assertEquals(privkey, key.getPrivateKeyEncoded(TestNet3Params.get()).toString()); - assertEquals(addr, key.toAddress(TestNet3Params.get()).toString()); - } - - @Test - public void base58Encoding_leadingZero() throws Exception { - String privkey = "91axuYLa8xK796DnBXXsMbjuc8pDYxYgJyQMvFzrZ6UfXaGYuqL"; - ECKey key = new DumpedPrivateKey(TestNet3Params.get(), privkey).getKey(); - assertEquals(privkey, key.getPrivateKeyEncoded(TestNet3Params.get()).toString()); - assertEquals(0, key.getPrivKeyBytes()[0]); - } - - @Test - public void base58Encoding_stress() throws Exception { - // Replace the loop bound with 1000 to get some keys with leading zero byte - for (int i = 0 ; i < 20 ; i++) { - ECKey key = new ECKey(); - ECKey key1 = new DumpedPrivateKey(TestNet3Params.get(), - key.getPrivateKeyEncoded(TestNet3Params.get()).toString()).getKey(); - assertEquals(Utils.HEX.encode(key.getPrivKeyBytes()), - Utils.HEX.encode(key1.getPrivKeyBytes())); - } - } - - @Test - public void signTextMessage() throws Exception { - ECKey key = new ECKey(); - String message = "聡中本"; - String signatureBase64 = key.signMessage(message); - log.info("Message signed with " + key.toAddress(MainNetParams.get()) + ": " + signatureBase64); - // Should verify correctly. - key.verifyMessage(message, signatureBase64); - try { - key.verifyMessage("Evil attacker says hello!", signatureBase64); - fail(); - } catch (SignatureException e) { - // OK. - } - } - - @Test - public void verifyMessage() throws Exception { - // Test vector generated by Bitcoin-Qt. - String message = "hello"; - String sigBase64 = "HxNZdo6ggZ41hd3mM3gfJRqOQPZYcO8z8qdX2BwmpbF11CaOQV+QiZGGQxaYOncKoNW61oRuSMMF8udfK54XqI8="; - Address expectedAddress = new Address(MainNetParams.get(), "14YPSNPi6NSXnUxtPAsyJSuw3pv7AU3Cag"); - ECKey key = ECKey.signedMessageToKey(message, sigBase64); - Address gotAddress = key.toAddress(MainNetParams.get()); - assertEquals(expectedAddress, gotAddress); - } - - @Test - public void keyRecovery() throws Exception { - ECKey key = new ECKey(); - String message = "Hello World!"; - Sha256Hash hash = Sha256Hash.create(message.getBytes()); - ECKey.ECDSASignature sig = key.sign(hash); - key = ECKey.fromPublicOnly(key.getPubKeyPoint()); - boolean found = false; - for (int i = 0; i < 4; i++) { - ECKey key2 = ECKey.recoverFromSignature(i, sig, hash, true); - checkNotNull(key2); - if (key.equals(key2)) { - found = true; - break; - } - } - assertTrue(found); - } - - @Test - public void testUnencryptedCreate() throws Exception { - Utils.setMockClock(); - ECKey key = new ECKey(); - long time = key.getCreationTimeSeconds(); - assertNotEquals(0, time); - assertTrue(!key.isEncrypted()); - byte[] originalPrivateKeyBytes = key.getPrivKeyBytes(); - ECKey encryptedKey = key.encrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1)); - assertEquals(time, encryptedKey.getCreationTimeSeconds()); - assertTrue(encryptedKey.isEncrypted()); - assertNull(encryptedKey.getSecretBytes()); - key = encryptedKey.decrypt(keyCrypter.deriveKey(PASSWORD1)); - assertTrue(!key.isEncrypted()); - assertArrayEquals(originalPrivateKeyBytes, key.getPrivKeyBytes()); - } - - @Test - public void testEncryptedCreate() throws Exception { - ECKey unencryptedKey = new ECKey(); - byte[] originalPrivateKeyBytes = checkNotNull(unencryptedKey.getPrivKeyBytes()); - log.info("Original private key = " + Utils.HEX.encode(originalPrivateKeyBytes)); - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(unencryptedKey.getPrivKeyBytes(), keyCrypter.deriveKey(PASSWORD1)); - ECKey encryptedKey = ECKey.fromEncrypted(encryptedPrivateKey, keyCrypter, unencryptedKey.getPubKey()); - assertTrue(encryptedKey.isEncrypted()); - assertNull(encryptedKey.getSecretBytes()); - ECKey rebornUnencryptedKey = encryptedKey.decrypt(keyCrypter.deriveKey(PASSWORD1)); - assertTrue(!rebornUnencryptedKey.isEncrypted()); - assertArrayEquals(originalPrivateKeyBytes, rebornUnencryptedKey.getPrivKeyBytes()); - } - - @Test - public void testEncryptionIsReversible() throws Exception { - ECKey originalUnencryptedKey = new ECKey(); - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(originalUnencryptedKey.getPrivKeyBytes(), keyCrypter.deriveKey(PASSWORD1)); - ECKey encryptedKey = ECKey.fromEncrypted(encryptedPrivateKey, keyCrypter, originalUnencryptedKey.getPubKey()); - - // The key should be encrypted - assertTrue("Key not encrypted at start", encryptedKey.isEncrypted()); - - // Check that the key can be successfully decrypted back to the original. - assertTrue("Key encryption is not reversible but it should be", ECKey.encryptionIsReversible(originalUnencryptedKey, encryptedKey, keyCrypter, keyCrypter.deriveKey(PASSWORD1))); - - // Check that key encryption is not reversible if a password other than the original is used to generate the AES key. - assertTrue("Key encryption is reversible with wrong password", !ECKey.encryptionIsReversible(originalUnencryptedKey, encryptedKey, keyCrypter, keyCrypter.deriveKey(WRONG_PASSWORD))); - - // Change one of the encrypted key bytes (this is to simulate a faulty keyCrypter). - // Encryption should not be reversible - byte[] goodEncryptedPrivateKeyBytes = encryptedPrivateKey.encryptedBytes; - - // Break the encrypted private key and check it is broken. - byte[] badEncryptedPrivateKeyBytes = new byte[goodEncryptedPrivateKeyBytes.length]; - encryptedPrivateKey = new EncryptedData(encryptedPrivateKey.initialisationVector, badEncryptedPrivateKeyBytes); - ECKey badEncryptedKey = ECKey.fromEncrypted(encryptedPrivateKey, keyCrypter, originalUnencryptedKey.getPubKey()); - assertTrue("Key encryption is reversible with faulty encrypted bytes", !ECKey.encryptionIsReversible(originalUnencryptedKey, badEncryptedKey, keyCrypter, keyCrypter.deriveKey(PASSWORD1))); - } - - @Test - public void testToString() throws Exception { - ECKey key = ECKey.fromPrivate(BigInteger.TEN).decompress(); // An example private key. - NetworkParameters params = MainNetParams.get(); - assertEquals("ECKey{pub HEX=04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7, isEncrypted=false, isPubKeyOnly=false}", key.toString()); - assertEquals("ECKey{pub HEX=04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7, priv HEX=000000000000000000000000000000000000000000000000000000000000000a, priv WIF=5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreBoNWTw6, isEncrypted=false, isPubKeyOnly=false}", key.toStringWithPrivate(params)); - } - - @Test - public void testGetPrivateKeyAsHex() throws Exception { - ECKey key = ECKey.fromPrivate(BigInteger.TEN).decompress(); // An example private key. - assertEquals("000000000000000000000000000000000000000000000000000000000000000a", key.getPrivateKeyAsHex()); - } - - @Test - public void testGetPublicKeyAsHex() throws Exception { - ECKey key = ECKey.fromPrivate(BigInteger.TEN).decompress(); // An example private key. - assertEquals("04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7", key.getPublicKeyAsHex()); - } - - @Test - public void keyRecoveryWithEncryptedKey() throws Exception { - ECKey unencryptedKey = new ECKey(); - KeyParameter aesKey = keyCrypter.deriveKey(PASSWORD1); - ECKey encryptedKey = unencryptedKey.encrypt(keyCrypter, aesKey); - - String message = "Goodbye Jupiter!"; - Sha256Hash hash = Sha256Hash.create(message.getBytes()); - ECKey.ECDSASignature sig = encryptedKey.sign(hash, aesKey); - unencryptedKey = ECKey.fromPublicOnly(unencryptedKey.getPubKeyPoint()); - boolean found = false; - for (int i = 0; i < 4; i++) { - ECKey key2 = ECKey.recoverFromSignature(i, sig, hash, true); - checkNotNull(key2); - if (unencryptedKey.equals(key2)) { - found = true; - break; - } - } - assertTrue(found); - } - - @Test - public void roundTripDumpedPrivKey() throws Exception { - ECKey key = new ECKey(); - assertTrue(key.isCompressed()); - NetworkParameters params = UnitTestParams.get(); - String base58 = key.getPrivateKeyEncoded(params).toString(); - ECKey key2 = new DumpedPrivateKey(params, base58).getKey(); - assertTrue(key2.isCompressed()); - assertTrue(Arrays.equals(key.getPrivKeyBytes(), key2.getPrivKeyBytes())); - assertTrue(Arrays.equals(key.getPubKey(), key2.getPubKey())); - } - - @Test - public void clear() throws Exception { - ECKey unencryptedKey = new ECKey(); - ECKey encryptedKey = (new ECKey()).encrypt(keyCrypter, keyCrypter.deriveKey(PASSWORD1)); - - checkSomeBytesAreNonZero(unencryptedKey.getPrivKeyBytes()); - - // The encryptedPrivateKey should be null in an unencrypted ECKey anyhow but check all the same. - assertTrue(unencryptedKey.getEncryptedPrivateKey() == null); - - checkSomeBytesAreNonZero(encryptedKey.getSecretBytes()); - checkSomeBytesAreNonZero(encryptedKey.getEncryptedPrivateKey().encryptedBytes); - checkSomeBytesAreNonZero(encryptedKey.getEncryptedPrivateKey().initialisationVector); - } - - @Test - public void testCanonicalSigs() throws Exception { - // Tests the canonical sigs from the reference client unit tests - InputStream in = getClass().getResourceAsStream("sig_canonical.json"); - - // Poor man's JSON parser (because pulling in a lib for this is overkill) - while (in.available() > 0) { - while (in.available() > 0 && in.read() != '"') ; - if (in.available() < 1) - break; - - StringBuilder sig = new StringBuilder(); - int c; - while (in.available() > 0 && (c = in.read()) != '"') - sig.append((char)c); - - assertTrue(TransactionSignature.isEncodingCanonical(HEX.decode(sig.toString()))); - } - in.close(); - } - - @Test - public void testNonCanonicalSigs() throws Exception { - // Tests the noncanonical sigs from the reference client unit tests - InputStream in = getClass().getResourceAsStream("sig_noncanonical.json"); - - // Poor man's JSON parser (because pulling in a lib for this is overkill) - while (in.available() > 0) { - while (in.available() > 0 && in.read() != '"') ; - if (in.available() < 1) - break; - - StringBuilder sig = new StringBuilder(); - int c; - while (in.available() > 0 && (c = in.read()) != '"') - sig.append((char)c); - - try { - final String sigStr = sig.toString(); - assertFalse(TransactionSignature.isEncodingCanonical(HEX.decode(sigStr))); - } catch (IllegalArgumentException e) { - // Expected for non-hex strings in the JSON that we should ignore - } - } - in.close(); - } - - @Test - public void testCreatedSigAndPubkeyAreCanonical() throws Exception { - // Tests that we will not generate non-canonical pubkeys or signatures - // We dump failed data to error log because this test is not expected to be deterministic - ECKey key = new ECKey(); - if (!ECKey.isPubKeyCanonical(key.getPubKey())) { - log.error(Utils.HEX.encode(key.getPubKey())); - fail(); - } - - byte[] hash = new byte[32]; - new Random().nextBytes(hash); - byte[] sigBytes = key.sign(new Sha256Hash(hash)).encodeToDER(); - byte[] encodedSig = Arrays.copyOf(sigBytes, sigBytes.length + 1); - encodedSig[sigBytes.length] = (byte) (Transaction.SigHash.ALL.ordinal() + 1); - if (!TransactionSignature.isEncodingCanonical(encodedSig)) { - log.error(Utils.HEX.encode(sigBytes)); - fail(); - } - } - - private static boolean checkSomeBytesAreNonZero(byte[] bytes) { - if (bytes == null) return false; - for (byte b : bytes) if (b != 0) return true; - return false; - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/FilteredBlockAndPartialMerkleTreeTests.java b/core/src/test/java/com/dogecoin/dogecoinj/core/FilteredBlockAndPartialMerkleTreeTests.java deleted file mode 100644 index 1af8772d..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/FilteredBlockAndPartialMerkleTreeTests.java +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Copyright 2012 Matt Corallo - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.google.common.collect.Lists; -import com.dogecoin.dogecoinj.core.TransactionConfidence.ConfidenceType; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.store.MemoryBlockStore; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.testing.InboundMessageQueuer; -import com.dogecoin.dogecoinj.testing.TestWithPeerGroup; -import com.dogecoin.dogecoinj.wallet.KeyChainGroup; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -@RunWith(value = Parameterized.class) -public class FilteredBlockAndPartialMerkleTreeTests extends TestWithPeerGroup { - @Parameterized.Parameters - public static Collection parameters() { - return Arrays.asList(new ClientType[] {ClientType.NIO_CLIENT_MANAGER}, - new ClientType[] {ClientType.BLOCKING_CLIENT_MANAGER}); - } - - public FilteredBlockAndPartialMerkleTreeTests(ClientType clientType) { - super(clientType); - } - - @Test - public void deserializeFilteredBlock() throws Exception { - NetworkParameters params = UnitTestParams.get(); - - // Random real block (000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45) - // With one tx - FilteredBlock block = new FilteredBlock(params, HEX.decode("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101")); - - // Check that the header was properly deserialized - assertTrue(block.getBlockHeader().getHash().equals(new Sha256Hash("000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45"))); - - // Check that the partial merkle tree is correct - List txesMatched = block.getTransactionHashes(); - assertTrue(txesMatched.size() == 1); - assertTrue(txesMatched.contains(new Sha256Hash("63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"))); - - // Check round tripping. - assertEquals(block, new FilteredBlock(params, block.bitcoinSerialize())); - } - - @Test - public void createFilteredBlock() throws Exception { - ECKey key1 = new ECKey(); - ECKey key2 = new ECKey(); - Transaction tx1 = FakeTxBuilder.createFakeTx(params, Coin.COIN, key1); - Transaction tx2 = FakeTxBuilder.createFakeTx(params, Coin.FIFTY_COINS, key2.toAddress(params)); - Block block = FakeTxBuilder.makeSolvedTestBlock(params.getGenesisBlock(), new Address(params, "msg2t2V2sWNd85LccoddtWysBTR8oPnkzW"), tx1, tx2); - BloomFilter filter = new BloomFilter(4, 0.1, 1); - filter.insert(key1); - filter.insert(key2); - FilteredBlock filteredBlock = filter.applyAndUpdate(block); - assertEquals(4, filteredBlock.getTransactionCount()); - // This call triggers verification of the just created data. - List txns = filteredBlock.getTransactionHashes(); - assertTrue(txns.contains(tx1.getHash())); - assertTrue(txns.contains(tx2.getHash())); - } - - private Sha256Hash numAsHash(int num) { - byte[] bits = new byte[32]; - bits[0] = (byte) num; - return new Sha256Hash(bits); - } - - @Test(expected = VerificationException.class) - public void merkleTreeMalleability() throws Exception { - List hashes = Lists.newArrayList(); - for (byte i = 1; i <= 10; i++) hashes.add(numAsHash(i)); - hashes.add(numAsHash(9)); - hashes.add(numAsHash(10)); - byte[] includeBits = new byte[2]; - Utils.setBitLE(includeBits, 9); - Utils.setBitLE(includeBits, 10); - PartialMerkleTree pmt = PartialMerkleTree.buildFromLeaves(params, includeBits, hashes); - List matchedHashes = Lists.newArrayList(); - pmt.getTxnHashAndMerkleRoot(matchedHashes); - } - - @Test - public void serializeDownloadBlockWithWallet() throws Exception { - unitTestParams = UnitTestParams.get(); - - // First we create all the neccessary objects, including lots of serialization and double-checks - // Note that all serialized forms here are generated by the reference client/pulled from block explorer - Block block = new Block(unitTestParams, HEX.decode("0100000006e533fd1ada86391f3f6c343204b0d278d4aaec1c0b20aa27ba0300000000006abbb3eb3d733a9fe18967fd7d4c117e4ccbbac5bec4d910d900b3ae0793e77f54241b4d4c86041b4089cc9b0c01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b010dffffffff0100f2052a01000000434104b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63ac000000000100000001d992e5a888a86d4c7a6a69167a4728ee69497509740fc5f456a24528c340219a000000008b483045022100f0519bdc9282ff476da1323b8ef7ffe33f495c1a8d52cc522b437022d83f6a230220159b61d197fbae01b4a66622a23bc3f1def65d5fa24efd5c26fa872f3a246b8e014104839f9023296a1fabb133140128ca2709f6818c7d099491690bd8ac0fd55279def6a2ceb6ab7b5e4a71889b6e739f09509565eec789e86886f6f936fa42097adeffffffff02000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac00e32321000000001976a9140c34f4e29ab5a615d5ea28d4817f12b137d62ed588ac0000000001000000059daf0abe7a92618546a9dbcfd65869b6178c66ec21ccfda878c1175979cfd9ef000000004a493046022100c2f7f25be5de6ce88ac3c1a519514379e91f39b31ddff279a3db0b1a229b708b022100b29efbdbd9837cc6a6c7318aa4900ed7e4d65662c34d1622a2035a3a5534a99a01ffffffffd516330ebdf075948da56db13d22632a4fb941122df2884397dda45d451acefb0000000048473044022051243debe6d4f2b433bee0cee78c5c4073ead0e3bde54296dbed6176e128659c022044417bfe16f44eb7b6eb0cdf077b9ce972a332e15395c09ca5e4f602958d266101ffffffffe1f5aa33961227b3c344e57179417ce01b7ccd421117fe2336289b70489883f900000000484730440220593252bb992ce3c85baf28d6e3aa32065816271d2c822398fe7ee28a856bc943022066d429dd5025d3c86fd8fd8a58e183a844bd94aa312cefe00388f57c85b0ca3201ffffffffe207e83718129505e6a7484831442f668164ae659fddb82e9e5421a081fb90d50000000049483045022067cf27eb733e5bcae412a586b25a74417c237161a084167c2a0b439abfebdcb2022100efcc6baa6824b4c5205aa967e0b76d31abf89e738d4b6b014e788c9a8cccaf0c01ffffffffe23b8d9d80a9e9d977fab3c94dbe37befee63822443c3ec5ae5a713ede66c3940000000049483045022020f2eb35036666b1debe0d1d2e77a36d5d9c4e96c1dba23f5100f193dbf524790221008ce79bc1321fb4357c6daee818038d41544749127751726e46b2b320c8b565a201ffffffff0200ba1dd2050000001976a914366a27645806e817a6cd40bc869bdad92fe5509188ac40420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac0000000001000000010abad2dc0c9b4b1dbb023077da513f81e5a71788d8680fca98ef1c37356c459c000000004a493046022100a894e521c87b3dbe23007079db4ac2896e9e791f8b57317ba6c0d99a7becd27a022100bc40981393eafeb33e89079f857c728701a9af4523c3f857cd96a500f240780901ffffffff024026ee22010000001976a914d28f9cefb58c1f7a5f97aa6b79047585f58fbd4388acc0cb1707000000001976a9142229481696e417aa5f51ad751d8cd4c6a669e4fe88ac000000000100000001f66d89b3649e0b18d84db056930676cb81c0168042fc4324c3682e252ea9410d0000000048473044022038e0b55b37c9253bfeda59c76c0134530f91fb586d6eb21738a77a984f370a44022048d4d477aaf97ef9c8275bbc5cb19b9c8a0e9b1f9fdafdd39bc85bf6c2f04a4d01ffffffff024041a523010000001976a914955f70ac8792b48b7bd52b15413bd8500ecf32c888ac00f36f06000000001976a91486116d15f3dbb23a2b58346f36e6ec2d867eba2b88ac00000000010000000126c384984f63446a4f2be8dd6531ba9837bd5f2c3d37403c5f51fb9192ee754e010000008b48304502210083af8324456f052ff1b2597ff0e6a8cce8b006e379a410cf781be7874a2691c2022072259e2f7292960dea0ffc361bbad0b861f719beb8550476f22ce0f82c023449014104f3ed46a81cba02af0593e8572a9130adb0d348b538c829ccaaf8e6075b78439b2746a76891ce7ba71abbcbb7ca76e8a220782738a6789562827c1065b0ce911dffffffff02c0dd9107000000001976a91463d4dd1b29d95ed601512b487bfc1c49d84d057988ac00a0491a010000001976a91465746bef92511df7b34abf71c162efb7ae353de388ac0000000001000000011b56cf3aab3286d582c055a42af3a911ee08423f276da702bb67f1222ac1a5b6000000008c4930460221009e9fba682e162c9627b96b7df272006a727988680b956c61baff869f0907b8fb022100a9c19adc7c36144bafe526630783845e5cb9554d30d3edfb56f0740274d507f30141046e0efbfac7b1615ad553a6f097615bc63b7cdb3b8e1cb3263b619ba63740012f51c7c5b09390e3577e377b7537e61226e315f95f926444fc5e5f2978c112e448ffffffff02c0072b11010000001976a914b73e9e01933351ca076faf8e0d94dd58079d0b1f88ac80b63908000000001976a9141aca0bdf0d2cee63db19aa4a484f45a4e26a880c88ac000000000100000001251b187504ea873b2c3915fad401f7a7734cc13567e0417708e86294a29f4f68010000008b4830450221009bef423141ed1ae60d0a5bcaa57b1673fc96001f0d4e105535cca817ba5a7724022037c399bd30374f22481ffc81327cfca4951c7264b227f765fcd6a429f3d9d2080141044d0d1b4f194c31a73dbce41c42b4b3946849117c5bb320467e014bad3b1532f28a9a1568ba7108f188e7823b6e618e91d974306701379a27b9339e646e156e7bffffffff02c00fd103010000001976a914ef7f5d9e1bc6ed68cfe0b1db9d8f09cef0f3ba4a88ac004dd208000000001976a914c22420641cea028c9e06c4d9104c1646f8b1769088ac0000000001000000013486dd5f0a2f3efcc04f64cb03872c021f98ee39f514747ce5336b874bbe47a7010000008b48304502201cadddc2838598fee7dc35a12b340c6bde8b389f7bfd19a1252a17c4b5ed2d71022100c1a251bbecb14b058a8bd77f65de87e51c47e95904f4c0e9d52eddc21c1415ac014104fe7df86d58aafa9246ca6fd30c905714533c25f700e2329b8ecec8aa52083b844baa3a8acd5d6b9732dcb39079bb56ba2711a3580dec824955fce0596a460c11ffffffff02c011f6e1000000001976a91490fac83c9adde91d670dde8755f8b475ab9e427d88acc0f9df15000000001976a91437f691b3e8ee5dcb56c2e31af4c80caa2df3b09b88ac00000000010000000170016bd1274b795b262f32a53003a4714b22b62f9057adf5fbe6ed939003b5190100000089463043022061456499582170a94d6b54308f792e37dad28bf0ed7aa61021f0301d2774d378021f4224b33f707efd810a01dd34ea86d6069cd599cc435513a0eef8c83c137bf7014104a2c95d6b98e745448eb45ed0ba95cf24dd7c3b16386e1028e24a0358ee4afc33e2f0199139853edaf32845d8a42254c75f7dc8add3286c682c650fbd93f0a4a1ffffffff02001bd2b7000000001976a9141b11c6acaa5223013f3a3240fdb024ecd9f8135488ac8023ad18000000001976a914ada27ca87bbaa1ee6fb1cb61bb0a29baaf6da2c988ac000000000100000001c8ff91f031ec6a5aba4baee6549e61dd01f26f61b70e2f1574f24cd680f464ad000000008b48304502210082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3022024bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724014104174f9eef1157dc1ad5eac198250b70d1c3b04b2fca12ad1483f07358486f02909b088bbc83f4de55f767f6cdf9d424aa02b5eeaffa08394d39b717895fc08d0affffffff0200ea3b43000000001976a914fb32df708f0610901f6d1b6df8c9c368fe0d981c88ac800f1777000000001976a914462c501c70fb996d15ac0771e7fc8d3ca3f7201888ac000000000100000001c67323867de802402e780a70e0deba3c708c4d87497e17590afee9c321f1c680010000008a473044022042734b25f54845d662e6499b75ff8529ff47f42fd224498a9f752d212326dbfa0220523e4b7b570bbb1f3af02baa2c04ea8eb7b0fccb1522cced130b666ae9a9d014014104b5a23b922949877e9eaf7512897ed091958e2e8cf05b0d0eb9064e7976043fde6023b4e2c188b7e38ef94eec6845dc4933f5e8635f1f6a3702290956aa9e284bffffffff0280041838030000001976a91436e5884215f7d3044be5d37bdd8c987d9d942c8488ac404b4c00000000001976a91460085d6838f8a44a21a0de56ff963cfa6242a96188ac00000000")); - FilteredBlock filteredBlock = new FilteredBlock(unitTestParams, HEX.decode("0100000006e533fd1ada86391f3f6c343204b0d278d4aaec1c0b20aa27ba0300000000006abbb3eb3d733a9fe18967fd7d4c117e4ccbbac5bec4d910d900b3ae0793e77f54241b4d4c86041b4089cc9b0c000000084c30b63cfcdc2d35e3329421b9805ef0c6565d35381ca857762ea0b3a5a128bbca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefbbb15ac1d57d0182aaee61c74743a9c4f785895e563909bafec45c9a2b0ff3181d77706be8b1dcc91112eada86d424e2d0a8907c3488b6e44fda5a74a25cbc7d6bb4fa04245f4ac8a1a571d5537eac24adca1454d65eda446055479af6c6d4dd3c9ab658448c10b6921b7a4ce3021eb22ed6bb6a7fde1e5bcc4b1db6615c6abc5ca042127bfaf9f44ebce29cb29c6df9d05b47f35b2edff4f0064b578ab741fa78276222651209fe1a2c4c0fa1c58510aec8b090dd1eb1f82f9d261b8273b525b02ff1a")); - - // Block 100001 - assertTrue(block.getHash().equals(new Sha256Hash("00000000000080b66c911bd5ba14a74260057311eaeb1982802f7010f1a9f090"))); - assertTrue(filteredBlock.getHash().equals(block.getHash())); - - List txHashList = filteredBlock.getTransactionHashes(); - assertTrue(txHashList.size() == 4); - // Four transactions (0, 1, 2, 6) from block 100001 - Transaction tx0 = new Transaction(unitTestParams, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b010dffffffff0100f2052a01000000434104b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63ac00000000")); - assertTrue(tx0.getHash().equals(new Sha256Hash("bb28a1a5b3a02e7657a81c38355d56c6f05e80b9219432e3352ddcfc3cb6304c"))); - assertEquals(tx0.getHash(), txHashList.get(0)); - - Transaction tx1 = new Transaction(unitTestParams, HEX.decode("0100000001d992e5a888a86d4c7a6a69167a4728ee69497509740fc5f456a24528c340219a000000008b483045022100f0519bdc9282ff476da1323b8ef7ffe33f495c1a8d52cc522b437022d83f6a230220159b61d197fbae01b4a66622a23bc3f1def65d5fa24efd5c26fa872f3a246b8e014104839f9023296a1fabb133140128ca2709f6818c7d099491690bd8ac0fd55279def6a2ceb6ab7b5e4a71889b6e739f09509565eec789e86886f6f936fa42097adeffffffff02000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac00e32321000000001976a9140c34f4e29ab5a615d5ea28d4817f12b137d62ed588ac00000000")); - assertTrue(tx1.getHash().equals(new Sha256Hash("fbde5d03b027d2b9ba4cf5d4fecab9a99864df2637b25ea4cbcb1796ff6550ca"))); - assertEquals(tx1.getHash(), txHashList.get(1)); - - Transaction tx2 = new Transaction(unitTestParams, HEX.decode("01000000059daf0abe7a92618546a9dbcfd65869b6178c66ec21ccfda878c1175979cfd9ef000000004a493046022100c2f7f25be5de6ce88ac3c1a519514379e91f39b31ddff279a3db0b1a229b708b022100b29efbdbd9837cc6a6c7318aa4900ed7e4d65662c34d1622a2035a3a5534a99a01ffffffffd516330ebdf075948da56db13d22632a4fb941122df2884397dda45d451acefb0000000048473044022051243debe6d4f2b433bee0cee78c5c4073ead0e3bde54296dbed6176e128659c022044417bfe16f44eb7b6eb0cdf077b9ce972a332e15395c09ca5e4f602958d266101ffffffffe1f5aa33961227b3c344e57179417ce01b7ccd421117fe2336289b70489883f900000000484730440220593252bb992ce3c85baf28d6e3aa32065816271d2c822398fe7ee28a856bc943022066d429dd5025d3c86fd8fd8a58e183a844bd94aa312cefe00388f57c85b0ca3201ffffffffe207e83718129505e6a7484831442f668164ae659fddb82e9e5421a081fb90d50000000049483045022067cf27eb733e5bcae412a586b25a74417c237161a084167c2a0b439abfebdcb2022100efcc6baa6824b4c5205aa967e0b76d31abf89e738d4b6b014e788c9a8cccaf0c01ffffffffe23b8d9d80a9e9d977fab3c94dbe37befee63822443c3ec5ae5a713ede66c3940000000049483045022020f2eb35036666b1debe0d1d2e77a36d5d9c4e96c1dba23f5100f193dbf524790221008ce79bc1321fb4357c6daee818038d41544749127751726e46b2b320c8b565a201ffffffff0200ba1dd2050000001976a914366a27645806e817a6cd40bc869bdad92fe5509188ac40420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac00000000")); - assertTrue(tx2.getHash().equals(new Sha256Hash("8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb"))); - assertEquals(tx2.getHash(), txHashList.get(2)); - - - Transaction tx3 = new Transaction(unitTestParams, HEX.decode("01000000011b56cf3aab3286d582c055a42af3a911ee08423f276da702bb67f1222ac1a5b6000000008c4930460221009e9fba682e162c9627b96b7df272006a727988680b956c61baff869f0907b8fb022100a9c19adc7c36144bafe526630783845e5cb9554d30d3edfb56f0740274d507f30141046e0efbfac7b1615ad553a6f097615bc63b7cdb3b8e1cb3263b619ba63740012f51c7c5b09390e3577e377b7537e61226e315f95f926444fc5e5f2978c112e448ffffffff02c0072b11010000001976a914b73e9e01933351ca076faf8e0d94dd58079d0b1f88ac80b63908000000001976a9141aca0bdf0d2cee63db19aa4a484f45a4e26a880c88ac00000000")); - assertTrue(tx3.getHash().equals(new Sha256Hash("c5abc61566dbb1c4bce5e1fda7b66bed22eb2130cea4b721690bc1488465abc9"))); - assertEquals(tx3.getHash(),txHashList.get(3)); - - // A wallet which contains a pubkey used in each transaction from above - KeyChainGroup group = new KeyChainGroup(unitTestParams); - group.importKeys(ECKey.fromPublicOnly(HEX.decode("04b27f7e9475ccf5d9a431cb86d665b8302c140144ec2397fce792f4a4e7765fecf8128534eaa71df04f93c74676ae8279195128a1506ebf7379d23dab8fca0f63")), - ECKey.fromPublicOnly(HEX.decode("04732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ec")), - ECKey.fromPublicOnly(HEX.decode("04cfb4113b3387637131ebec76871fd2760fc430dd16de0110f0eb07bb31ffac85e2607c189cb8582ea1ccaeb64ffd655409106589778f3000fdfe3263440b0350")), - ECKey.fromPublicOnly(HEX.decode("04b2f30018908a59e829c1534bfa5010d7ef7f79994159bba0f534d863ef9e4e973af6a8de20dc41dbea50bc622263ec8a770b2c9406599d39e4c9afe61f8b1613"))); - Wallet wallet = new Wallet(unitTestParams, group); - - BloomFilter filter = wallet.getBloomFilter(wallet.getKeychainSize()*2, 0.001, 0xDEADBEEF); - // Compare the serialized bloom filter to a known-good value - assertTrue(Arrays.equals(filter.bitcoinSerialize(), HEX.decode("0e1b091ca195e45a9164889b6bc46a09000000efbeadde02"))); - - // Cheat and place the previous block (block 100000) at the head of the block store without supporting blocks - blockStore = new MemoryBlockStore(UnitTestParams.get()); - blockStore.put(new StoredBlock(new Block(unitTestParams, HEX.decode("0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f337221b4d4c86041b0f2b5710")), - BigInteger.valueOf(1), 100000)); - blockStore.setChainHead(blockStore.get(new Sha256Hash("000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"))); - super.setUp(blockStore); - - peerGroup.addWallet(wallet); - peerGroup.setUseLocalhostPeerWhenPossible(false); // Prevents from connecting to bitcoin nodes on localhost. - - blockChain.addWallet(wallet); - - peerGroup.start(); - - // Create a peer. - InboundMessageQueuer p1 = connectPeer(1); - assertEquals(1, peerGroup.numConnectedPeers()); - // Send an inv for block 100001 - InventoryMessage inv = new InventoryMessage(unitTestParams); - inv.addBlock(block); - inbound(p1, inv); - - // Check that we properly requested the correct FilteredBlock - Object getData = outbound(p1); - assertTrue(getData instanceof GetDataMessage); - assertTrue(((GetDataMessage)getData).getItems().size() == 1); - assertTrue(((GetDataMessage)getData).getItems().get(0).hash.equals(block.getHash())); - assertTrue(((GetDataMessage)getData).getItems().get(0).type == InventoryItem.Type.FilteredBlock); - - // Check that we then immediately pinged. - Object ping = outbound(p1); - assertTrue(ping instanceof Ping); - - // Respond with transactions and the filtered block - inbound(p1, filteredBlock); - inbound(p1, tx0); - inbound(p1, tx1); - inbound(p1, tx2); - inbound(p1, tx3); - inbound(p1, new Pong(((Ping)ping).getNonce())); - - pingAndWait(p1); - - Set transactions = wallet.getTransactions(false); - assertTrue(transactions.size() == 4); - for (Transaction tx : transactions) { - assertTrue(tx.getConfidence().getConfidenceType() == ConfidenceType.BUILDING); - assertTrue(tx.getConfidence().getDepthInBlocks() == 1); - assertTrue(tx.getAppearsInHashes().keySet().contains(block.getHash())); - assertTrue(tx.getAppearsInHashes().size() == 1); - } - - // Peer 1 goes away. - closePeer(peerOf(p1)); - super.tearDown(); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/FullBlockTestGenerator.java b/core/src/test/java/com/dogecoin/dogecoinj/core/FullBlockTestGenerator.java deleted file mode 100644 index ab0b201f..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/FullBlockTestGenerator.java +++ /dev/null @@ -1,1869 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.dogecoin.dogecoinj.core.Transaction.SigHash; -import com.dogecoin.dogecoinj.crypto.TransactionSignature; -import com.dogecoin.dogecoinj.script.Script; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.google.common.base.Preconditions; - -import javax.annotation.Nullable; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.util.*; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.*; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -/** - * YOU ARE READING THIS CODE BECAUSE EITHER... - * - * a) You are testing an alternative implementation with full validation rules. If you are doing this, you should go - * rethink your life. Seriously, why are you reimplementing Bitcoin consensus rules? Instead, go work on making - * Bitcoin Core consensus rules a shared library and use that. Seriously, you wont get it right, and starting with - * this tester as a way to try to do so will simply end in pain and lost coins. SERIOUSLY, JUST STOP! - * - * b) Bitcoin Core is faling some test in here and you're wondering what test is causing failure. Just stop. There is no - * hope trying to read this file and decipher it. Give up and ping BlueMatt. Seriously, this stuff is a huge mess. - * - * c) You are trying to add a new test. STOP! WHY THE HELL WOULD YOU EVEN DO THAT? GO REWRITE THIS TESTER! - * - * d) You are BlueMatt and you're trying to hack more crap onto this multi-headed lopsided Proof Of Stake. Why are you - * doing this? Seriously, why have you not rewritten this thing yet? WTF man... - * - * IN ANY CASE, STOP READING NOW. IT WILL SAVE YOU MUCH PAIN AND MISERY LATER - */ - -class NewBlock { - public Block block; - private TransactionOutPointWithValue spendableOutput; - public NewBlock(Block block, TransactionOutPointWithValue spendableOutput) { - this.block = block; this.spendableOutput = spendableOutput; - } - // Wrappers to make it more block-like - public Sha256Hash getHash() { return block.getHash(); } - public void solve() { block.solve(); } - public void addTransaction(Transaction tx) { block.addTransaction(tx); } - - public TransactionOutPointWithValue getCoinbaseOutput() { - return new TransactionOutPointWithValue(block.getTransactions().get(0), 0); - } - - public TransactionOutPointWithValue getSpendableOutput() { - return spendableOutput; - } -} - -class TransactionOutPointWithValue { - public TransactionOutPoint outpoint; - public Coin value; - public Script scriptPubKey; - - public TransactionOutPointWithValue(TransactionOutPoint outpoint, Coin value, Script scriptPubKey) { - this.outpoint = outpoint; - this.value = value; - this.scriptPubKey = scriptPubKey; - } - - public TransactionOutPointWithValue(Transaction tx, int output) { - this(new TransactionOutPoint(tx.getParams(), output, tx.getHash()), - tx.getOutput(output).getValue(), tx.getOutput(output).getScriptPubKey()); - } -} - -/** An arbitrary rule which the testing client must match */ -class Rule { - String ruleName; - Rule(String ruleName) { - this.ruleName = ruleName; - } -} - -/** - * A test which checks the mempool state (ie defined which transactions should be in memory pool - */ -class MemoryPoolState extends Rule { - Set mempool; - public MemoryPoolState(Set mempool, String ruleName) { - super(ruleName); - this.mempool = mempool; - } -} - -class UTXORule extends Rule { - List query; - UTXOsMessage result; - - public UTXORule(String ruleName, TransactionOutPoint query, UTXOsMessage result) { - super(ruleName); - this.query = Collections.singletonList(query); - this.result = result; - } - - public UTXORule(String ruleName, List query, UTXOsMessage result) { - super(ruleName); - this.query = query; - this.result = result; - } -} - -class RuleList { - public List list; - public int maximumReorgBlockCount; - Map hashHeaderMap; - public RuleList(List list, Map hashHeaderMap, int maximumReorgBlockCount) { - this.list = list; - this.hashHeaderMap = hashHeaderMap; - this.maximumReorgBlockCount = maximumReorgBlockCount; - } -} - -public class FullBlockTestGenerator { - // Used by BitcoindComparisonTool and AbstractFullPrunedBlockChainTest to create test cases - private NetworkParameters params; - private ECKey coinbaseOutKey; - private byte[] coinbaseOutKeyPubKey; - - // Used to double-check that we are always using the right next-height - private Map blockToHeightMap = new HashMap(); - - private Map hashHeaderMap = new HashMap(); - private Map coinbaseBlockMap = new HashMap(); - - public FullBlockTestGenerator(NetworkParameters params) { - this.params = params; - coinbaseOutKey = new ECKey(); - coinbaseOutKeyPubKey = coinbaseOutKey.getPubKey(); - Utils.setMockClock(); - } - - public RuleList getBlocksToTest(boolean runBarelyExpensiveTests, boolean runExpensiveTests, File blockStorageFile) throws ScriptException, ProtocolException, IOException { - final FileOutputStream outStream = blockStorageFile != null ? new FileOutputStream(blockStorageFile) : null; - - final Script OP_TRUE_SCRIPT = new ScriptBuilder().op(OP_TRUE).build(); - final Script OP_NOP_SCRIPT = new ScriptBuilder().op(OP_NOP).build(); - - // TODO: Rename this variable. - List blocks = new LinkedList() { - @Override - public boolean add(Rule element) { - if (outStream != null && element instanceof BlockAndValidity) { - try { - outStream.write((int) (params.getPacketMagic() >>> 24)); - outStream.write((int) (params.getPacketMagic() >>> 16)); - outStream.write((int) (params.getPacketMagic() >>> 8)); - outStream.write((int) (params.getPacketMagic() >>> 0)); - byte[] block = ((BlockAndValidity)element).block.bitcoinSerialize(); - byte[] length = new byte[4]; - Utils.uint32ToByteArrayBE(block.length, length, 0); - outStream.write(Utils.reverseBytes(length)); - outStream.write(block); - ((BlockAndValidity)element).block = null; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return super.add(element); - } - }; - RuleList ret = new RuleList(blocks, hashHeaderMap, 10); - - Queue spendableOutputs = new LinkedList(); - - int chainHeadHeight = 1; - Block chainHead = params.getGenesisBlock().createNextBlockWithCoinbase(coinbaseOutKeyPubKey); - blocks.add(new BlockAndValidity(chainHead, true, false, chainHead.getHash(), 1, "Initial Block")); - spendableOutputs.offer(new TransactionOutPointWithValue( - new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()), - FIFTY_COINS, chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); - for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { - chainHead = chainHead.createNextBlockWithCoinbase(coinbaseOutKeyPubKey); - chainHeadHeight++; - blocks.add(new BlockAndValidity(chainHead, true, false, chainHead.getHash(), i+1, "Initial Block chain output generation")); - spendableOutputs.offer(new TransactionOutPointWithValue( - new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()), - FIFTY_COINS, chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); - } - - // Start by building a couple of blocks on top of the genesis block. - NewBlock b1 = createNextBlock(chainHead, chainHeadHeight + 1, spendableOutputs.poll(), null); - blocks.add(new BlockAndValidity(b1, true, false, b1.getHash(), chainHeadHeight + 1, "b1")); - spendableOutputs.offer(b1.getCoinbaseOutput()); - - TransactionOutPointWithValue out1 = spendableOutputs.poll(); checkState(out1 != null); - NewBlock b2 = createNextBlock(b1, chainHeadHeight + 2, out1, null); - blocks.add(new BlockAndValidity(b2, true, false, b2.getHash(), chainHeadHeight + 2, "b2")); - // Make sure nothing funky happens if we try to re-add b2 - blocks.add(new BlockAndValidity(b2, true, false, b2.getHash(), chainHeadHeight + 2, "b2")); - spendableOutputs.offer(b2.getCoinbaseOutput()); - // We now have the following chain (which output is spent is in parentheses): - // genesis -> b1 (0) -> b2 (1) - // - // so fork like this: - // - // genesis -> b1 (0) -> b2 (1) - // \-> b3 (1) - // - // Nothing should happen at this point. We saw b2 first so it takes priority. - NewBlock b3 = createNextBlock(b1, chainHeadHeight + 2, out1, null); - blocks.add(new BlockAndValidity(b3, true, false, b2.getHash(), chainHeadHeight + 2, "b3")); - // Make sure nothing breaks if we add b3 twice - blocks.add(new BlockAndValidity(b3, true, false, b2.getHash(), chainHeadHeight + 2, "b3")); - - // Do a simple UTXO query. - UTXORule utxo1; - { - Transaction coinbase = b2.block.getTransactions().get(0); - TransactionOutPoint outpoint = new TransactionOutPoint(params, 0, coinbase.getHash()); - long[] heights = new long[] {chainHeadHeight + 2}; - UTXOsMessage result = new UTXOsMessage(params, ImmutableList.of(coinbase.getOutput(0)), heights, b2.getHash(), chainHeadHeight + 2); - utxo1 = new UTXORule("utxo1", outpoint, result); - blocks.add(utxo1); - } - - // Now we add another block to make the alternative chain longer. - // - // genesis -> b1 (0) -> b2 (1) - // \-> b3 (1) -> b4 (2) - // - TransactionOutPointWithValue out2 = checkNotNull(spendableOutputs.poll()); - NewBlock b4 = createNextBlock(b3, chainHeadHeight + 3, out2, null); - blocks.add(new BlockAndValidity(b4, true, false, b4.getHash(), chainHeadHeight + 3, "b4")); - - // Check that the old coinbase is no longer in the UTXO set and the new one is. - { - Transaction coinbase = b4.block.getTransactions().get(0); - TransactionOutPoint outpoint = new TransactionOutPoint(params, 0, coinbase.getHash()); - List queries = ImmutableList.of(utxo1.query.get(0), outpoint); - List results = Lists.asList(null, coinbase.getOutput(0), new TransactionOutput[]{}); - long[] heights = new long[] {chainHeadHeight + 3}; - UTXOsMessage result = new UTXOsMessage(params, results, heights, b4.getHash(), chainHeadHeight + 3); - UTXORule utxo2 = new UTXORule("utxo2", queries, result); - blocks.add(utxo2); - } - - // ... and back to the first chain. - NewBlock b5 = createNextBlock(b2, chainHeadHeight + 3, out2, null); - blocks.add(new BlockAndValidity(b5, true, false, b4.getHash(), chainHeadHeight + 3, "b5")); - spendableOutputs.offer(b5.getCoinbaseOutput()); - - TransactionOutPointWithValue out3 = spendableOutputs.poll(); - - NewBlock b6 = createNextBlock(b5, chainHeadHeight + 4, out3, null); - blocks.add(new BlockAndValidity(b6, true, false, b6.getHash(), chainHeadHeight + 4, "b6")); - // - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b3 (1) -> b4 (2) - // - - // Try to create a fork that double-spends - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b7 (2) -> b8 (4) - // \-> b3 (1) -> b4 (2) - // - NewBlock b7 = createNextBlock(b5, chainHeadHeight + 5, out2, null); - blocks.add(new BlockAndValidity(b7, true, false, b6.getHash(), chainHeadHeight + 4, "b7")); - - TransactionOutPointWithValue out4 = spendableOutputs.poll(); - - NewBlock b8 = createNextBlock(b7, chainHeadHeight + 6, out4, null); - blocks.add(new BlockAndValidity(b8, false, true, b6.getHash(), chainHeadHeight + 4, "b8")); - - // Try to create a block that has too much fee - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b9 (4) - // \-> b3 (1) -> b4 (2) - // - NewBlock b9 = createNextBlock(b6, chainHeadHeight + 5, out4, SATOSHI); - blocks.add(new BlockAndValidity(b9, false, true, b6.getHash(), chainHeadHeight + 4, "b9")); - - // Create a fork that ends in a block with too much fee (the one that causes the reorg) - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b10 (3) -> b11 (4) - // \-> b3 (1) -> b4 (2) - // - NewBlock b10 = createNextBlock(b5, chainHeadHeight + 4, out3, null); - blocks.add(new BlockAndValidity(b10, true, false, b6.getHash(), chainHeadHeight + 4, "b10")); - - NewBlock b11 = createNextBlock(b10, chainHeadHeight + 5, out4, SATOSHI); - blocks.add(new BlockAndValidity(b11, false, true, b6.getHash(), chainHeadHeight + 4, "b11")); - - // Try again, but with a valid fork first - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b14 (5) - // (b12 added last) - // \-> b3 (1) -> b4 (2) - // - NewBlock b12 = createNextBlock(b5, chainHeadHeight + 4, out3, null); - spendableOutputs.offer(b12.getCoinbaseOutput()); - - NewBlock b13 = createNextBlock(b12, chainHeadHeight + 5, out4, null); - blocks.add(new BlockAndValidity(b13, false, false, b6.getHash(), chainHeadHeight + 4, "b13")); - // Make sure we dont die if an orphan gets added twice - blocks.add(new BlockAndValidity(b13, false, false, b6.getHash(), chainHeadHeight + 4, "b13")); - spendableOutputs.offer(b13.getCoinbaseOutput()); - - TransactionOutPointWithValue out5 = spendableOutputs.poll(); - - NewBlock b14 = createNextBlock(b13, chainHeadHeight + 6, out5, SATOSHI); - // This will be "validly" added, though its actually invalid, it will just be marked orphan - // and will be discarded when an attempt is made to reorg to it. - // TODO: Use a WeakReference to check that it is freed properly after the reorg - blocks.add(new BlockAndValidity(b14, false, false, b6.getHash(), chainHeadHeight + 4, "b14")); - // Make sure we dont die if an orphan gets added twice - blocks.add(new BlockAndValidity(b14, false, false, b6.getHash(), chainHeadHeight + 4, "b14")); - - blocks.add(new BlockAndValidity(b12, false, true, b13.getHash(), chainHeadHeight + 5, "b12")); - - // Add a block with MAX_BLOCK_SIGOPS and one with one more sigop - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) - // \-> b3 (1) -> b4 (2) - // - NewBlock b15 = createNextBlock(b13, chainHeadHeight + 6, out5, null); - { - int sigOps = 0; - for (Transaction tx : b15.block.getTransactions()) - sigOps += tx.getSigOpCount(); - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps]; - Arrays.fill(outputScript, (byte) OP_CHECKSIG); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b15); - b15.addTransaction(tx); - - sigOps = 0; - for (Transaction tx2 : b15.block.getTransactions()) - sigOps += tx2.getSigOpCount(); - checkState(sigOps == Block.MAX_BLOCK_SIGOPS); - } - b15.solve(); - - blocks.add(new BlockAndValidity(b15, true, false, b15.getHash(), chainHeadHeight + 6, "b15")); - spendableOutputs.offer(b15.getCoinbaseOutput()); - - TransactionOutPointWithValue out6 = spendableOutputs.poll(); - - NewBlock b16 = createNextBlock(b15, chainHeadHeight + 7, out6, null); - { - int sigOps = 0; - for (Transaction tx : b16.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + 1]; - Arrays.fill(outputScript, (byte) OP_CHECKSIG); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b16); - b16.addTransaction(tx); - - sigOps = 0; - for (Transaction tx2 : b16.block.getTransactions()) - sigOps += tx2.getSigOpCount(); - checkState(sigOps == Block.MAX_BLOCK_SIGOPS + 1); - } - b16.solve(); - - blocks.add(new BlockAndValidity(b16, false, true, b15.getHash(), chainHeadHeight + 6, "b16")); - - // Attempt to spend a transaction created on a different fork - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (6) - // \-> b3 (1) -> b4 (2) - // - NewBlock b17 = createNextBlock(b15, chainHeadHeight + 7, out6, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, new byte[] {})); - addOnlyInputToTransaction(tx, b3); - b17.addTransaction(tx); - } - b17.solve(); - blocks.add(new BlockAndValidity(b17, false, true, b15.getHash(), chainHeadHeight + 6, "b17")); - - // Attempt to spend a transaction created on a different fork (on a fork this time) - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) - // \-> b18 (5) -> b19 (6) - // \-> b3 (1) -> b4 (2) - // - NewBlock b18 = createNextBlock(b13, chainHeadHeight + 6, out5, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, new byte[] {})); - addOnlyInputToTransaction(tx, b3); - b18.addTransaction(tx); - } - b18.solve(); - blocks.add(new BlockAndValidity(b18, true, false, b15.getHash(), chainHeadHeight + 6, "b17")); - - NewBlock b19 = createNextBlock(b18, chainHeadHeight + 7, out6, null); - blocks.add(new BlockAndValidity(b19, false, true, b15.getHash(), chainHeadHeight + 6, "b19")); - - // Attempt to spend a coinbase at depth too low - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) - // \-> b3 (1) -> b4 (2) - // - TransactionOutPointWithValue out7 = spendableOutputs.poll(); - - NewBlock b20 = createNextBlock(b15.block, chainHeadHeight + 7, out7, null); - blocks.add(new BlockAndValidity(b20, false, true, b15.getHash(), chainHeadHeight + 6, "b20")); - - // Attempt to spend a coinbase at depth too low (on a fork this time) - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) - // \-> b21 (6) -> b22 (5) - // \-> b3 (1) -> b4 (2) - // - NewBlock b21 = createNextBlock(b13, chainHeadHeight + 6, out6, null); - blocks.add(new BlockAndValidity(b21.block, true, false, b15.getHash(), chainHeadHeight + 6, "b21")); - NewBlock b22 = createNextBlock(b21, chainHeadHeight + 7, out5, null); - blocks.add(new BlockAndValidity(b22.block, false, true, b15.getHash(), chainHeadHeight + 6, "b22")); - - // Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) - // \-> b24 (6) -> b25 (7) - // \-> b3 (1) -> b4 (2) - // - NewBlock b23 = createNextBlock(b15, chainHeadHeight + 7, out6, null); - { - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIZE - b23.block.getMessageSize() - 65]; - Arrays.fill(outputScript, (byte) OP_FALSE); - tx.addOutput(new TransactionOutput(params, tx, ZERO, outputScript)); - addOnlyInputToTransaction(tx, b23); - b23.addTransaction(tx); - } - b23.solve(); - checkState(b23.block.getMessageSize() == Block.MAX_BLOCK_SIZE); - blocks.add(new BlockAndValidity(b23, true, false, b23.getHash(), chainHeadHeight + 7, "b23")); - spendableOutputs.offer(b23.getCoinbaseOutput()); - - NewBlock b24 = createNextBlock(b15, chainHeadHeight + 7, out6, null); - { - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIZE - b24.block.getMessageSize() - 64]; - Arrays.fill(outputScript, (byte) OP_FALSE); - tx.addOutput(new TransactionOutput(params, tx, ZERO, outputScript)); - addOnlyInputToTransaction(tx, b24); - b24.addTransaction(tx); - } - b24.solve(); - checkState(b24.block.getMessageSize() == Block.MAX_BLOCK_SIZE + 1); - blocks.add(new BlockAndValidity(b24, false, true, b23.getHash(), chainHeadHeight + 7, "b24")); - - // Extend the b24 chain to make sure bitcoind isn't accepting b24 - NewBlock b25 = createNextBlock(b24, chainHeadHeight + 8, out7, null); - blocks.add(new BlockAndValidity(b25, false, false, b23.getHash(), chainHeadHeight + 7, "b25")); - - // Create blocks with a coinbase input script size out of range - // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) - // \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) - // \-> ... (6) -> ... (7) - // \-> b3 (1) -> b4 (2) - // - NewBlock b26 = createNextBlock(b15, chainHeadHeight + 7, out6, null); - // 1 is too small, but we already generate every other block with 2, so that is tested - b26.block.getTransactions().get(0).getInputs().get(0).setScriptBytes(new byte[] {0}); - b26.block.setMerkleRoot(null); - b26.solve(); - blocks.add(new BlockAndValidity(b26, false, true, b23.getHash(), chainHeadHeight + 7, "b26")); - - // Extend the b26 chain to make sure bitcoind isn't accepting b26 - NewBlock b27 = createNextBlock(b26, chainHeadHeight + 8, out7, null); - blocks.add(new BlockAndValidity(b27, false, false, b23.getHash(), chainHeadHeight + 7, "b27")); - - NewBlock b28 = createNextBlock(b15, chainHeadHeight + 7, out6, null); - { - byte[] coinbase = new byte[101]; - Arrays.fill(coinbase, (byte)0); - b28.block.getTransactions().get(0).getInputs().get(0).setScriptBytes(coinbase); - } - b28.block.setMerkleRoot(null); - b28.solve(); - blocks.add(new BlockAndValidity(b28, false, true, b23.getHash(), chainHeadHeight + 7, "b28")); - - // Extend the b28 chain to make sure bitcoind isn't accepting b28 - NewBlock b29 = createNextBlock(b28, chainHeadHeight + 8, out7, null); - blocks.add(new BlockAndValidity(b29, false, false, b23.getHash(), chainHeadHeight + 7, "b29")); - - NewBlock b30 = createNextBlock(b23, chainHeadHeight + 8, out7, null); - { - byte[] coinbase = new byte[100]; - Arrays.fill(coinbase, (byte)0); - b30.block.getTransactions().get(0).getInputs().get(0).setScriptBytes(coinbase); - } - b30.block.setMerkleRoot(null); - b30.solve(); - blocks.add(new BlockAndValidity(b30, true, false, b30.getHash(), chainHeadHeight + 8, "b30")); - spendableOutputs.offer(b30.getCoinbaseOutput()); - - // Check sigops of OP_CHECKMULTISIG/OP_CHECKMULTISIGVERIFY/OP_CHECKSIGVERIFY - // 6 (3) - // 12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) - // \-> b36 (11) - // \-> b34 (10) - // \-> b32 (9) - // - TransactionOutPointWithValue out8 = spendableOutputs.poll(); - - NewBlock b31 = createNextBlock(b30, chainHeadHeight + 9, out8, null); - { - int sigOps = 0; - for (Transaction tx : b31.block.transactions) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[(Block.MAX_BLOCK_SIGOPS - sigOps)/20]; - Arrays.fill(outputScript, (byte) OP_CHECKMULTISIG); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b31); - b31.addTransaction(tx); - } - b31.solve(); - - blocks.add(new BlockAndValidity(b31, true, false, b31.getHash(), chainHeadHeight + 9, "b31")); - spendableOutputs.offer(b31.getCoinbaseOutput()); - - TransactionOutPointWithValue out9 = spendableOutputs.poll(); - - NewBlock b32 = createNextBlock(b31, chainHeadHeight + 10, out9, null); - { - int sigOps = 0; - for (Transaction tx : b32.block.transactions) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[(Block.MAX_BLOCK_SIGOPS - sigOps)/20 + (Block.MAX_BLOCK_SIGOPS - sigOps)%20 + 1]; - Arrays.fill(outputScript, (byte) OP_CHECKMULTISIG); - for (int i = 0; i < (Block.MAX_BLOCK_SIGOPS - sigOps)%20; i++) - outputScript[i] = (byte) OP_CHECKSIG; - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b32); - b32.addTransaction(tx); - } - b32.solve(); - blocks.add(new BlockAndValidity(b32, false, true, b31.getHash(), chainHeadHeight + 9, "b32")); - - NewBlock b33 = createNextBlock(b31, chainHeadHeight + 10, out9, null); - { - int sigOps = 0; - for (Transaction tx : b33.block.transactions) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[(Block.MAX_BLOCK_SIGOPS - sigOps)/20]; - Arrays.fill(outputScript, (byte) OP_CHECKMULTISIGVERIFY); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b33); - b33.addTransaction(tx); - } - b33.solve(); - - blocks.add(new BlockAndValidity(b33, true, false, b33.getHash(), chainHeadHeight + 10, "b33")); - spendableOutputs.offer(b33.getCoinbaseOutput()); - - TransactionOutPointWithValue out10 = spendableOutputs.poll(); - - NewBlock b34 = createNextBlock(b33, chainHeadHeight + 11, out10, null); - { - int sigOps = 0; - for (Transaction tx : b34.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[(Block.MAX_BLOCK_SIGOPS - sigOps)/20 + (Block.MAX_BLOCK_SIGOPS - sigOps)%20 + 1]; - Arrays.fill(outputScript, (byte) OP_CHECKMULTISIGVERIFY); - for (int i = 0; i < (Block.MAX_BLOCK_SIGOPS - sigOps)%20; i++) - outputScript[i] = (byte) OP_CHECKSIG; - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b34); - b34.addTransaction(tx); - } - b34.solve(); - blocks.add(new BlockAndValidity(b34, false, true, b33.getHash(), chainHeadHeight + 10, "b34")); - - NewBlock b35 = createNextBlock(b33, chainHeadHeight + 11, out10, null); - { - int sigOps = 0; - for (Transaction tx : b35.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps]; - Arrays.fill(outputScript, (byte) OP_CHECKSIGVERIFY); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b35); - b35.addTransaction(tx); - } - b35.solve(); - - blocks.add(new BlockAndValidity(b35, true, false, b35.getHash(), chainHeadHeight + 11, "b35")); - spendableOutputs.offer(b35.getCoinbaseOutput()); - - TransactionOutPointWithValue out11 = spendableOutputs.poll(); - - NewBlock b36 = createNextBlock(b35, chainHeadHeight + 12, out11, null); - { - int sigOps = 0; - for (Transaction tx : b36.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + 1]; - Arrays.fill(outputScript, (byte) OP_CHECKSIGVERIFY); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b36); - b36.addTransaction(tx); - } - b36.solve(); - - blocks.add(new BlockAndValidity(b36, false, true, b35.getHash(), chainHeadHeight + 11, "b36")); - - // Check spending of a transaction in a block which failed to connect - // (test block store transaction abort handling, not that it should get this far if that's broken...) - // 6 (3) - // 12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) - // \-> b37 (11) - // \-> b38 (11) - // - NewBlock b37 = createNextBlock(b35, chainHeadHeight + 12, out11, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, new byte[] {})); - addOnlyInputToTransaction(tx, out11); // double spend out11 - b37.addTransaction(tx); - } - b37.solve(); - blocks.add(new BlockAndValidity(b37, false, true, b35.getHash(), chainHeadHeight + 11, "b37")); - - NewBlock b38 = createNextBlock(b35, chainHeadHeight + 12, out11, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, new byte[] {})); - // Attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid - addOnlyInputToTransaction(tx, b37); - b38.addTransaction(tx); - } - b38.solve(); - blocks.add(new BlockAndValidity(b38, false, true, b35.getHash(), chainHeadHeight + 11, "b38")); - - // Check P2SH SigOp counting - // 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) - // \-> b40 (12) - // - // Create some P2SH outputs that will require 6 sigops to spend - byte[] b39p2shScriptPubKey; - int b39numP2SHOutputs = 0, b39sigOpsPerOutput = 6; - NewBlock b39 = createNextBlock(b35, chainHeadHeight + 12, null, null); - { - ByteArrayOutputStream p2shScriptPubKey = new UnsafeByteArrayOutputStream(); - try { - Script.writeBytes(p2shScriptPubKey, coinbaseOutKeyPubKey); - p2shScriptPubKey.write(OP_2DUP); - p2shScriptPubKey.write(OP_CHECKSIGVERIFY); - p2shScriptPubKey.write(OP_2DUP); - p2shScriptPubKey.write(OP_CHECKSIGVERIFY); - p2shScriptPubKey.write(OP_2DUP); - p2shScriptPubKey.write(OP_CHECKSIGVERIFY); - p2shScriptPubKey.write(OP_2DUP); - p2shScriptPubKey.write(OP_CHECKSIGVERIFY); - p2shScriptPubKey.write(OP_2DUP); - p2shScriptPubKey.write(OP_CHECKSIGVERIFY); - p2shScriptPubKey.write(OP_CHECKSIG); - } catch (IOException e) { - throw new RuntimeException(e); // Cannot happen. - } - b39p2shScriptPubKey = p2shScriptPubKey.toByteArray(); - - byte[] scriptHash = Utils.sha256hash160(b39p2shScriptPubKey); - UnsafeByteArrayOutputStream scriptPubKey = new UnsafeByteArrayOutputStream(scriptHash.length + 3); - scriptPubKey.write(OP_HASH160); - try { - Script.writeBytes(scriptPubKey, scriptHash); - } catch (IOException e) { - throw new RuntimeException(e); // Cannot happen. - } - scriptPubKey.write(OP_EQUAL); - - Coin lastOutputValue = out11.value.subtract(SATOSHI); - TransactionOutPoint lastOutPoint; - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, scriptPubKey.toByteArray())); - tx.addOutput(new TransactionOutput(params, tx, lastOutputValue, new byte[]{OP_1})); - addOnlyInputToTransaction(tx, out11); - lastOutPoint = new TransactionOutPoint(params, 1, tx.getHash()); - b39.addTransaction(tx); - } - b39numP2SHOutputs++; - - while (b39.block.getMessageSize() < Block.MAX_BLOCK_SIZE) - { - Transaction tx = new Transaction(params); - - lastOutputValue = lastOutputValue.subtract(SATOSHI); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, scriptPubKey.toByteArray())); - tx.addOutput(new TransactionOutput(params, tx, lastOutputValue, new byte[]{OP_1})); - tx.addInput(new TransactionInput(params, tx, new byte[]{OP_1}, lastOutPoint)); - lastOutPoint = new TransactionOutPoint(params, 1, tx.getHash()); - - if (b39.block.getMessageSize() + tx.getMessageSize() < Block.MAX_BLOCK_SIZE) { - b39.addTransaction(tx); - b39numP2SHOutputs++; - } else - break; - } - } - b39.solve(); - blocks.add(new BlockAndValidity(b39, true, false, b39.getHash(), chainHeadHeight + 12, "b39")); - spendableOutputs.offer(b39.getCoinbaseOutput()); - - TransactionOutPointWithValue out12 = spendableOutputs.poll(); - - NewBlock b40 = createNextBlock(b39, chainHeadHeight + 13, out12, null); - { - int sigOps = 0; - for (Transaction tx : b40.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - - int numTxes = (Block.MAX_BLOCK_SIGOPS - sigOps) / b39sigOpsPerOutput; - checkState(numTxes <= b39numP2SHOutputs); - - TransactionOutPoint lastOutPoint = new TransactionOutPoint(params, 1, b40.block.getTransactions().get(1).getHash()); - - byte[] scriptSig = null; - for (int i = 1; i <= numTxes; i++) { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, new byte[] {OP_1})); - tx.addInput(new TransactionInput(params, tx, new byte[]{OP_1}, lastOutPoint)); - - TransactionInput input = new TransactionInput(params, tx, new byte[]{}, - new TransactionOutPoint(params, 0, b39.block.getTransactions().get(i).getHash())); - tx.addInput(input); - - if (scriptSig == null) { - // Exploit the SigHash.SINGLE bug to avoid having to make more than one signature - Sha256Hash hash = tx.hashForSignature(1, b39p2shScriptPubKey, SigHash.SINGLE, false); - - // Sign input - try { - ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(73); - bos.write(coinbaseOutKey.sign(hash).encodeToDER()); - bos.write(SigHash.SINGLE.ordinal() + 1); - byte[] signature = bos.toByteArray(); - - ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream(signature.length + b39p2shScriptPubKey.length + 3); - Script.writeBytes(scriptSigBos, new byte[] {(byte) OP_CHECKSIG}); - scriptSigBos.write(Script.createInputScript(signature)); - Script.writeBytes(scriptSigBos, b39p2shScriptPubKey); - - scriptSig = scriptSigBos.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); // Cannot happen. - } - } - - input.setScriptBytes(scriptSig); - - lastOutPoint = new TransactionOutPoint(params, 0, tx.getHash()); - - b40.addTransaction(tx); - } - - sigOps += numTxes * b39sigOpsPerOutput; - Transaction tx = new Transaction(params); - tx.addInput(new TransactionInput(params, tx, new byte[]{OP_1}, lastOutPoint)); - byte[] scriptPubKey = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + 1]; - Arrays.fill(scriptPubKey, (byte) OP_CHECKSIG); - tx.addOutput(new TransactionOutput(params, tx, ZERO, scriptPubKey)); - b40.addTransaction(tx); - } - b40.solve(); - blocks.add(new BlockAndValidity(b40, false, true, b39.getHash(), chainHeadHeight + 12, "b40")); - - NewBlock b41 = null; - if (runBarelyExpensiveTests) { - b41 = createNextBlock(b39, chainHeadHeight + 13, out12, null); - { - int sigOps = 0; - for (Transaction tx : b41.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - - int numTxes = (Block.MAX_BLOCK_SIGOPS - sigOps) - / b39sigOpsPerOutput; - checkState(numTxes <= b39numP2SHOutputs); - - TransactionOutPoint lastOutPoint = new TransactionOutPoint( - params, 1, b41.block.getTransactions().get(1).getHash()); - - byte[] scriptSig = null; - for (int i = 1; i <= numTxes; i++) { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, Coin - .SATOSHI, new byte[] {OP_1})); - tx.addInput(new TransactionInput(params, tx, - new byte[] {OP_1}, lastOutPoint)); - - TransactionInput input = new TransactionInput(params, tx, - new byte[] {}, new TransactionOutPoint(params, 0, - b39.block.getTransactions().get(i).getHash())); - tx.addInput(input); - - if (scriptSig == null) { - // Exploit the SigHash.SINGLE bug to avoid having to make more than one signature - Sha256Hash hash = tx.hashForSignature(1, - b39p2shScriptPubKey, SigHash.SINGLE, false); - - // Sign input - try { - ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream( - 73); - bos.write(coinbaseOutKey.sign(hash).encodeToDER()); - bos.write(SigHash.SINGLE.ordinal() + 1); - byte[] signature = bos.toByteArray(); - - ByteArrayOutputStream scriptSigBos = new UnsafeByteArrayOutputStream( - signature.length - + b39p2shScriptPubKey.length + 3); - Script.writeBytes(scriptSigBos, - new byte[] { (byte) OP_CHECKSIG}); - scriptSigBos.write(Script - .createInputScript(signature)); - Script.writeBytes(scriptSigBos, b39p2shScriptPubKey); - - scriptSig = scriptSigBos.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); // Cannot happen. - } - } - - input.setScriptBytes(scriptSig); - - lastOutPoint = new TransactionOutPoint(params, 0, - tx.getHash()); - - b41.addTransaction(tx); - } - - sigOps += numTxes * b39sigOpsPerOutput; - Transaction tx = new Transaction(params); - tx.addInput(new TransactionInput(params, tx, - new byte[] {OP_1}, lastOutPoint)); - byte[] scriptPubKey = new byte[Block.MAX_BLOCK_SIGOPS - sigOps]; - Arrays.fill(scriptPubKey, (byte) OP_CHECKSIG); - tx.addOutput(new TransactionOutput(params, tx, ZERO, scriptPubKey)); - b41.addTransaction(tx); - } - b41.solve(); - blocks.add(new BlockAndValidity(b41, true, false, b41.getHash(), chainHeadHeight + 13, "b41")); - } - - // Fork off of b39 to create a constant base again - // b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) - // \-> b41 (12) - // - NewBlock b42 = createNextBlock(b39, chainHeadHeight + 13, out12, null); - blocks.add(new BlockAndValidity(b42, true, false, b41 == null ? b42.getHash() : b41.getHash(), chainHeadHeight + 13, "b42")); - spendableOutputs.offer(b42.getCoinbaseOutput()); - - TransactionOutPointWithValue out13 = spendableOutputs.poll(); - - NewBlock b43 = createNextBlock(b42, chainHeadHeight + 14, out13, null); - blocks.add(new BlockAndValidity(b43, true, false, b43.getHash(), chainHeadHeight + 14, "b43")); - spendableOutputs.offer(b43.getCoinbaseOutput()); - - // Test a number of really invalid scenarios - // -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b44 (14) - // \-> ??? (15) - // - TransactionOutPointWithValue out14 = spendableOutputs.poll(); - - // A valid block created exactly like b44 to make sure the creation itself works - Block b44 = new Block(params); - byte[] outScriptBytes = ScriptBuilder.createOutputScript(ECKey.fromPublicOnly(coinbaseOutKeyPubKey)).getProgram(); - { - b44.setDifficultyTarget(b43.block.getDifficultyTarget()); - b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, ZERO); - - Transaction t = new Transaction(params); - // Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much - t.addOutput(new TransactionOutput(params, t, ZERO, new byte[] {OP_PUSHDATA1 - 1 })); - t.addOutput(new TransactionOutput(params, t, SATOSHI, outScriptBytes)); - // Spendable output - t.addOutput(new TransactionOutput(params, t, ZERO, new byte[] {OP_1})); - addOnlyInputToTransaction(t, out14); - b44.addTransaction(t); - - b44.setPrevBlockHash(b43.getHash()); - b44.setTime(b43.block.getTimeSeconds() + 1); - } - b44.solve(); - blocks.add(new BlockAndValidity(b44, true, false, b44.getHash(), chainHeadHeight + 15, "b44")); - - TransactionOutPointWithValue out15 = spendableOutputs.poll(); - - // A block with a non-coinbase as the first tx - Block b45 = new Block(params); - { - b45.setDifficultyTarget(b44.getDifficultyTarget()); - //b45.addCoinbaseTransaction(pubKey, coinbaseValue); - - Transaction t = new Transaction(params); - // Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much - t.addOutput(new TransactionOutput(params, t, ZERO, new byte[] {OP_PUSHDATA1 - 1 })); - t.addOutput(new TransactionOutput(params, t, SATOSHI, outScriptBytes)); - // Spendable output - t.addOutput(new TransactionOutput(params, t, ZERO, new byte[] {OP_1})); - addOnlyInputToTransaction(t, out15); - try { - b45.addTransaction(t); - } catch (RuntimeException e) { } // Should happen - if (b45.getTransactions().size() > 0) - throw new RuntimeException("addTransaction doesn't properly check for adding a non-coinbase as first tx"); - b45.addTransaction(t, false); - - b45.setPrevBlockHash(b44.getHash()); - b45.setTime(b44.getTimeSeconds() + 1); - } - b45.solve(); - blocks.add(new BlockAndValidity(b45, false, true, b44.getHash(), chainHeadHeight + 15, "b45")); - - // A block with no txn - Block b46 = new Block(params); - { - b46.transactions = new ArrayList(); - b46.setDifficultyTarget(b44.getDifficultyTarget()); - b46.setMerkleRoot(Sha256Hash.ZERO_HASH); - - b46.setPrevBlockHash(b44.getHash()); - b46.setTime(b44.getTimeSeconds() + 1); - } - b46.solve(); - blocks.add(new BlockAndValidity(b46, false, true, b44.getHash(), chainHeadHeight + 15, "b46")); - - // A block with invalid work - NewBlock b47 = createNextBlock(b44, chainHeadHeight + 16, out15, null); - { - try { - // Inverse solve - BigInteger target = b47.block.getDifficultyTargetAsInteger(); - while (true) { - BigInteger h = b47.getHash().toBigInteger(); - if (h.compareTo(target) > 0) // if invalid - break; - // increment the nonce and try again. - b47.block.setNonce(b47.block.getNonce() + 1); - } - } catch (VerificationException e) { - throw new RuntimeException(e); // Cannot happen. - } - } - blocks.add(new BlockAndValidity(b47, false, true, b44.getHash(), chainHeadHeight + 15, "b47")); - - // Block with timestamp > 2h in the future - NewBlock b48 = createNextBlock(b44, chainHeadHeight + 16, out15, null); - b48.block.setTime(Utils.currentTimeSeconds() + 60 * 60 * 3); - b48.solve(); - blocks.add(new BlockAndValidity(b48, false, true, b44.getHash(), chainHeadHeight + 15, "b48")); - - // Block with invalid merkle hash - NewBlock b49 = createNextBlock(b44, chainHeadHeight + 16, out15, null); - byte[] b49MerkleHash = Sha256Hash.ZERO_HASH.getBytes().clone(); - b49MerkleHash[1] = (byte) 0xDE; - b49.block.setMerkleRoot(Sha256Hash.create(b49MerkleHash)); - b49.solve(); - blocks.add(new BlockAndValidity(b49, false, true, b44.getHash(), chainHeadHeight + 15, "b49")); - - // Block with incorrect POW limit - NewBlock b50 = createNextBlock(b44, chainHeadHeight + 16, out15, null); - { - long diffTarget = b44.getDifficultyTarget(); - diffTarget &= 0xFFBFFFFF; // Make difficulty one bit harder - b50.block.setDifficultyTarget(diffTarget); - } - b50.solve(); - blocks.add(new BlockAndValidity(b50, false, true, b44.getHash(), chainHeadHeight + 15, "b50")); - - // A block with two coinbase txn - NewBlock b51 = createNextBlock(b44, chainHeadHeight + 16, out15, null); - { - Transaction coinbase = new Transaction(params); - coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) 0xff, 110, 1})); - coinbase.addOutput(new TransactionOutput(params, coinbase, SATOSHI, outScriptBytes)); - b51.block.addTransaction(coinbase, false); - } - b51.solve(); - blocks.add(new BlockAndValidity(b51, false, true, b44.getHash(), chainHeadHeight + 15, "b51")); - - // A block with duplicate txn - NewBlock b52 = createNextBlock(b44, chainHeadHeight + 16, out15, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, new byte[] {})); - addOnlyInputToTransaction(tx, b52); - b52.addTransaction(tx); - b52.addTransaction(tx); - } - b52.solve(); - blocks.add(new BlockAndValidity(b52, false, true, b44.getHash(), chainHeadHeight + 15, "b52")); - - // Test block timestamp - // -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) - // \-> b54 (15) - // \-> b44 (14) - // - NewBlock b53 = createNextBlock(b43, chainHeadHeight + 15, out14, null); - blocks.add(new BlockAndValidity(b53, true, false, b44.getHash(), chainHeadHeight + 15, "b53")); - spendableOutputs.offer(b53.getCoinbaseOutput()); - - // Block with invalid timestamp - NewBlock b54 = createNextBlock(b53, chainHeadHeight + 16, out15, null); - b54.block.setTime(b35.block.getTimeSeconds() - 1); - b54.solve(); - blocks.add(new BlockAndValidity(b54, false, true, b44.getHash(), chainHeadHeight + 15, "b54")); - - // Block with valid timestamp - NewBlock b55 = createNextBlock(b53, chainHeadHeight + 16, out15, null); - b55.block.setTime(b35.block.getTimeSeconds()); - b55.solve(); - blocks.add(new BlockAndValidity(b55, true, false, b55.getHash(), chainHeadHeight + 16, "b55")); - spendableOutputs.offer(b55.getCoinbaseOutput()); - - // Test CVE-2012-2459 - // -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) - // \-> b56 (16) - // - TransactionOutPointWithValue out16 = spendableOutputs.poll(); - - NewBlock b57 = createNextBlock(b55, chainHeadHeight + 17, out16, null); - Transaction b56txToDuplicate; - { - b56txToDuplicate = new Transaction(params); - b56txToDuplicate.addOutput(new TransactionOutput(params, b56txToDuplicate, SATOSHI, new byte[] {})); - addOnlyInputToTransaction(b56txToDuplicate, b57); - b57.addTransaction(b56txToDuplicate); - } - b57.solve(); - - Block b56; - try { - b56 = new Block(params, b57.block.bitcoinSerialize()); - } catch (ProtocolException e) { - throw new RuntimeException(e); // Cannot happen. - } - b56.addTransaction(b56txToDuplicate); - checkState(b56.getHash().equals(b57.getHash())); - blocks.add(new BlockAndValidity(b56, false, true, b55.getHash(), chainHeadHeight + 16, "b56")); - - NewBlock b57p2 = createNextBlock(b55, chainHeadHeight + 17, out16, null); - Transaction b56p2txToDuplicate1, b56p2txToDuplicate2; - { - Transaction tx1 = new Transaction(params); - tx1.addOutput(new TransactionOutput(params, tx1, SATOSHI, new byte[] {OP_TRUE})); - addOnlyInputToTransaction(tx1, b57p2); - b57p2.addTransaction(tx1); - - Transaction tx2 = new Transaction(params); - tx2.addOutput(new TransactionOutput(params, tx2, SATOSHI, new byte[] {OP_TRUE})); - addOnlyInputToTransaction(tx2, new TransactionOutPointWithValue( - new TransactionOutPoint(params, 0, tx1.getHash()), - SATOSHI, tx1.getOutputs().get(0).getScriptPubKey())); - b57p2.addTransaction(tx2); - - b56p2txToDuplicate1 = new Transaction(params); - b56p2txToDuplicate1.addOutput(new TransactionOutput(params, b56p2txToDuplicate1, SATOSHI, new byte[]{OP_TRUE})); - addOnlyInputToTransaction(b56p2txToDuplicate1, new TransactionOutPointWithValue( - new TransactionOutPoint(params, 0, tx2.getHash()), - SATOSHI, tx2.getOutputs().get(0).getScriptPubKey())); - b57p2.addTransaction(b56p2txToDuplicate1); - - b56p2txToDuplicate2 = new Transaction(params); - b56p2txToDuplicate2.addOutput(new TransactionOutput(params, b56p2txToDuplicate2, SATOSHI, new byte[]{})); - addOnlyInputToTransaction(b56p2txToDuplicate2, new TransactionOutPointWithValue( - new TransactionOutPoint(params, 0, b56p2txToDuplicate1.getHash()), - SATOSHI, b56p2txToDuplicate1.getOutputs().get(0).getScriptPubKey())); - b57p2.addTransaction(b56p2txToDuplicate2); - } - b57p2.solve(); - - Block b56p2; - try { - b56p2 = new Block(params, b57p2.block.bitcoinSerialize()); - } catch (ProtocolException e) { - throw new RuntimeException(e); // Cannot happen. - } - b56p2.addTransaction(b56p2txToDuplicate1); - b56p2.addTransaction(b56p2txToDuplicate2); - checkState(b56p2.getHash().equals(b57p2.getHash())); - blocks.add(new BlockAndValidity(b56p2, false, true, b55.getHash(), chainHeadHeight + 16, "b56p2")); - blocks.add(new BlockAndValidity(b57p2, true, false, b57p2.getHash(), chainHeadHeight + 17, "b57p2")); - - blocks.add(new BlockAndValidity(b57, true, false, b57p2.getHash(), chainHeadHeight + 17, "b57")); - spendableOutputs.offer(b57.getCoinbaseOutput()); - - // Test a few invalid tx types - // -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) - // \-> ??? (17) - // - TransactionOutPointWithValue out17 = spendableOutputs.poll(); - - // tx with prevout.n out of range - NewBlock b58 = createNextBlock(b57, chainHeadHeight + 18, out17, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, ZERO, new byte[] {})); - b58.getSpendableOutput().outpoint.setIndex(42); - addOnlyInputToTransaction(tx, b58); - b58.addTransaction(tx); - } - b58.solve(); - blocks.add(new BlockAndValidity(b58, false, true, b57p2.getHash(), chainHeadHeight + 17, "b58")); - - // tx with output value > input value out of range - NewBlock b59 = createNextBlock(b57, chainHeadHeight + 18, out17, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, - b59.getSpendableOutput().value.add(SATOSHI), new byte[]{})); - addOnlyInputToTransaction(tx, b59); - b59.addTransaction(tx); - } - b59.solve(); - blocks.add(new BlockAndValidity(b59, false, true, b57p2.getHash(), chainHeadHeight + 17, "b59")); - - NewBlock b60 = createNextBlock(b57, chainHeadHeight + 18, out17, null); - blocks.add(new BlockAndValidity(b60, true, false, b60.getHash(), chainHeadHeight + 18, "b60")); - spendableOutputs.offer(b60.getCoinbaseOutput()); - - // Test BIP30 - // -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) - // \-> b61 (18) - // - TransactionOutPointWithValue out18 = spendableOutputs.poll(); - - NewBlock b61 = createNextBlock(b60, chainHeadHeight + 19, out18, null); - { - b61.block.getTransactions().get(0).getInput(0).setScriptBytes(b60.block.getTransactions().get(0).getInput(0).getScriptBytes()); - b61.block.unCache(); - checkState(b61.block.getTransactions().get(0).equals(b60.block.getTransactions().get(0))); - } - b61.solve(); - blocks.add(new BlockAndValidity(b61, false, true, b60.getHash(), chainHeadHeight + 18, "b61")); - - // Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) - // -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) - // \-> b62 (18) - // - NewBlock b62 = createNextBlock(b60, chainHeadHeight + 19, null, null); - { - Transaction tx = new Transaction(params); - tx.setLockTime(0xffffffffL); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx, out18, 0); - b62.addTransaction(tx); - checkState(!tx.isFinal(chainHeadHeight + 17, b62.block.getTimeSeconds())); - } - b62.solve(); - blocks.add(new BlockAndValidity(b62, false, true, b60.getHash(), chainHeadHeight + 18, "b62")); - - // Test a non-final coinbase is also rejected - // -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) - // \-> b63 (-) - // - NewBlock b63 = createNextBlock(b60, chainHeadHeight + 19, null, null); - { - b63.block.getTransactions().get(0).setLockTime(0xffffffffL); - b63.block.getTransactions().get(0).getInputs().get(0).setSequenceNumber(0xDEADBEEF); - checkState(!b63.block.getTransactions().get(0).isFinal(chainHeadHeight + 17, b63.block.getTimeSeconds())); - } - b63.solve(); - blocks.add(new BlockAndValidity(b63, false, true, b60.getHash(), chainHeadHeight + 18, "b63")); - - // Check that a block which is (when properly encoded) <= MAX_BLOCK_SIZE is accepted - // Even when it is encoded with varints that make its encoded size actually > MAX_BLOCK_SIZE - // -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) - // - Block b64; NewBlock b64Original; - { - b64Original = createNextBlock(b60, chainHeadHeight + 19, out18, null); - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIZE - b64Original.block.getMessageSize() - 65]; - Arrays.fill(outputScript, (byte) OP_FALSE); - tx.addOutput(new TransactionOutput(params, tx, ZERO, outputScript)); - addOnlyInputToTransaction(tx, b64Original); - b64Original.addTransaction(tx); - b64Original.solve(); - checkState(b64Original.block.getMessageSize() == Block.MAX_BLOCK_SIZE); - - UnsafeByteArrayOutputStream stream = new UnsafeByteArrayOutputStream(b64Original.block.getMessageSize() + 8); - b64Original.block.writeHeader(stream); - - byte[] varIntBytes = new byte[9]; - varIntBytes[0] = (byte) 255; - Utils.uint32ToByteArrayLE((long)b64Original.block.getTransactions().size(), varIntBytes, 1); - Utils.uint32ToByteArrayLE(((long)b64Original.block.getTransactions().size()) >>> 32, varIntBytes, 5); - stream.write(varIntBytes); - checkState(new VarInt(varIntBytes, 0).value == b64Original.block.getTransactions().size()); - - for (Transaction transaction : b64Original.block.getTransactions()) - transaction.bitcoinSerialize(stream); - b64 = new Block(params, stream.toByteArray(), false, true, stream.size()); - - // The following checks are checking to ensure block serialization functions in the way needed for this test - // If they fail, it is likely not an indication of error, but an indication that this test needs rewritten - checkState(stream.size() == b64Original.block.getMessageSize() + 8); - checkState(stream.size() == b64.getMessageSize()); - checkState(Arrays.equals(stream.toByteArray(), b64.bitcoinSerialize())); - checkState(b64.getOptimalEncodingMessageSize() == b64Original.block.getMessageSize()); - } - blocks.add(new BlockAndValidity(b64, true, false, b64.getHash(), chainHeadHeight + 19, "b64")); - spendableOutputs.offer(b64Original.getCoinbaseOutput()); - - // Spend an output created in the block itself - // -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) - // - TransactionOutPointWithValue out19 = spendableOutputs.poll(); checkState(out19 != null);//TODO preconditions all the way up - - NewBlock b65 = createNextBlock(b64, chainHeadHeight + 20, null, null); - { - Transaction tx1 = new Transaction(params); - tx1.addOutput(out19.value, OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx1, out19, 0); - b65.addTransaction(tx1); - Transaction tx2 = new Transaction(params); - tx2.addOutput(ZERO, OP_TRUE_SCRIPT); - tx2.addInput(tx1.getHash(), 0, OP_TRUE_SCRIPT); - b65.addTransaction(tx2); - } - b65.solve(); - blocks.add(new BlockAndValidity(b65, true, false, b65.getHash(), chainHeadHeight + 20, "b65")); - spendableOutputs.offer(b65.getCoinbaseOutput()); - - // Attempt to spend an output created later in the same block - // -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) - // \-> b66 (20) - // - TransactionOutPointWithValue out20 = spendableOutputs.poll(); checkState(out20 != null); - - NewBlock b66 = createNextBlock(b65, chainHeadHeight + 21, null, null); - { - Transaction tx1 = new Transaction(params); - tx1.addOutput(out20.value, OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx1, out20, 0); - Transaction tx2 = new Transaction(params); - tx2.addOutput(ZERO, OP_TRUE_SCRIPT); - tx2.addInput(tx1.getHash(), 0, OP_NOP_SCRIPT); - b66.addTransaction(tx2); - b66.addTransaction(tx1); - } - b66.solve(); - blocks.add(new BlockAndValidity(b66, false, true, b65.getHash(), chainHeadHeight + 20, "b66")); - - // Attempt to double-spend a transaction created in a block - // -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) - // \-> b67 (20) - // - NewBlock b67 = createNextBlock(b65, chainHeadHeight + 21, null, null); - { - Transaction tx1 = new Transaction(params); - tx1.addOutput(out20.value, OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx1, out20, 0); - b67.addTransaction(tx1); - Transaction tx2 = new Transaction(params); - tx2.addOutput(ZERO, OP_TRUE_SCRIPT); - tx2.addInput(tx1.getHash(), 0, OP_NOP_SCRIPT); - b67.addTransaction(tx2); - Transaction tx3 = new Transaction(params); - tx3.addOutput(out20.value, OP_TRUE_SCRIPT); - tx3.addInput(tx1.getHash(), 0, OP_NOP_SCRIPT); - b67.addTransaction(tx3); - } - b67.solve(); - blocks.add(new BlockAndValidity(b67, false, true, b65.getHash(), chainHeadHeight + 20, "b67")); - - // A few more tests of block subsidy - // -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) - // \-> b68 (20) - // - NewBlock b68 = createNextBlock(b65, chainHeadHeight + 21, null, SATOSHI.multiply(10)); - { - Transaction tx = new Transaction(params); - tx.addOutput(out20.value.subtract(Coin.valueOf(9)), OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx, out20, 0); - b68.addTransaction(tx); - } - b68.solve(); - blocks.add(new BlockAndValidity(b68, false, true, b65.getHash(), chainHeadHeight + 20, "b68")); - - NewBlock b69 = createNextBlock(b65, chainHeadHeight + 21, null, SATOSHI.multiply(10)); - { - Transaction tx = new Transaction(params); - tx.addOutput(out20.value.subtract(Coin.valueOf(10)), OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx, out20, 0); - b69.addTransaction(tx); - } - b69.solve(); - blocks.add(new BlockAndValidity(b69, true, false, b69.getHash(), chainHeadHeight + 21, "b69")); - spendableOutputs.offer(b69.getCoinbaseOutput()); - - // Test spending the outpoint of a non-existent transaction - // -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) - // \-> b70 (21) - // - TransactionOutPointWithValue out21 = spendableOutputs.poll(); checkState(out21 != null); - NewBlock b70 = createNextBlock(b69, chainHeadHeight + 22, out21, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - tx.addInput(new Sha256Hash("23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c"), 0, - OP_NOP_SCRIPT); - b70.addTransaction(tx); - } - b70.solve(); - blocks.add(new BlockAndValidity(b70, false, true, b69.getHash(), chainHeadHeight + 21, "b70")); - - // Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) - // -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b71 (21) - // \-> b72 (21) - // - NewBlock b72 = createNextBlock(b69, chainHeadHeight + 22, out21, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - addOnlyInputToTransaction(tx, b72); - b72.addTransaction(tx); - } - b72.solve(); - - Block b71 = new Block(params, b72.block.bitcoinSerialize()); - b71.addTransaction(b72.block.getTransactions().get(2)); - checkState(b71.getHash().equals(b72.getHash())); - blocks.add(new BlockAndValidity(b71, false, true, b69.getHash(), chainHeadHeight + 21, "b71")); - blocks.add(new BlockAndValidity(b72, true, false, b72.getHash(), chainHeadHeight + 22, "b72")); - spendableOutputs.offer(b72.getCoinbaseOutput()); - - // Have some fun with invalid scripts and MAX_BLOCK_SIGOPS - // -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) - // \-> b** (22) - // - TransactionOutPointWithValue out22 = spendableOutputs.poll(); checkState(out22 != null); - - NewBlock b73 = createNextBlock(b72, chainHeadHeight + 23, out22, null); - { - int sigOps = 0; - for (Transaction tx : b73.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + (int)Script.MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1]; - Arrays.fill(outputScript, (byte) OP_CHECKSIG); - // If we push an element that is too large, the CHECKSIGs after that push are still counted - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps] = OP_PUSHDATA4; - Utils.uint32ToByteArrayLE(Script.MAX_SCRIPT_ELEMENT_SIZE + 1, outputScript, Block.MAX_BLOCK_SIGOPS - sigOps + 1); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b73); - b73.addTransaction(tx); - } - b73.solve(); - blocks.add(new BlockAndValidity(b73, false, true, b72.getHash(), chainHeadHeight + 22, "b73")); - - NewBlock b74 = createNextBlock(b72, chainHeadHeight + 23, out22, null); - { - int sigOps = 0; - for (Transaction tx : b74.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + (int)Script.MAX_SCRIPT_ELEMENT_SIZE + 42]; - Arrays.fill(outputScript, (byte) OP_CHECKSIG); - // If we push an invalid element, all previous CHECKSIGs are counted - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 1] = OP_PUSHDATA4; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 2] = (byte)0xfe; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 3] = (byte)0xff; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 4] = (byte)0xff; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 5] = (byte)0xff; - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b74); - b74.addTransaction(tx); - } - b74.solve(); - blocks.add(new BlockAndValidity(b74, false, true, b72.getHash(), chainHeadHeight + 22, "b74")); - - NewBlock b75 = createNextBlock(b72, chainHeadHeight + 23, out22, null); - { - int sigOps = 0; - for (Transaction tx : b75.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + (int)Script.MAX_SCRIPT_ELEMENT_SIZE + 42]; - Arrays.fill(outputScript, (byte) OP_CHECKSIG); - // If we push an invalid element, all subsequent CHECKSIGs are not counted - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps] = OP_PUSHDATA4; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 1] = (byte)0xff; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 2] = (byte)0xff; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 3] = (byte)0xff; - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps + 4] = (byte)0xff; - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b75); - b75.addTransaction(tx); - } - b75.solve(); - blocks.add(new BlockAndValidity(b75, true, false, b75.getHash(), chainHeadHeight + 23, "b75")); - spendableOutputs.offer(b75.getCoinbaseOutput()); - - TransactionOutPointWithValue out23 = spendableOutputs.poll(); checkState(out23 != null); - - NewBlock b76 = createNextBlock(b75, chainHeadHeight + 24, out23, null); - { - int sigOps = 0; - for (Transaction tx : b76.block.getTransactions()) { - sigOps += tx.getSigOpCount(); - } - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIGOPS - sigOps + (int)Script.MAX_SCRIPT_ELEMENT_SIZE + 1 + 5]; - Arrays.fill(outputScript, (byte) OP_CHECKSIG); - // If we push an element that is filled with CHECKSIGs, they (obviously) arent counted - outputScript[Block.MAX_BLOCK_SIGOPS - sigOps] = OP_PUSHDATA4; - Utils.uint32ToByteArrayLE(Block.MAX_BLOCK_SIGOPS, outputScript, Block.MAX_BLOCK_SIGOPS - sigOps + 1); - tx.addOutput(new TransactionOutput(params, tx, SATOSHI, outputScript)); - addOnlyInputToTransaction(tx, b76); - b76.addTransaction(tx); - } - b76.solve(); - blocks.add(new BlockAndValidity(b76, true, false, b76.getHash(), chainHeadHeight + 24, "b76")); - spendableOutputs.offer(b76.getCoinbaseOutput()); - - // Test transaction resurrection - // -> b77 (24) -> b78 (25) -> b79 (26) - // \-> b80 (25) -> b81 (26) -> b82 (27) - // b78 creates a tx, which is spent in b79. after b82, both should be in mempool - // - TransactionOutPointWithValue out24 = checkNotNull(spendableOutputs.poll()); - TransactionOutPointWithValue out25 = checkNotNull(spendableOutputs.poll()); - TransactionOutPointWithValue out26 = checkNotNull(spendableOutputs.poll()); - TransactionOutPointWithValue out27 = checkNotNull(spendableOutputs.poll()); - - NewBlock b77 = createNextBlock(b76, chainHeadHeight + 25, out24, null); - blocks.add(new BlockAndValidity(b77, true, false, b77.getHash(), chainHeadHeight + 25, "b77")); - spendableOutputs.offer(b77.getCoinbaseOutput()); - - NewBlock b78 = createNextBlock(b77, chainHeadHeight + 26, out25, null); - Transaction b78tx = new Transaction(params); - { - b78tx.addOutput(ZERO, OP_TRUE_SCRIPT); - addOnlyInputToTransaction(b78tx, b77); - b78.addTransaction(b78tx); - } - b78.solve(); - blocks.add(new BlockAndValidity(b78, true, false, b78.getHash(), chainHeadHeight + 26, "b78")); - - NewBlock b79 = createNextBlock(b78, chainHeadHeight + 27, out26, null); - Transaction b79tx = new Transaction(params); - - { - b79tx.addOutput(ZERO, OP_TRUE_SCRIPT); - b79tx.addInput(b78tx.getHash(), 0, OP_NOP_SCRIPT); - b79.addTransaction(b79tx); - } - b79.solve(); - blocks.add(new BlockAndValidity(b79, true, false, b79.getHash(), chainHeadHeight + 27, "b79")); - - blocks.add(new MemoryPoolState(new HashSet(), "post-b79 empty mempool")); - - NewBlock b80 = createNextBlock(b77, chainHeadHeight + 26, out25, null); - blocks.add(new BlockAndValidity(b80, true, false, b79.getHash(), chainHeadHeight + 27, "b80")); - spendableOutputs.offer(b80.getCoinbaseOutput()); - - NewBlock b81 = createNextBlock(b80, chainHeadHeight + 27, out26, null); - blocks.add(new BlockAndValidity(b81, true, false, b79.getHash(), chainHeadHeight + 27, "b81")); - spendableOutputs.offer(b81.getCoinbaseOutput()); - - NewBlock b82 = createNextBlock(b81, chainHeadHeight + 28, out27, null); - blocks.add(new BlockAndValidity(b82, true, false, b82.getHash(), chainHeadHeight + 28, "b82")); - spendableOutputs.offer(b82.getCoinbaseOutput()); - - HashSet post82Mempool = new HashSet(); - post82Mempool.add(new InventoryItem(InventoryItem.Type.Transaction, b78tx.getHash())); - post82Mempool.add(new InventoryItem(InventoryItem.Type.Transaction, b79tx.getHash())); - blocks.add(new MemoryPoolState(post82Mempool, "post-b82 tx resurrection")); - - // Check the UTXO query takes mempool into account. - { - TransactionOutPoint outpoint = new TransactionOutPoint(params, 0, b79tx.getHash()); - long[] heights = new long[] { UTXOsMessage.MEMPOOL_HEIGHT }; - UTXOsMessage result = new UTXOsMessage(params, ImmutableList.of(b79tx.getOutput(0)), heights, b82.getHash(), chainHeadHeight + 28); - UTXORule utxo3 = new UTXORule("utxo3", outpoint, result); - blocks.add(utxo3); - } - - // Test invalid opcodes in dead execution paths. - // -> b81 (26) -> b82 (27) -> b83 (28) - // b83 creates a tx which contains a transaction script with an invalid opcode in a dead execution path: - // OP_FALSE OP_IF OP_INVALIDOPCODE OP_ELSE OP_TRUE OP_ENDIF - // - TransactionOutPointWithValue out28 = spendableOutputs.poll(); Preconditions.checkState(out28 != null); - - NewBlock b83 = createNextBlock(b82, chainHeadHeight + 29, null, null); - { - Transaction tx1 = new Transaction(params); - tx1.addOutput(new TransactionOutput(params, tx1, out28.value, - new byte[]{OP_IF, (byte) OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF})); - addOnlyInputToTransaction(tx1, out28, 0); - b83.addTransaction(tx1); - Transaction tx2 = new Transaction(params); - tx2.addOutput(new TransactionOutput(params, tx2, ZERO, new byte[]{OP_TRUE})); - tx2.addInput(new TransactionInput(params, tx2, new byte[]{OP_FALSE}, - new TransactionOutPoint(params, 0, tx1.getHash()))); - b83.addTransaction(tx2); - } - b83.solve(); - blocks.add(new BlockAndValidity(b83, true, false, b83.getHash(), chainHeadHeight + 29, "b83")); - spendableOutputs.offer(b83.getCoinbaseOutput()); - - // Reorg on/off blocks that have OP_RETURN in them (and try to spend them) - // -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31) - // \-> b85 (29) -> b86 (30) \-> b89 (32) - // - TransactionOutPointWithValue out29 = spendableOutputs.poll(); Preconditions.checkState(out29 != null); - TransactionOutPointWithValue out30 = spendableOutputs.poll(); Preconditions.checkState(out30 != null); - TransactionOutPointWithValue out31 = spendableOutputs.poll(); Preconditions.checkState(out31 != null); - TransactionOutPointWithValue out32 = spendableOutputs.poll(); Preconditions.checkState(out32 != null); - - NewBlock b84 = createNextBlock(b83, chainHeadHeight + 30, out29, null); - Transaction b84tx1 = new Transaction(params); - { - b84tx1.addOutput(new TransactionOutput(params, b84tx1, ZERO, new byte[]{OP_RETURN})); - b84tx1.addOutput(new TransactionOutput(params, b84tx1, ZERO, new byte[]{OP_TRUE})); - b84tx1.addOutput(new TransactionOutput(params, b84tx1, ZERO, new byte[]{OP_TRUE})); - b84tx1.addOutput(new TransactionOutput(params, b84tx1, ZERO, new byte[]{OP_TRUE})); - b84tx1.addOutput(new TransactionOutput(params, b84tx1, ZERO, new byte[]{OP_TRUE})); - addOnlyInputToTransaction(b84tx1, b84); - b84.addTransaction(b84tx1); - - Transaction tx2 = new Transaction(params); - tx2.addOutput(new TransactionOutput(params, tx2, ZERO, new byte[]{OP_RETURN})); - tx2.addOutput(new TransactionOutput(params, tx2, ZERO, new byte[]{OP_RETURN})); - tx2.addInput(new TransactionInput(params, tx2, new byte[]{OP_TRUE}, new TransactionOutPoint(params, 1, b84tx1))); - b84.addTransaction(tx2); - - Transaction tx3 = new Transaction(params); - tx3.addOutput(new TransactionOutput(params, tx3, ZERO, new byte[]{OP_RETURN})); - tx3.addOutput(new TransactionOutput(params, tx3, ZERO, new byte[]{OP_TRUE})); - tx3.addInput(new TransactionInput(params, tx3, new byte[]{OP_TRUE}, new TransactionOutPoint(params, 2, b84tx1))); - b84.addTransaction(tx3); - - Transaction tx4 = new Transaction(params); - tx4.addOutput(new TransactionOutput(params, tx4, ZERO, new byte[]{OP_TRUE})); - tx4.addOutput(new TransactionOutput(params, tx4, ZERO, new byte[]{OP_RETURN})); - tx4.addInput(new TransactionInput(params, tx4, new byte[]{OP_TRUE}, new TransactionOutPoint(params, 3, b84tx1))); - b84.addTransaction(tx4); - - Transaction tx5 = new Transaction(params); - tx5.addOutput(new TransactionOutput(params, tx5, ZERO, new byte[]{OP_RETURN})); - tx5.addInput(new TransactionInput(params, tx5, new byte[]{OP_TRUE}, new TransactionOutPoint(params, 4, b84tx1))); - b84.addTransaction(tx5); - } - b84.solve(); - blocks.add(new BlockAndValidity(b84, true, false, b84.getHash(), chainHeadHeight + 30, "b84")); - spendableOutputs.offer(b84.getCoinbaseOutput()); - - NewBlock b85 = createNextBlock(b83, chainHeadHeight + 30, out29, null); - blocks.add(new BlockAndValidity(b85, true, false, b84.getHash(), chainHeadHeight + 30, "b85")); - - NewBlock b86 = createNextBlock(b85, chainHeadHeight + 31, out30, null); - blocks.add(new BlockAndValidity(b86, true, false, b86.getHash(), chainHeadHeight + 31, "b86")); - - NewBlock b87 = createNextBlock(b84, chainHeadHeight + 31, out30, null); - blocks.add(new BlockAndValidity(b87, true, false, b86.getHash(), chainHeadHeight + 31, "b87")); - spendableOutputs.offer(b87.getCoinbaseOutput()); - - NewBlock b88 = createNextBlock(b87, chainHeadHeight + 32, out31, null); - blocks.add(new BlockAndValidity(b88, true, false, b88.getHash(), chainHeadHeight + 32, "b88")); - spendableOutputs.offer(b88.getCoinbaseOutput()); - - NewBlock b89 = createNextBlock(b88, chainHeadHeight + 33, out32, null); - { - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, ZERO, new byte[] {OP_TRUE})); - tx.addInput(new TransactionInput(params, tx, new byte[]{OP_TRUE}, new TransactionOutPoint(params, 0, b84tx1))); - b89.addTransaction(tx); - b89.solve(); - } - blocks.add(new BlockAndValidity(b89, false, true, b88.getHash(), chainHeadHeight + 32, "b89")); - - // The remaining tests arent designed to fit in the standard flow, and thus must always come last - // Add new tests here. - - //TODO: Explicitly address MoneyRange() checks - - if (!runBarelyExpensiveTests) { - if (outStream != null) - outStream.close(); - - // (finally) return the created chain - return ret; - } - - // Test massive reorgs (in terms of block count/size) - // -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31) -> lots of blocks -> b1000 - // \-> b85 (29) -> b86 (30) \-> lots more blocks - // - NewBlock largeReorgFinal; - int LARGE_REORG_SIZE = 1008; // +/- a week of blocks - int largeReorgLastHeight = chainHeadHeight + 33 + LARGE_REORG_SIZE + 1; - { - NewBlock nextBlock = b88; - int nextHeight = chainHeadHeight + 33; - TransactionOutPointWithValue largeReorgOutput = out32; - for (int i = 0; i < LARGE_REORG_SIZE; i++) { - nextBlock = createNextBlock(nextBlock, nextHeight, largeReorgOutput, null); - Transaction tx = new Transaction(params); - byte[] outputScript = new byte[Block.MAX_BLOCK_SIZE - nextBlock.block.getMessageSize() - 65]; - Arrays.fill(outputScript, (byte) OP_FALSE); - tx.addOutput(new TransactionOutput(params, tx, ZERO, outputScript)); - addOnlyInputToTransaction(tx, nextBlock); - nextBlock.addTransaction(tx); - nextBlock.solve(); - blocks.add(new BlockAndValidity(nextBlock, true, false, nextBlock.getHash(), nextHeight++, "large reorg initial blocks " + i)); - spendableOutputs.offer(nextBlock.getCoinbaseOutput()); - largeReorgOutput = spendableOutputs.poll(); - } - NewBlock reorgBase = b88; - int reorgBaseHeight = chainHeadHeight + 33; - for (int i = 0; i < LARGE_REORG_SIZE; i++) { - reorgBase = createNextBlock(reorgBase, reorgBaseHeight++, null, null); - blocks.add(new BlockAndValidity(reorgBase, true, false, nextBlock.getHash(), nextHeight - 1, "large reorg reorg block " + i)); - } - reorgBase = createNextBlock(reorgBase, reorgBaseHeight, null, null); - blocks.add(new BlockAndValidity(reorgBase, true, false, reorgBase.getHash(), reorgBaseHeight, "large reorg reorging block")); - nextBlock = createNextBlock(nextBlock, nextHeight, null, null); - blocks.add(new BlockAndValidity(nextBlock, true, false, reorgBase.getHash(), nextHeight++, "large reorg second reorg initial")); - spendableOutputs.offer(nextBlock.getCoinbaseOutput()); - nextBlock = createNextBlock(nextBlock, nextHeight, null, null); spendableOutputs.poll(); - blocks.add(new BlockAndValidity(nextBlock, true, false, nextBlock.getHash(), nextHeight++, "large reorg second reorg")); - spendableOutputs.offer(nextBlock.getCoinbaseOutput()); - largeReorgFinal = nextBlock; - } - ret.maximumReorgBlockCount = Math.max(ret.maximumReorgBlockCount, LARGE_REORG_SIZE + 2); - - // Test massive reorgs (in terms of tx count) - // -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) -> b1001 (22) -> lots of outputs -> lots of spends - // Reorg back to: - // -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) -> b1001 (22) -> empty blocks - // - NewBlock b1001 = createNextBlock(largeReorgFinal, largeReorgLastHeight + 1, spendableOutputs.poll(), null); - blocks.add(new BlockAndValidity(b1001, true, false, b1001.getHash(), largeReorgLastHeight + 1, "b1001")); - spendableOutputs.offer(b1001.getCoinbaseOutput()); - int heightAfter1001 = largeReorgLastHeight + 2; - - if (runExpensiveTests) { - // No way you can fit this test in memory - Preconditions.checkArgument(blockStorageFile != null); - - NewBlock lastBlock = b1001; - TransactionOutPoint lastOutput = new TransactionOutPoint(params, 1, b1001.block.getTransactions().get(1).getHash()); - int blockCountAfter1001; - int nextHeight = heightAfter1001; - - List hashesToSpend = new LinkedList(); // all index 0 - final int TRANSACTION_CREATION_BLOCKS = 100; - for (blockCountAfter1001 = 0; blockCountAfter1001 < TRANSACTION_CREATION_BLOCKS; blockCountAfter1001++) { - NewBlock block = createNextBlock(lastBlock, nextHeight++, null, null); - while (block.block.getMessageSize() < Block.MAX_BLOCK_SIZE - 500) { - Transaction tx = new Transaction(params); - tx.addInput(lastOutput.getHash(), lastOutput.getIndex(), OP_NOP_SCRIPT); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - lastOutput = new TransactionOutPoint(params, 1, tx.getHash()); - hashesToSpend.add(tx.getHash()); - block.addTransaction(tx); - } - block.solve(); - blocks.add(new BlockAndValidity(block, true, false, block.getHash(), nextHeight-1, - "post-b1001 repeated transaction generator " + blockCountAfter1001 + "/" + TRANSACTION_CREATION_BLOCKS).setSendOnce(true)); - lastBlock = block; - } - - Iterator hashes = hashesToSpend.iterator(); - for (int i = 0; hashes.hasNext(); i++) { - NewBlock block = createNextBlock(lastBlock, nextHeight++, null, null); - while (block.block.getMessageSize() < Block.MAX_BLOCK_SIZE - 500 && hashes.hasNext()) { - Transaction tx = new Transaction(params); - tx.addInput(hashes.next(), 0, OP_NOP_SCRIPT); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - block.addTransaction(tx); - } - block.solve(); - blocks.add(new BlockAndValidity(block, true, false, block.getHash(), nextHeight-1, - "post-b1001 repeated transaction spender " + i).setSendOnce(true)); - lastBlock = block; - blockCountAfter1001++; - } - - // Reorg back to b1001 + empty blocks - Sha256Hash firstHash = lastBlock.getHash(); - int height = nextHeight-1; - nextHeight = heightAfter1001; - lastBlock = b1001; - for (int i = 0; i < blockCountAfter1001; i++) { - NewBlock block = createNextBlock(lastBlock, nextHeight++, null, null); - blocks.add(new BlockAndValidity(block, true, false, firstHash, height, "post-b1001 empty reorg block " + i + "/" + blockCountAfter1001)); - lastBlock = block; - } - - // Try to spend from the other chain - NewBlock b1002 = createNextBlock(lastBlock, nextHeight, null, null); - { - Transaction tx = new Transaction(params); - tx.addInput(hashesToSpend.get(0), 0, OP_NOP_SCRIPT); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - b1002.addTransaction(tx); - } - b1002.solve(); - blocks.add(new BlockAndValidity(b1002, false, true, firstHash, height, "b1002")); - - // Now actually reorg - NewBlock b1003 = createNextBlock(lastBlock, nextHeight, null, null); - blocks.add(new BlockAndValidity(b1003, true, false, b1003.getHash(), nextHeight, "b1003")); - - // Now try to spend again - NewBlock b1004 = createNextBlock(b1003, nextHeight + 1, null, null); - { - Transaction tx = new Transaction(params); - tx.addInput(hashesToSpend.get(0), 0, OP_NOP_SCRIPT); - tx.addOutput(ZERO, OP_TRUE_SCRIPT); - b1004.addTransaction(tx); - } - b1004.solve(); - blocks.add(new BlockAndValidity(b1004, false, true, b1003.getHash(), nextHeight, "b1004")); - - ret.maximumReorgBlockCount = Math.max(ret.maximumReorgBlockCount, blockCountAfter1001); - } - - if (outStream != null) - outStream.close(); - - // (finally) return the created chain - return ret; - } - - private byte uniquenessCounter = 0; - private NewBlock createNextBlock(Block baseBlock, int nextBlockHeight, @Nullable TransactionOutPointWithValue prevOut, - Coin additionalCoinbaseValue) throws ScriptException { - Integer height = blockToHeightMap.get(baseBlock.getHash()); - if (height != null) - checkState(height == nextBlockHeight - 1); - Coin coinbaseValue = FIFTY_COINS.shiftRight(nextBlockHeight / params.getSubsidyDecreaseBlockCount()) - .add((prevOut != null ? prevOut.value.subtract(SATOSHI) : ZERO)) - .add(additionalCoinbaseValue == null ? ZERO : additionalCoinbaseValue); - Block block = baseBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey, coinbaseValue); - Transaction t = new Transaction(params); - if (prevOut != null) { - // Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much - t.addOutput(new TransactionOutput(params, t, ZERO, new byte[] {(byte)(new Random().nextInt() & 0xff), uniquenessCounter++})); - // Spendable output - t.addOutput(new TransactionOutput(params, t, SATOSHI, new byte[] {OP_1})); - addOnlyInputToTransaction(t, prevOut); - block.addTransaction(t); - block.solve(); - } - return new NewBlock(block, prevOut == null ? null : new TransactionOutPointWithValue(t, 1)); - } - private NewBlock createNextBlock(NewBlock baseBlock, int nextBlockHeight, @Nullable TransactionOutPointWithValue prevOut, - Coin additionalCoinbaseValue) throws ScriptException { - return createNextBlock(baseBlock.block, nextBlockHeight, prevOut, additionalCoinbaseValue); - } - - private void addOnlyInputToTransaction(Transaction t, NewBlock block) throws ScriptException { - addOnlyInputToTransaction(t, block.getSpendableOutput(), TransactionInput.NO_SEQUENCE); - } - - private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut) throws ScriptException { - addOnlyInputToTransaction(t, prevOut, TransactionInput.NO_SEQUENCE); - } - - private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException { - TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint); - input.setSequenceNumber(sequence); - t.addInput(input); - - if (prevOut.scriptPubKey.getChunks().get(0).equalsOpCode(OP_TRUE)) { - input.setScriptSig(new ScriptBuilder().op(OP_1).build()); - } else { - // Sign input - checkState(prevOut.scriptPubKey.isSentToRawPubKey()); - Sha256Hash hash = t.hashForSignature(0, prevOut.scriptPubKey, SigHash.ALL, false); - input.setScriptSig(ScriptBuilder.createInputScript( - new TransactionSignature(coinbaseOutKey.sign(hash), SigHash.ALL, false)) - ); - } - } - - /** - * Represents a block which is sent to the tested application and which the application must either reject or accept, - * depending on the flags in the rule - */ - class BlockAndValidity extends Rule { - Block block; - Sha256Hash blockHash; - boolean connects; - boolean throwsException; - boolean sendOnce; // We can throw away the memory for this block once we send it the first time (if bitcoind asks again, its broken) - Sha256Hash hashChainTipAfterBlock; - int heightAfterBlock; - - public BlockAndValidity(Block block, boolean connects, boolean throwsException, Sha256Hash hashChainTipAfterBlock, int heightAfterBlock, String blockName) { - super(blockName); - if (connects && throwsException) - throw new RuntimeException("A block cannot connect if an exception was thrown while adding it."); - this.block = block; - this.blockHash = block.getHash(); - this.connects = connects; - this.throwsException = throwsException; - this.hashChainTipAfterBlock = hashChainTipAfterBlock; - this.heightAfterBlock = heightAfterBlock; - - // Keep track of the set of blocks indexed by hash - hashHeaderMap.put(block.getHash(), block.cloneAsHeader()); - - // Double-check that we are always marking any given block at the same height - Integer height = blockToHeightMap.get(hashChainTipAfterBlock); - if (height != null) - checkState(height == heightAfterBlock); - else - blockToHeightMap.put(hashChainTipAfterBlock, heightAfterBlock); - } - - public BlockAndValidity(NewBlock block, boolean connects, boolean throwsException, Sha256Hash hashChainTipAfterBlock, int heightAfterBlock, String blockName) { - this(block.block, connects, throwsException, hashChainTipAfterBlock, heightAfterBlock, blockName); - coinbaseBlockMap.put(block.getCoinbaseOutput().outpoint.getHash(), block.getHash()); - Integer blockHeight = blockToHeightMap.get(block.block.getPrevBlockHash()); - if (blockHeight != null) { - blockHeight++; - for (Transaction t : block.block.getTransactions()) - for (TransactionInput in : t.getInputs()) { - Sha256Hash blockSpendingHash = coinbaseBlockMap.get(in.getOutpoint().getHash()); - checkState(blockSpendingHash == null || blockToHeightMap.get(blockSpendingHash) == null || - blockToHeightMap.get(blockSpendingHash) == blockHeight - params.getSpendableCoinbaseDepth()); - } - } - } - - public BlockAndValidity setSendOnce(boolean sendOnce) { - this.sendOnce = sendOnce; - return this; - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/H2FullPrunedBlockChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/H2FullPrunedBlockChainTest.java deleted file mode 100644 index f2f24efd..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/H2FullPrunedBlockChainTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.store.BlockStoreException; -import com.dogecoin.dogecoinj.store.FullPrunedBlockStore; -import com.dogecoin.dogecoinj.store.H2FullPrunedBlockStore; -import org.junit.After; - -import java.io.File; - -/** - * An H2 implementation of the FullPrunedBlockStoreTest - */ -public class H2FullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest { - @After - public void tearDown() throws Exception { - deleteFiles(); - } - - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) throws BlockStoreException { - deleteFiles(); - return new H2FullPrunedBlockStore(params, "test", blockCount); - } - - private void deleteFiles() { - maybeDelete("test.h2.db"); - maybeDelete("test.trace.db"); - maybeDelete("test.lock.db"); - } - - private void maybeDelete(String s) { - new File(s).delete(); - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { - ((H2FullPrunedBlockStore)store).resetStore(); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/LazyParseByteCacheTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/LazyParseByteCacheTest.java deleted file mode 100644 index dae088b6..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/LazyParseByteCacheTest.java +++ /dev/null @@ -1,529 +0,0 @@ -/** - * Copyright 2011 Steve Coughlan. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.store.BlockStore; -import com.dogecoin.dogecoinj.store.MemoryBlockStore; -import org.junit.Before; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; -import java.nio.ByteBuffer; -import java.util.Arrays; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeBlock; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeTx; -import static org.junit.Assert.*; - -public class LazyParseByteCacheTest { - - private final byte[] txMessage = HEX.withSeparator(" ", 2).decode( - "f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" + - "02 01 00 00 e2 93 cd be 01 00 00 00 01 6d bd db" + - "08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + - "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + - "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + - "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a" + - "59 23 3f 45 bc 57 83 80 02 20 59 af 01 ca 17 d0" + - "0e 41 83 7a 1d 58 e9 7a a3 1b ae 58 4e de c2 8d" + - "35 bd 96 92 36 90 91 3b ae 9a 01 41 04 9c 02 bf" + - "c9 7e f2 36 ce 6d 8f e5 d9 40 13 c7 21 e9 15 98" + - "2a cd 2b 12 b6 5d 9b 7d 59 e2 0a 84 20 05 f8 fc" + - "4e 02 53 2e 87 3d 37 b9 6f 09 d6 d4 51 1a da 8f" + - "14 04 2f 46 61 4a 4c 70 c0 f1 4b ef f5 ff ff ff" + - "ff 02 40 4b 4c 00 00 00 00 00 19 76 a9 14 1a a0" + - "cd 1c be a6 e7 45 8a 7a ba d5 12 a9 d9 ea 1a fb" + - "22 5e 88 ac 80 fa e9 c7 00 00 00 00 19 76 a9 14" + - "0e ab 5b ea 43 6a 04 84 cf ab 12 48 5e fd a0 b7" + - "8b 4e cc 52 88 ac 00 00 00 00"); - - private final byte[] txMessagePart = HEX.withSeparator(" ", 2).decode( - "08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + - "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + - "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + - "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a"); - - private Wallet wallet; - private BlockStore blockStore; - private NetworkParameters unitTestParams; - - private byte[] b1Bytes; - private byte[] b1BytesWithHeader; - - private byte[] tx1Bytes; - private byte[] tx1BytesWithHeader; - - private byte[] tx2Bytes; - private byte[] tx2BytesWithHeader; - - private void resetBlockStore() { - blockStore = new MemoryBlockStore(unitTestParams); - } - - @Before - public void setUp() throws Exception { - unitTestParams = UnitTestParams.get(); - wallet = new Wallet(unitTestParams); - wallet.freshReceiveKey(); - - resetBlockStore(); - - Transaction tx1 = createFakeTx(unitTestParams, - valueOf(2, 0), - wallet.currentReceiveKey().toAddress(unitTestParams)); - - //add a second input so can test granularity of byte cache. - Transaction prevTx = new Transaction(unitTestParams); - TransactionOutput prevOut = new TransactionOutput(unitTestParams, prevTx, COIN, wallet.currentReceiveKey().toAddress(unitTestParams)); - prevTx.addOutput(prevOut); - // Connect it. - tx1.addInput(prevOut); - - Transaction tx2 = createFakeTx(unitTestParams, COIN, - new ECKey().toAddress(unitTestParams)); - - Block b1 = createFakeBlock(blockStore, tx1, tx2).block; - - BitcoinSerializer bs = new BitcoinSerializer(unitTestParams); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bs.serialize(tx1, bos); - tx1BytesWithHeader = bos.toByteArray(); - tx1Bytes = tx1.bitcoinSerialize(); - - bos.reset(); - bs.serialize(tx2, bos); - tx2BytesWithHeader = bos.toByteArray(); - tx2Bytes = tx2.bitcoinSerialize(); - - bos.reset(); - bs.serialize(b1, bos); - b1BytesWithHeader = bos.toByteArray(); - b1Bytes = b1.bitcoinSerialize(); - } - - @Test - public void validateSetup() { - byte[] b1 = new byte[] {1, 1, 1, 2, 3, 4, 5, 6, 7}; - byte[] b2 = new byte[] {1, 2, 3}; - assertTrue(arrayContains(b1, b2)); - assertTrue(arrayContains(txMessage, txMessagePart)); - assertTrue(arrayContains(tx1BytesWithHeader, tx1Bytes)); - assertTrue(arrayContains(tx2BytesWithHeader, tx2Bytes)); - assertTrue(arrayContains(b1BytesWithHeader, b1Bytes)); - assertTrue(arrayContains(b1BytesWithHeader, tx1Bytes)); - assertTrue(arrayContains(b1BytesWithHeader, tx2Bytes)); - assertFalse(arrayContains(tx1BytesWithHeader, b1Bytes)); - } - - @Test - public void testTransactionsLazyRetain() throws Exception { - testTransaction(MainNetParams.get(), txMessage, false, true, true); - testTransaction(unitTestParams, tx1BytesWithHeader, false, true, true); - testTransaction(unitTestParams, tx2BytesWithHeader, false, true, true); - } - - @Test - public void testTransactionsLazyNoRetain() throws Exception { - testTransaction(MainNetParams.get(), txMessage, false, true, false); - testTransaction(unitTestParams, tx1BytesWithHeader, false, true, false); - testTransaction(unitTestParams, tx2BytesWithHeader, false, true, false); - } - - @Test - public void testTransactionsNoLazyNoRetain() throws Exception { - testTransaction(MainNetParams.get(), txMessage, false, false, false); - testTransaction(unitTestParams, tx1BytesWithHeader, false, false, false); - testTransaction(unitTestParams, tx2BytesWithHeader, false, false, false); - } - - @Test - public void testTransactionsNoLazyRetain() throws Exception { - testTransaction(MainNetParams.get(), txMessage, false, false, true); - testTransaction(unitTestParams, tx1BytesWithHeader, false, false, true); - testTransaction(unitTestParams, tx2BytesWithHeader, false, false, true); - } - - @Test - public void testBlockAll() throws Exception { - testBlock(b1BytesWithHeader, false, false, false); - testBlock(b1BytesWithHeader, false, true, true); - testBlock(b1BytesWithHeader, false, true, false); - testBlock(b1BytesWithHeader, false, false, true); - } - - - public void testBlock(byte[] blockBytes, boolean isChild, boolean lazy, boolean retain) throws Exception { - //reference serializer to produce comparison serialization output after changes to - //message structure. - BitcoinSerializer bsRef = new BitcoinSerializer(unitTestParams, false, false); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - BitcoinSerializer bs = new BitcoinSerializer(unitTestParams, lazy, retain); - Block b1; - Block bRef; - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //verify our reference BitcoinSerializer produces matching byte array. - bos.reset(); - bsRef.serialize(bRef, bos); - assertTrue(Arrays.equals(bos.toByteArray(), blockBytes)); - - //check lazy and retain status survive both before and after a serialization - assertEquals(!lazy, b1.isParsedTransactions()); - assertEquals(!lazy, b1.isParsedHeader()); - if (b1.isParsedHeader()) - assertEquals(retain, b1.isHeaderBytesValid()); - if (b1.isParsedTransactions()) - assertEquals(retain, b1.isTransactionBytesValid()); - - serDeser(bs, b1, blockBytes, null, null); - - assertEquals(!lazy, b1.isParsedTransactions()); - assertEquals(!lazy, b1.isParsedHeader()); - if (b1.isParsedHeader()) - assertEquals(retain, b1.isHeaderBytesValid()); - if (b1.isParsedTransactions()) - assertEquals(retain, b1.isTransactionBytesValid()); - - //compare to ref block - bos.reset(); - bsRef.serialize(bRef, bos); - serDeser(bs, b1, bos.toByteArray(), null, null); - - //retrieve a value from a child - b1.getTransactions(); - assertTrue(b1.isParsedTransactions()); - if (b1.getTransactions().size() > 0) { - assertTrue(b1.isParsedTransactions()); - Transaction tx1 = b1.getTransactions().get(0); - - //this will always be true for all children of a block once they are retrieved. - //the tx child inputs/outputs may not be parsed however. - - //no longer forced to parse if length not provided. - //assertEquals(true, tx1.isParsed()); - if (tx1.isParsed()) - assertEquals(retain, tx1.isCached()); - else - assertTrue(tx1.isCached()); - - //does it still match ref block? - serDeser(bs, b1, bos.toByteArray(), null, null); - } - - //refresh block - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //retrieve a value from header - b1.getDifficultyTarget(); - assertTrue(b1.isParsedHeader()); - assertEquals(lazy, !b1.isParsedTransactions()); - - //does it still match ref block? - serDeser(bs, b1, bos.toByteArray(), null, null); - - - //refresh block - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //retrieve a value from a child and header - b1.getDifficultyTarget(); - assertTrue(b1.isParsedHeader()); - assertEquals(lazy, !b1.isParsedTransactions()); - - b1.getTransactions(); - assertTrue(b1.isParsedTransactions()); - if (b1.getTransactions().size() > 0) { - assertTrue(b1.isParsedTransactions()); - Transaction tx1 = b1.getTransactions().get(0); - - //no longer forced to parse if length not provided. - //assertEquals(true, tx1.isParsed()); - - if (tx1.isParsed()) - assertEquals(retain, tx1.isCached()); - else - assertTrue(tx1.isCached()); - } - //does it still match ref block? - serDeser(bs, b1, bos.toByteArray(), null, null); - - //refresh block - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //change a value in header - b1.setNonce(23); - bRef.setNonce(23); - assertTrue(b1.isParsedHeader()); - assertEquals(lazy, !b1.isParsedTransactions()); - assertFalse(b1.isHeaderBytesValid()); - if (b1.isParsedTransactions()) - assertEquals(retain , b1.isTransactionBytesValid()); - else - assertEquals(true, b1.isTransactionBytesValid()); - //does it still match ref block? - bos.reset(); - bsRef.serialize(bRef, bos); - serDeser(bs, b1, bos.toByteArray(), null, null); - - //refresh block - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //retrieve a value from a child of a child - b1.getTransactions(); - if (b1.getTransactions().size() > 0) { - Transaction tx1 = b1.getTransactions().get(0); - - TransactionInput tin = tx1.getInputs().get(0); - - assertTrue(tx1.isParsed()); - assertTrue(b1.isParsedTransactions()); - assertEquals(!lazy, b1.isParsedHeader()); - - assertEquals(!lazy, tin.isParsed()); - assertEquals(tin.isParsed() ? retain : true, tin.isCached()); - - //does it still match ref tx? - bos.reset(); - bsRef.serialize(bRef, bos); - serDeser(bs, b1, bos.toByteArray(), null, null); - } - - //refresh block - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //add an input - b1.getTransactions(); - if (b1.getTransactions().size() > 0) { - Transaction tx1 = b1.getTransactions().get(0); - - if (tx1.getInputs().size() > 0) { - tx1.addInput(tx1.getInputs().get(0)); - //replicate on reference tx - bRef.getTransactions().get(0).addInput(bRef.getTransactions().get(0).getInputs().get(0)); - - assertFalse(tx1.isCached()); - assertTrue(tx1.isParsed()); - assertFalse(b1.isTransactionBytesValid()); - assertTrue(b1.isParsedHeader()); - - //confirm sibling cache status was unaffected - if (tx1.getInputs().size() > 1) { - boolean parsed = tx1.getInputs().get(1).isParsed(); - assertEquals(parsed ? retain : true, tx1.getInputs().get(1).isCached()); - assertEquals(!lazy, parsed); - } - - //this has to be false. Altering a tx invalidates the merkle root. - //when we have seperate merkle caching then the entire header won't need to be - //invalidated. - assertFalse(b1.isHeaderBytesValid()); - - bos.reset(); - bsRef.serialize(bRef, bos); - byte[] source = bos.toByteArray(); - //confirm we still match the reference tx. - serDeser(bs, b1, source, null, null); - } - - //does it still match ref tx? - bos.reset(); - bsRef.serialize(bRef, bos); - serDeser(bs, b1, bos.toByteArray(), null, null); - } - - //refresh block - b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - Block b2 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes)); - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - Block bRef2 = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - - //reparent an input - b1.getTransactions(); - if (b1.getTransactions().size() > 0) { - Transaction tx1 = b1.getTransactions().get(0); - Transaction tx2 = b2.getTransactions().get(0); - - if (tx1.getInputs().size() > 0) { - TransactionInput fromTx1 = tx1.getInputs().get(0); - tx2.addInput(fromTx1); - - //replicate on reference tx - TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0); - bRef2.getTransactions().get(0).addInput(fromTxRef); - - //b1 hasn't changed but it's no longer in the parent - //chain of fromTx1 so has to have been uncached since it won't be - //notified of changes throught the parent chain anymore. - assertFalse(b1.isTransactionBytesValid()); - - //b2 should have it's cache invalidated because it has changed. - assertFalse(b2.isTransactionBytesValid()); - - bos.reset(); - bsRef.serialize(bRef2, bos); - byte[] source = bos.toByteArray(); - //confirm altered block matches altered ref block. - serDeser(bs, b2, source, null, null); - } - - //does unaltered block still match ref block? - bos.reset(); - bsRef.serialize(bRef, bos); - serDeser(bs, b1, bos.toByteArray(), null, null); - - //how about if we refresh it? - bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes)); - bos.reset(); - bsRef.serialize(bRef, bos); - serDeser(bs, b1, bos.toByteArray(), null, null); - } - - } - - public void testTransaction(NetworkParameters params, byte[] txBytes, boolean isChild, boolean lazy, boolean retain) throws Exception { - - //reference serializer to produce comparison serialization output after changes to - //message structure. - BitcoinSerializer bsRef = new BitcoinSerializer(params, false, false); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - BitcoinSerializer bs = new BitcoinSerializer(params, lazy, retain); - Transaction t1; - Transaction tRef; - t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes)); - tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes)); - - //verify our reference BitcoinSerializer produces matching byte array. - bos.reset(); - bsRef.serialize(tRef, bos); - assertTrue(Arrays.equals(bos.toByteArray(), txBytes)); - - //check lazy and retain status survive both before and after a serialization - assertEquals(!lazy, t1.isParsed()); - if (t1.isParsed()) - assertEquals(retain, t1.isCached()); - - serDeser(bs, t1, txBytes, null, null); - - assertEquals(lazy, !t1.isParsed()); - if (t1.isParsed()) - assertEquals(retain, t1.isCached()); - - //compare to ref tx - bos.reset(); - bsRef.serialize(tRef, bos); - serDeser(bs, t1, bos.toByteArray(), null, null); - - //retrieve a value from a child - t1.getInputs(); - assertTrue(t1.isParsed()); - if (t1.getInputs().size() > 0) { - assertTrue(t1.isParsed()); - TransactionInput tin = t1.getInputs().get(0); - assertEquals(!lazy, tin.isParsed()); - if (tin.isParsed()) - assertEquals(retain, tin.isCached()); - - //does it still match ref tx? - serDeser(bs, t1, bos.toByteArray(), null, null); - } - - //refresh tx - t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes)); - tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes)); - - //add an input - if (t1.getInputs().size() > 0) { - - t1.addInput(t1.getInputs().get(0)); - - //replicate on reference tx - tRef.addInput(tRef.getInputs().get(0)); - - assertFalse(t1.isCached()); - assertTrue(t1.isParsed()); - - bos.reset(); - bsRef.serialize(tRef, bos); - byte[] source = bos.toByteArray(); - //confirm we still match the reference tx. - serDeser(bs, t1, source, null, null); - } - - } - - private void serDeser(BitcoinSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bs.serialize(message, bos); - byte[] b1 = bos.toByteArray(); - - Message m2 = bs.deserialize(ByteBuffer.wrap(b1)); - - assertEquals(message, m2); - - bos.reset(); - bs.serialize(m2, bos); - byte[] b2 = bos.toByteArray(); - assertTrue(Arrays.equals(b1, b2)); - - if (sourceBytes != null) { - assertTrue(arrayContains(sourceBytes, b1)); - - assertTrue(arrayContains(sourceBytes, b2)); - } - - if (containedBytes != null) { - assertTrue(arrayContains(b1, containedBytes)); - } - if (containingBytes != null) { - assertTrue(arrayContains(containingBytes, b1)); - } - } - - public static boolean arrayContains(byte[] sup, byte[] sub) { - if (sup.length < sub.length) - return false; - - String superstring = Utils.HEX.encode(sup); - String substring = Utils.HEX.encode(sub); - - int ind = superstring.indexOf(substring); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < superstring.indexOf(substring); i++) - sb.append(" "); - - //System.out.println(superstring); - //System.out.println(sb.append(substring).toString()); - //System.out.println(); - return ind > -1; - - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/MemoryFullPrunedBlockChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/MemoryFullPrunedBlockChainTest.java deleted file mode 100644 index 6cdf66a4..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/MemoryFullPrunedBlockChainTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.store.BlockStoreException; -import com.dogecoin.dogecoinj.store.FullPrunedBlockStore; -import com.dogecoin.dogecoinj.store.MemoryFullPrunedBlockStore; - -/** - * A MemoryStore implementation of the FullPrunedBlockStoreTest - */ -public class MemoryFullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest -{ - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) throws BlockStoreException - { - return new MemoryFullPrunedBlockStore(params, blockCount); - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException - { - //No-op for memory store, because it's not persistent - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/MemoryPoolTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/MemoryPoolTest.java deleted file mode 100644 index 3d0b2564..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/MemoryPoolTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import org.junit.Before; -import org.junit.Test; - -import java.net.InetAddress; - -import static com.dogecoin.dogecoinj.core.Coin.COIN; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class MemoryPoolTest { - private NetworkParameters params = UnitTestParams.get(); - private Transaction tx1, tx2; - private PeerAddress address1, address2, address3; - - @Before - public void setup() throws Exception { - BriefLogFormatter.init(); - tx1 = FakeTxBuilder.createFakeTx(params, COIN, new ECKey().toAddress(params)); - tx2 = new Transaction(params, tx1.bitcoinSerialize()); - - address1 = new PeerAddress(InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 })); - address2 = new PeerAddress(InetAddress.getByAddress(new byte[] { 127, 0, 0, 2 })); - address3 = new PeerAddress(InetAddress.getByAddress(new byte[] { 127, 0, 0, 3 })); - } - - @Test - public void canonicalInstance() throws Exception { - TxConfidenceTable table = new TxConfidenceTable(); - // Check that if we repeatedly send it the same transaction but with different objects, we get back the same - // canonical instance with the confidences update. - assertEquals(0, table.numBroadcastPeers(tx1.getHash())); - assertEquals(tx1, table.seen(tx1, address1)); - assertEquals(1, tx1.getConfidence().numBroadcastPeers()); - assertEquals(1, table.numBroadcastPeers(tx1.getHash())); - assertEquals(tx1, table.seen(tx2, address2)); - assertEquals(2, tx1.getConfidence().numBroadcastPeers()); - assertEquals(2, table.numBroadcastPeers(tx1.getHash())); - assertEquals(tx1, table.get(tx1.getHash())); - } - - @Test - public void invAndDownload() throws Exception { - TxConfidenceTable table = new TxConfidenceTable(); - // Base case: we see a transaction announced twice and then download it. The count is in the confidence object. - assertEquals(0, table.numBroadcastPeers(tx1.getHash())); - table.seen(tx1.getHash(), address1); - assertEquals(1, table.numBroadcastPeers(tx1.getHash())); - assertTrue(table.maybeWasSeen(tx1.getHash())); - table.seen(tx1.getHash(), address2); - assertEquals(2, table.numBroadcastPeers(tx1.getHash())); - Transaction t = table.seen(tx1, address1); - assertEquals(2, t.getConfidence().numBroadcastPeers()); - // And now we see another inv. - table.seen(tx1.getHash(), address3); - assertEquals(3, t.getConfidence().numBroadcastPeers()); - assertEquals(3, table.numBroadcastPeers(tx1.getHash())); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/MessageTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/MessageTest.java deleted file mode 100644 index 8d18ad36..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/MessageTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 Piotr Włodarek - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.UnitTestParams; -import org.junit.Test; - -public class MessageTest { - - // If readStr() is vulnerable this causes OutOfMemory - @Test(expected = ProtocolException.class) - public void readStrOfExtremeLength() throws Exception { - NetworkParameters params = UnitTestParams.get(); - VarInt length = new VarInt(Integer.MAX_VALUE); - byte[] payload = length.encode(); - new VarStrMessage(params, payload); - } - - static class VarStrMessage extends Message { - public VarStrMessage(NetworkParameters params, byte[] payload) { - super(params, payload, 0); - } - - @Override - void parse() throws ProtocolException { - readStr(); - } - - @Override - protected void parseLite() throws ProtocolException {} - } - - // If readBytes() is vulnerable this causes OutOfMemory - @Test(expected = ProtocolException.class) - public void readByteArrayOfExtremeLength() throws Exception { - NetworkParameters params = UnitTestParams.get(); - VarInt length = new VarInt(Integer.MAX_VALUE); - byte[] payload = length.encode(); - new VarBytesMessage(params, payload); - } - - static class VarBytesMessage extends Message { - public VarBytesMessage(NetworkParameters params, byte[] payload) { - super(params, payload, 0); - } - - @Override - void parse() throws ProtocolException { - readByteArray(); - } - - @Override - protected void parseLite() throws ProtocolException {} - } - -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/MySQLFullPrunedBlockChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/MySQLFullPrunedBlockChainTest.java deleted file mode 100644 index 9b361b4c..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/MySQLFullPrunedBlockChainTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014 Kalpesh Parmar. - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.store.BlockStoreException; -import com.dogecoin.dogecoinj.store.FullPrunedBlockStore; -import com.dogecoin.dogecoinj.store.MySQLFullPrunedBlockStore; -import org.junit.After; -import org.junit.Ignore; - -/** - * A MySQL implementation of the {@link AbstractFullPrunedBlockChainTest} - */ -@Ignore("enable the mysql driver dependency in the maven POM") -public class MySQLFullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest { - - @After - public void tearDown() throws Exception { - ((MySQLFullPrunedBlockStore)store).deleteStore(); - } - - // Replace these with your mysql location/credentials and remove @Ignore to test - private static final String DB_HOSTNAME = "localhost"; - private static final String DB_NAME = "bitcoinj_test"; - private static final String DB_USERNAME = "bitcoinj"; - private static final String DB_PASSWORD = "password"; - - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) - throws BlockStoreException { - return new MySQLFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD); - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { - ((MySQLFullPrunedBlockStore)store).resetStore(); - } -} \ No newline at end of file diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/PeerAddressTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/PeerAddressTest.java deleted file mode 100644 index ae8a19a3..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/PeerAddressTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.MainNetParams; -import org.junit.Test; - -import java.net.InetAddress; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.assertEquals; - -public class PeerAddressTest -{ - @Test - public void testPeerAddressRoundtrip() throws Exception { - // copied verbatim from https://en.bitcoin.it/wiki/Protocol_specification#Network_address - String fromSpec = "010000000000000000000000000000000000ffff0a000001208d"; - PeerAddress pa = new PeerAddress(MainNetParams.get(), - HEX.decode(fromSpec), 0, 0); - String reserialized = Utils.HEX.encode(pa.bitcoinSerialize()); - assertEquals(reserialized,fromSpec ); - } - - @Test - public void testBitcoinSerialize() throws Exception { - PeerAddress pa = new PeerAddress(InetAddress.getByName(null), 8333, 0); - assertEquals("000000000000000000000000000000000000ffff7f000001208d", - Utils.HEX.encode(pa.bitcoinSerialize())); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/PeerTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/PeerTest.java deleted file mode 100644 index 8f446c07..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/PeerTest.java +++ /dev/null @@ -1,872 +0,0 @@ -/* - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.testing.InboundMessageQueuer; -import com.dogecoin.dogecoinj.testing.TestWithNetworkConnections; -import com.dogecoin.dogecoinj.utils.Threading; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; -import com.google.common.util.concurrent.Uninterruptibles; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.nio.channels.CancelledKeyException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.*; -import static org.junit.Assert.*; - -@RunWith(value = Parameterized.class) -public class PeerTest extends TestWithNetworkConnections { - private Peer peer; - private InboundMessageQueuer writeTarget; - private static final int OTHER_PEER_CHAIN_HEIGHT = 110; - private TxConfidenceTable confidenceTable; - private final AtomicBoolean fail = new AtomicBoolean(false); - - - @Parameterized.Parameters - public static Collection parameters() { - return Arrays.asList(new ClientType[] {ClientType.NIO_CLIENT_MANAGER}, - new ClientType[] {ClientType.BLOCKING_CLIENT_MANAGER}, - new ClientType[] {ClientType.NIO_CLIENT}, - new ClientType[] {ClientType.BLOCKING_CLIENT}); - } - - public PeerTest(ClientType clientType) { - super(clientType); - } - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - - confidenceTable = blockChain.getContext().getConfidenceTable(); - VersionMessage ver = new VersionMessage(unitTestParams, 100); - InetSocketAddress address = new InetSocketAddress("127.0.0.1", 4000); - peer = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain); - peer.addWallet(wallet); - } - - @Override - @After - public void tearDown() throws Exception { - super.tearDown(); - assertFalse(fail.get()); - } - - private void connect() throws Exception { - connectWithVersion(70001); - } - - private void connectWithVersion(int version) throws Exception { - VersionMessage peerVersion = new VersionMessage(unitTestParams, OTHER_PEER_CHAIN_HEIGHT); - peerVersion.clientVersion = version; - peerVersion.localServices = VersionMessage.NODE_NETWORK; - writeTarget = connect(peer, peerVersion); - } - - @Test - public void testAddEventListener() throws Exception { - connect(); - PeerEventListener listener = new AbstractPeerEventListener(); - peer.addEventListener(listener); - assertTrue(peer.removeEventListener(listener)); - assertFalse(peer.removeEventListener(listener)); - } - - // Check that it runs through the event loop and shut down correctly - @Test - public void shutdown() throws Exception { - closePeer(peer); - } - - @Test - public void chainDownloadEnd2End() throws Exception { - // A full end-to-end test of the chain download process, with a new block being solved in the middle. - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - Block b2 = makeSolvedTestBlock(b1); - Block b3 = makeSolvedTestBlock(b2); - Block b4 = makeSolvedTestBlock(b3); - Block b5 = makeSolvedTestBlock(b4); - - connect(); - - peer.startBlockChainDownload(); - GetBlocksMessage getblocks = (GetBlocksMessage)outbound(writeTarget); - assertEquals(blockStore.getChainHead().getHeader().getHash(), getblocks.getLocator().get(0)); - assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); - // Remote peer sends us an inv with some blocks. - InventoryMessage inv = new InventoryMessage(unitTestParams); - inv.addBlock(b2); - inv.addBlock(b3); - // We do a getdata on them. - inbound(writeTarget, inv); - GetDataMessage getdata = (GetDataMessage)outbound(writeTarget); - assertEquals(b2.getHash(), getdata.getItems().get(0).hash); - assertEquals(b3.getHash(), getdata.getItems().get(1).hash); - assertEquals(2, getdata.getItems().size()); - // Remote peer sends us the blocks. The act of doing a getdata for b3 results in getting an inv with just the - // best chain head in it. - inbound(writeTarget, b2); - inbound(writeTarget, b3); - - inv = new InventoryMessage(unitTestParams); - inv.addBlock(b5); - // We request the head block. - inbound(writeTarget, inv); - getdata = (GetDataMessage)outbound(writeTarget); - assertEquals(b5.getHash(), getdata.getItems().get(0).hash); - assertEquals(1, getdata.getItems().size()); - // Peer sends us the head block. The act of receiving the orphan block triggers a getblocks to fill in the - // rest of the chain. - inbound(writeTarget, b5); - getblocks = (GetBlocksMessage)outbound(writeTarget); - assertEquals(b5.getHash(), getblocks.getStopHash()); - assertEquals(b3.getHash(), getblocks.getLocator().get(0)); - // At this point another block is solved and broadcast. The inv triggers a getdata but we do NOT send another - // getblocks afterwards, because that would result in us receiving the same set of blocks twice which is a - // timewaste. The getblocks message that would have been generated is set to be the same as the previous - // because we walk backwards down the orphan chain and then discover we already asked for those blocks, so - // nothing is done. - Block b6 = makeSolvedTestBlock(b5); - inv = new InventoryMessage(unitTestParams); - inv.addBlock(b6); - inbound(writeTarget, inv); - getdata = (GetDataMessage)outbound(writeTarget); - assertEquals(1, getdata.getItems().size()); - assertEquals(b6.getHash(), getdata.getItems().get(0).hash); - inbound(writeTarget, b6); - assertNull(outbound(writeTarget)); // Nothing is sent at this point. - // We're still waiting for the response to the getblocks (b3,b5) sent above. - inv = new InventoryMessage(unitTestParams); - inv.addBlock(b4); - inv.addBlock(b5); - inbound(writeTarget, inv); - getdata = (GetDataMessage)outbound(writeTarget); - assertEquals(1, getdata.getItems().size()); - assertEquals(b4.getHash(), getdata.getItems().get(0).hash); - // We already have b5 from before, so it's not requested again. - inbound(writeTarget, b4); - assertNull(outbound(writeTarget)); - // b5 and b6 are now connected by the block chain and we're done. - assertNull(outbound(writeTarget)); - closePeer(peer); - } - - // Check that an inventory tickle is processed correctly when downloading missing blocks is active. - @Test - public void invTickle() throws Exception { - connect(); - - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - // Make a missing block. - Block b2 = makeSolvedTestBlock(b1); - Block b3 = makeSolvedTestBlock(b2); - inbound(writeTarget, b3); - InventoryMessage inv = new InventoryMessage(unitTestParams); - InventoryItem item = new InventoryItem(InventoryItem.Type.Block, b3.getHash()); - inv.addItem(item); - inbound(writeTarget, inv); - - GetBlocksMessage getblocks = (GetBlocksMessage)outbound(writeTarget); - List expectedLocator = new ArrayList(); - expectedLocator.add(b1.getHash()); - expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); - - assertEquals(getblocks.getLocator(), expectedLocator); - assertEquals(getblocks.getStopHash(), b3.getHash()); - assertNull(outbound(writeTarget)); - } - - // Check that an inv to a peer that is not set to download missing blocks does nothing. - @Test - public void invNoDownload() throws Exception { - // Don't download missing blocks. - peer.setDownloadData(false); - - connect(); - - // Make a missing block that we receive. - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - Block b2 = makeSolvedTestBlock(b1); - - // Receive an inv. - InventoryMessage inv = new InventoryMessage(unitTestParams); - InventoryItem item = new InventoryItem(InventoryItem.Type.Block, b2.getHash()); - inv.addItem(item); - inbound(writeTarget, inv); - - // Peer does nothing with it. - assertNull(outbound(writeTarget)); - } - - @Test - public void invDownloadTx() throws Exception { - connect(); - - peer.setDownloadData(true); - // Make a transaction and tell the peer we have it. - Coin value = COIN; - Transaction tx = createFakeTx(unitTestParams, value, address); - InventoryMessage inv = new InventoryMessage(unitTestParams); - InventoryItem item = new InventoryItem(InventoryItem.Type.Transaction, tx.getHash()); - inv.addItem(item); - inbound(writeTarget, inv); - // Peer hasn't seen it before, so will ask for it. - GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(1, getdata.getItems().size()); - assertEquals(tx.getHash(), getdata.getItems().get(0).hash); - inbound(writeTarget, tx); - // Ask for the dependency, it's not in the mempool (in chain). - getdata = (GetDataMessage) outbound(writeTarget); - inbound(writeTarget, new NotFoundMessage(unitTestParams, getdata.getItems())); - pingAndWait(writeTarget); - assertEquals(value, wallet.getBalance(Wallet.BalanceType.ESTIMATED)); - } - - @Test - public void invDownloadTxMultiPeer() throws Exception { - // Check co-ordination of which peer to download via the memory pool. - VersionMessage ver = new VersionMessage(unitTestParams, 100); - InetSocketAddress address = new InetSocketAddress("127.0.0.1", 4242); - Peer peer2 = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain); - peer2.addWallet(wallet); - VersionMessage peerVersion = new VersionMessage(unitTestParams, OTHER_PEER_CHAIN_HEIGHT); - peerVersion.clientVersion = 70001; - peerVersion.localServices = VersionMessage.NODE_NETWORK; - - connect(); - InboundMessageQueuer writeTarget2 = connect(peer2, peerVersion); - - // Make a tx and advertise it to one of the peers. - Coin value = COIN; - Transaction tx = createFakeTx(unitTestParams, value, this.address); - InventoryMessage inv = new InventoryMessage(unitTestParams); - InventoryItem item = new InventoryItem(InventoryItem.Type.Transaction, tx.getHash()); - inv.addItem(item); - - inbound(writeTarget, inv); - - // We got a getdata message. - GetDataMessage message = (GetDataMessage)outbound(writeTarget); - assertEquals(1, message.getItems().size()); - assertEquals(tx.getHash(), message.getItems().get(0).hash); - assertTrue(confidenceTable.maybeWasSeen(tx.getHash())); - - // Advertising to peer2 results in no getdata message. - inbound(writeTarget2, inv); - pingAndWait(writeTarget2); - assertNull(outbound(writeTarget2)); - } - - // Check that inventory message containing blocks we want is processed correctly. - @Test - public void newBlock() throws Exception { - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - final Block b2 = makeSolvedTestBlock(b1); - // Receive notification of a new block. - final InventoryMessage inv = new InventoryMessage(unitTestParams); - InventoryItem item = new InventoryItem(InventoryItem.Type.Block, b2.getHash()); - inv.addItem(item); - - final AtomicInteger newBlockMessagesReceived = new AtomicInteger(0); - - connect(); - // Round-trip a ping so that we never see the response verack if we attach too quick - pingAndWait(writeTarget); - peer.addEventListener(new AbstractPeerEventListener() { - @Override - public synchronized Message onPreMessageReceived(Peer p, Message m) { - if (p != peer) - fail.set(true); - if (m instanceof Pong) - return m; - int newValue = newBlockMessagesReceived.incrementAndGet(); - if (newValue == 1 && !inv.equals(m)) - fail.set(true); - else if (newValue == 2 && !b2.equals(m)) - fail.set(true); - else if (newValue > 3) - fail.set(true); - return m; - } - - @Override - public synchronized void onBlocksDownloaded(Peer p, Block block, int blocksLeft) { - int newValue = newBlockMessagesReceived.incrementAndGet(); - if (newValue != 3 || p != peer || !block.equals(b2) || blocksLeft != OTHER_PEER_CHAIN_HEIGHT - 2) - fail.set(true); - } - }, Threading.SAME_THREAD); - long height = peer.getBestHeight(); - - inbound(writeTarget, inv); - pingAndWait(writeTarget); - assertEquals(height + 1, peer.getBestHeight()); - // Response to the getdata message. - inbound(writeTarget, b2); - - pingAndWait(writeTarget); - Threading.waitForUserCode(); - pingAndWait(writeTarget); - assertEquals(3, newBlockMessagesReceived.get()); - - GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); - List items = getdata.getItems(); - assertEquals(1, items.size()); - assertEquals(b2.getHash(), items.get(0).hash); - assertEquals(InventoryItem.Type.Block, items.get(0).type); - } - - // Check that it starts downloading the block chain correctly on request. - @Test - public void startBlockChainDownload() throws Exception { - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - Block b2 = makeSolvedTestBlock(b1); - blockChain.add(b2); - - connect(); - fail.set(true); - peer.addEventListener(new AbstractPeerEventListener() { - @Override - public void onChainDownloadStarted(Peer p, int blocksLeft) { - if (p == peer && blocksLeft == 108) - fail.set(false); - } - }, Threading.SAME_THREAD); - peer.startBlockChainDownload(); - - List expectedLocator = new ArrayList(); - expectedLocator.add(b2.getHash()); - expectedLocator.add(b1.getHash()); - expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); - - GetBlocksMessage message = (GetBlocksMessage) outbound(writeTarget); - assertEquals(message.getLocator(), expectedLocator); - assertEquals(Sha256Hash.ZERO_HASH, message.getStopHash()); - } - - @Test - public void getBlock() throws Exception { - connect(); - - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - Block b2 = makeSolvedTestBlock(b1); - Block b3 = makeSolvedTestBlock(b2); - - // Request the block. - Future resultFuture = peer.getBlock(b3.getHash()); - assertFalse(resultFuture.isDone()); - // Peer asks for it. - GetDataMessage message = (GetDataMessage) outbound(writeTarget); - assertEquals(message.getItems().get(0).hash, b3.getHash()); - assertFalse(resultFuture.isDone()); - // Peer receives it. - inbound(writeTarget, b3); - Block b = resultFuture.get(); - assertEquals(b, b3); - } - - @Test - public void getLargeBlock() throws Exception { - connect(); - - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - Block b2 = makeSolvedTestBlock(b1); - Transaction t = new Transaction(unitTestParams); - t.addInput(b1.getTransactions().get(0).getOutput(0)); - t.addOutput(new TransactionOutput(unitTestParams, t, Coin.ZERO, new byte[Block.MAX_BLOCK_SIZE - 1000])); - b2.addTransaction(t); - - // Request the block. - Future resultFuture = peer.getBlock(b2.getHash()); - assertFalse(resultFuture.isDone()); - // Peer asks for it. - GetDataMessage message = (GetDataMessage) outbound(writeTarget); - assertEquals(message.getItems().get(0).hash, b2.getHash()); - assertFalse(resultFuture.isDone()); - // Peer receives it. - inbound(writeTarget, b2); - Block b = resultFuture.get(); - assertEquals(b, b2); - } - - @Test - public void fastCatchup() throws Exception { - connect(); - Utils.setMockClock(); - // Check that blocks before the fast catchup point are retrieved using getheaders, and after using getblocks. - // This test is INCOMPLETE because it does not check we handle >2000 blocks correctly. - Block b1 = createFakeBlock(blockStore).block; - blockChain.add(b1); - Utils.rollMockClock(60 * 10); // 10 minutes later. - Block b2 = makeSolvedTestBlock(b1); - b2.setTime(Utils.currentTimeSeconds()); - b2.solve(); - Utils.rollMockClock(60 * 10); // 10 minutes later. - Block b3 = makeSolvedTestBlock(b2); - b3.setTime(Utils.currentTimeSeconds()); - b3.solve(); - Utils.rollMockClock(60 * 10); - Block b4 = makeSolvedTestBlock(b3); - b4.setTime(Utils.currentTimeSeconds()); - b4.solve(); - - // Request headers until the last 2 blocks. - peer.setDownloadParameters(Utils.currentTimeSeconds() - (600*2) + 1, false); - peer.startBlockChainDownload(); - GetHeadersMessage getheaders = (GetHeadersMessage) outbound(writeTarget); - List expectedLocator = new ArrayList(); - expectedLocator.add(b1.getHash()); - expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); - assertEquals(getheaders.getLocator(), expectedLocator); - assertEquals(getheaders.getStopHash(), Sha256Hash.ZERO_HASH); - // Now send all the headers. - HeadersMessage headers = new HeadersMessage(unitTestParams, b2.cloneAsHeader(), - b3.cloneAsHeader(), b4.cloneAsHeader()); - // We expect to be asked for b3 and b4 again, but this time, with a body. - expectedLocator.clear(); - expectedLocator.add(b2.getHash()); - expectedLocator.add(b1.getHash()); - expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); - inbound(writeTarget, headers); - GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget); - assertEquals(expectedLocator, getblocks.getLocator()); - assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); - // We're supposed to get an inv here. - InventoryMessage inv = new InventoryMessage(unitTestParams); - inv.addItem(new InventoryItem(InventoryItem.Type.Block, b3.getHash())); - inbound(writeTarget, inv); - GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(b3.getHash(), getdata.getItems().get(0).hash); - // All done. - inbound(writeTarget, b3); - pingAndWait(writeTarget); - closePeer(peer); - } - - @Test - public void pingPong() throws Exception { - connect(); - Utils.setMockClock(); - // No ping pong happened yet. - assertEquals(Long.MAX_VALUE, peer.getLastPingTime()); - assertEquals(Long.MAX_VALUE, peer.getPingTime()); - ListenableFuture future = peer.ping(); - assertEquals(Long.MAX_VALUE, peer.getLastPingTime()); - assertEquals(Long.MAX_VALUE, peer.getPingTime()); - assertFalse(future.isDone()); - Ping pingMsg = (Ping) outbound(writeTarget); - Utils.rollMockClock(5); - // The pong is returned. - inbound(writeTarget, new Pong(pingMsg.getNonce())); - pingAndWait(writeTarget); - assertTrue(future.isDone()); - long elapsed = future.get(); - assertTrue("" + elapsed, elapsed > 1000); - assertEquals(elapsed, peer.getLastPingTime()); - assertEquals(elapsed, peer.getPingTime()); - // Do it again and make sure it affects the average. - future = peer.ping(); - pingMsg = (Ping) outbound(writeTarget); - Utils.rollMockClock(50); - inbound(writeTarget, new Pong(pingMsg.getNonce())); - elapsed = future.get(); - assertEquals(elapsed, peer.getLastPingTime()); - assertEquals(7250, peer.getPingTime()); - } - - @Test - public void recursiveDependencyDownloadDisabled() throws Exception { - peer.setDownloadTxDependencies(false); - connect(); - // Check that if we request dependency download to be disabled and receive a relevant tx, things work correctly. - Transaction tx = FakeTxBuilder.createFakeTx(unitTestParams, COIN, address); - final Transaction[] result = new Transaction[1]; - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - result[0] = tx; - } - }); - inbound(writeTarget, tx); - pingAndWait(writeTarget); - assertEquals(tx, result[0]); - } - - @Test - public void recursiveDependencyDownload() throws Exception { - // Using ping or notfound? - connectWithVersion(70001); - // Check that we can download all dependencies of an unconfirmed relevant transaction from the mempool. - ECKey to = new ECKey(); - - final Transaction[] onTx = new Transaction[1]; - peer.addEventListener(new AbstractPeerEventListener() { - @Override - public void onTransaction(Peer peer1, Transaction t) { - onTx[0] = t; - } - }, Threading.SAME_THREAD); - - // Make the some fake transactions in the following graph: - // t1 -> t2 -> [t5] - // -> t3 -> t4 -> [t6] - // -> [t7] - // -> [t8] - // The ones in brackets are assumed to be in the chain and are represented only by hashes. - Transaction t2 = FakeTxBuilder.createFakeTx(unitTestParams, COIN, to); - Sha256Hash t5 = t2.getInput(0).getOutpoint().getHash(); - Transaction t4 = FakeTxBuilder.createFakeTx(unitTestParams, COIN, new ECKey()); - Sha256Hash t6 = t4.getInput(0).getOutpoint().getHash(); - t4.addOutput(COIN, new ECKey()); - Transaction t3 = new Transaction(unitTestParams); - t3.addInput(t4.getOutput(0)); - t3.addOutput(COIN, new ECKey()); - Transaction t1 = new Transaction(unitTestParams); - t1.addInput(t2.getOutput(0)); - t1.addInput(t3.getOutput(0)); - Sha256Hash someHash = new Sha256Hash("2b801dd82f01d17bbde881687bf72bc62e2faa8ab8133d36fcb8c3abe7459da6"); - t1.addInput(new TransactionInput(unitTestParams, t1, new byte[]{}, new TransactionOutPoint(unitTestParams, 0, someHash))); - Sha256Hash anotherHash = new Sha256Hash("3b801dd82f01d17bbde881687bf72bc62e2faa8ab8133d36fcb8c3abe7459da6"); - t1.addInput(new TransactionInput(unitTestParams, t1, new byte[]{}, new TransactionOutPoint(unitTestParams, 1, anotherHash))); - t1.addOutput(COIN, to); - t1 = FakeTxBuilder.roundTripTransaction(unitTestParams, t1); - t2 = FakeTxBuilder.roundTripTransaction(unitTestParams, t2); - t3 = FakeTxBuilder.roundTripTransaction(unitTestParams, t3); - t4 = FakeTxBuilder.roundTripTransaction(unitTestParams, t4); - - // Announce the first one. Wait for it to be downloaded. - InventoryMessage inv = new InventoryMessage(unitTestParams); - inv.addTransaction(t1); - inbound(writeTarget, inv); - GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); - Threading.waitForUserCode(); - assertEquals(t1.getHash(), getdata.getItems().get(0).hash); - inbound(writeTarget, t1); - pingAndWait(writeTarget); - assertEquals(t1, onTx[0]); - // We want its dependencies so ask for them. - ListenableFuture> futures = peer.downloadDependencies(t1); - assertFalse(futures.isDone()); - // It will recursively ask for the dependencies of t1: t2, t3, someHash and anotherHash. - getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(4, getdata.getItems().size()); - assertEquals(t2.getHash(), getdata.getItems().get(0).hash); - assertEquals(t3.getHash(), getdata.getItems().get(1).hash); - assertEquals(someHash, getdata.getItems().get(2).hash); - assertEquals(anotherHash, getdata.getItems().get(3).hash); - long nonce = -1; - // For some random reason, t4 is delivered at this point before it's needed - perhaps it was a Bloom filter - // false positive. We do this to check that the mempool is being checked for seen transactions before - // requesting them. - inbound(writeTarget, t4); - // Deliver the requested transactions. - inbound(writeTarget, t2); - inbound(writeTarget, t3); - NotFoundMessage notFound = new NotFoundMessage(unitTestParams); - notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, someHash)); - notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, anotherHash)); - inbound(writeTarget, notFound); - assertFalse(futures.isDone()); - // It will recursively ask for the dependencies of t2: t5 and t4, but not t3 because it already found t4. - getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(getdata.getItems().get(0).hash, t2.getInput(0).getOutpoint().getHash()); - // t5 isn't found and t4 is. - notFound = new NotFoundMessage(unitTestParams); - notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, t5)); - inbound(writeTarget, notFound); - assertFalse(futures.isDone()); - // Continue to explore the t4 branch and ask for t6, which is in the chain. - getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(t6, getdata.getItems().get(0).hash); - notFound = new NotFoundMessage(unitTestParams); - notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, t6)); - inbound(writeTarget, notFound); - pingAndWait(writeTarget); - // That's it, we explored the entire tree. - assertTrue(futures.isDone()); - List results = futures.get(); - assertTrue(results.contains(t2)); - assertTrue(results.contains(t3)); - assertTrue(results.contains(t4)); - } - - @Test - public void timeLockedTransactionNew() throws Exception { - connectWithVersion(70001); - // Test that if we receive a relevant transaction that has a lock time, it doesn't result in a notification - // until we explicitly opt in to seeing those. - Wallet wallet = new Wallet(unitTestParams); - ECKey key = wallet.freshReceiveKey(); - peer.addWallet(wallet); - final Transaction[] vtx = new Transaction[1]; - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - vtx[0] = tx; - } - }); - // Send a normal relevant transaction, it's received correctly. - Transaction t1 = FakeTxBuilder.createFakeTx(unitTestParams, COIN, key); - inbound(writeTarget, t1); - GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); - inbound(writeTarget, new NotFoundMessage(unitTestParams, getdata.getItems())); - pingAndWait(writeTarget); - Threading.waitForUserCode(); - assertNotNull(vtx[0]); - vtx[0] = null; - // Send a timelocked transaction, nothing happens. - Transaction t2 = FakeTxBuilder.createFakeTx(unitTestParams, valueOf(2, 0), key); - t2.setLockTime(999999); - inbound(writeTarget, t2); - Threading.waitForUserCode(); - assertNull(vtx[0]); - // Now we want to hear about them. Send another, we are told about it. - wallet.setAcceptRiskyTransactions(true); - inbound(writeTarget, t2); - getdata = (GetDataMessage) outbound(writeTarget); - inbound(writeTarget, new NotFoundMessage(unitTestParams, getdata.getItems())); - pingAndWait(writeTarget); - Threading.waitForUserCode(); - assertEquals(t2, vtx[0]); - } - - @Test - public void rejectTimeLockedDependency() throws Exception { - // Check that we also verify the lock times of dependencies. Otherwise an attacker could still build a tx that - // looks legitimate and useful but won't actually ever confirm, by sending us a normal tx that spends a - // timelocked tx. - checkTimeLockedDependency(false); - } - - @Test - public void acceptTimeLockedDependency() throws Exception { - checkTimeLockedDependency(true); - } - - private void checkTimeLockedDependency(boolean shouldAccept) throws Exception { - // Initial setup. - connectWithVersion(70001); - Wallet wallet = new Wallet(unitTestParams); - ECKey key = wallet.freshReceiveKey(); - wallet.setAcceptRiskyTransactions(shouldAccept); - peer.addWallet(wallet); - final Transaction[] vtx = new Transaction[1]; - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - vtx[0] = tx; - } - }); - // t1 -> t2 [locked] -> t3 (not available) - Transaction t2 = new Transaction(unitTestParams); - t2.setLockTime(999999); - // Add a fake input to t3 that goes nowhere. - Sha256Hash t3 = Sha256Hash.create("abc".getBytes(Charset.forName("UTF-8"))); - t2.addInput(new TransactionInput(unitTestParams, t2, new byte[]{}, new TransactionOutPoint(unitTestParams, 0, t3))); - t2.getInput(0).setSequenceNumber(0xDEADBEEF); - t2.addOutput(COIN, new ECKey()); - Transaction t1 = new Transaction(unitTestParams); - t1.addInput(t2.getOutput(0)); - t1.addOutput(COIN, key); // Make it relevant. - // Announce t1. - InventoryMessage inv = new InventoryMessage(unitTestParams); - inv.addTransaction(t1); - inbound(writeTarget, inv); - // Send it. - GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(t1.getHash(), getdata.getItems().get(0).hash); - inbound(writeTarget, t1); - // Nothing arrived at our event listener yet. - assertNull(vtx[0]); - // We request t2. - getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(t2.getHash(), getdata.getItems().get(0).hash); - inbound(writeTarget, t2); - // We request t3. - getdata = (GetDataMessage) outbound(writeTarget); - assertEquals(t3, getdata.getItems().get(0).hash); - // Can't find it: bottom of tree. - NotFoundMessage notFound = new NotFoundMessage(unitTestParams); - notFound.addItem(new InventoryItem(InventoryItem.Type.Transaction, t3)); - inbound(writeTarget, notFound); - pingAndWait(writeTarget); - Threading.waitForUserCode(); - // We're done but still not notified because it was timelocked. - if (shouldAccept) - assertNotNull(vtx[0]); - else - assertNull(vtx[0]); - } - - @Test - public void disconnectOldVersions1() throws Exception { - // Set up the connection with an old version. - final SettableFuture connectedFuture = SettableFuture.create(); - final SettableFuture disconnectedFuture = SettableFuture.create(); - peer.addEventListener(new AbstractPeerEventListener() { - @Override - public void onPeerConnected(Peer peer, int peerCount) { - connectedFuture.set(null); - } - - @Override - public void onPeerDisconnected(Peer peer, int peerCount) { - disconnectedFuture.set(null); - } - }); - connectWithVersion(500); - // We must wait uninterruptibly here because connect[WithVersion] generates a peer that interrupts the current - // thread when it disconnects. - Uninterruptibles.getUninterruptibly(connectedFuture); - Uninterruptibles.getUninterruptibly(disconnectedFuture); - try { - peer.writeTarget.writeBytes(new byte[1]); - fail(); - } catch (IOException e) { - assertTrue((e.getCause() != null && e.getCause() instanceof CancelledKeyException) - || (e instanceof SocketException && e.getMessage().equals("Socket is closed"))); - } - } - - @Test - public void exceptionListener() throws Exception { - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - throw new NullPointerException("boo!"); - } - }); - final Throwable[] throwables = new Throwable[1]; - Threading.uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread thread, Throwable throwable) { - throwables[0] = throwable; - } - }; - // In real usage we're not really meant to adjust the uncaught exception handler after stuff started happening - // but in the unit test environment other tests have just run so the thread is probably still kicking around. - // Force it to crash so it'll be recreated with our new handler. - Threading.USER_THREAD.execute(new Runnable() { - @Override - public void run() { - throw new RuntimeException(); - } - }); - connect(); - Transaction t1 = new Transaction(unitTestParams); - t1.addInput(new TransactionInput(unitTestParams, t1, new byte[]{})); - t1.addOutput(COIN, new ECKey().toAddress(unitTestParams)); - Transaction t2 = new Transaction(unitTestParams); - t2.addInput(t1.getOutput(0)); - t2.addOutput(COIN, wallet.getChangeAddress()); - inbound(writeTarget, t2); - final InventoryItem inventoryItem = new InventoryItem(InventoryItem.Type.Transaction, t2.getInput(0).getOutpoint().getHash()); - final NotFoundMessage nfm = new NotFoundMessage(unitTestParams, Lists.newArrayList(inventoryItem)); - inbound(writeTarget, nfm); - pingAndWait(writeTarget); - Threading.waitForUserCode(); - assertTrue(throwables[0] instanceof NullPointerException); - Threading.uncaughtExceptionHandler = null; - } - - @Test - public void badMessage() throws Exception { - // Bring up an actual network connection and feed it bogus data. - final SettableFuture result = SettableFuture.create(); - Threading.uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread thread, Throwable throwable) { - result.setException(throwable); - } - }; - connect(); // Writes out a verack+version. - final SettableFuture peerDisconnected = SettableFuture.create(); - writeTarget.peer.addEventListener(new AbstractPeerEventListener() { - @Override - public void onPeerDisconnected(Peer p, int peerCount) { - peerDisconnected.set(null); - } - }); - final NetworkParameters params = TestNet3Params.get(); - BitcoinSerializer serializer = new BitcoinSerializer(params); - // Now write some bogus truncated message. - ByteArrayOutputStream out = new ByteArrayOutputStream(); - serializer.serialize("inv", new InventoryMessage(params) { - @Override - public void bitcoinSerializeToStream(OutputStream stream) throws IOException { - // Add some hashes. - addItem(new InventoryItem(InventoryItem.Type.Transaction, Sha256Hash.create(new byte[] { 1 }))); - addItem(new InventoryItem(InventoryItem.Type.Transaction, Sha256Hash.create(new byte[] { 2 }))); - addItem(new InventoryItem(InventoryItem.Type.Transaction, Sha256Hash.create(new byte[] { 3 }))); - - // Write out a copy that's truncated in the middle. - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - super.bitcoinSerializeToStream(bos); - byte[] bits = bos.toByteArray(); - bits = Arrays.copyOf(bits, bits.length / 2); - stream.write(bits); - } - }.bitcoinSerialize(), out); - writeTarget.writeTarget.writeBytes(out.toByteArray()); - try { - result.get(); - fail(); - } catch (ExecutionException e) { - assertTrue(e.getCause() instanceof ProtocolException); - } - peerDisconnected.get(); - try { - peer.writeTarget.writeBytes(new byte[1]); - fail(); - } catch (IOException e) { - assertTrue((e.getCause() != null && e.getCause() instanceof CancelledKeyException) - || (e instanceof SocketException && e.getMessage().equals("Socket is closed"))); - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/PostgresFullPrunedBlockChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/PostgresFullPrunedBlockChainTest.java deleted file mode 100644 index c614f425..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/PostgresFullPrunedBlockChainTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.store.BlockStoreException; -import com.dogecoin.dogecoinj.store.FullPrunedBlockStore; -import com.dogecoin.dogecoinj.store.PostgresFullPrunedBlockStore; -import org.junit.After; -import org.junit.Ignore; -import org.junit.Test; - -/** - * A Postgres implementation of the {@link AbstractFullPrunedBlockChainTest} - */ -@Ignore("enable the postgres driver dependency in the maven POM") -public class PostgresFullPrunedBlockChainTest extends AbstractFullPrunedBlockChainTest -{ - // Replace these with your postgres location/credentials and remove @Ignore to test - // You can set up a fresh postgres with the command: create user bitcoinj superuser password 'password'; - private static final String DB_HOSTNAME = "localhost"; - private static final String DB_NAME = "dogecoinj_test"; - private static final String DB_USERNAME = "dogecoinj"; - private static final String DB_PASSWORD = "password"; - private static final String DB_SCHEMA = "blockstore_schema"; - - // whether to run the test with a schema name - private boolean useSchema = false; - - @After - public void tearDown() throws Exception { - ((PostgresFullPrunedBlockStore)store).deleteStore(); - } - - @Override - public FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) - throws BlockStoreException { - if(useSchema) { - return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD, DB_SCHEMA); - } - else { - return new PostgresFullPrunedBlockStore(params, blockCount, DB_HOSTNAME, DB_NAME, DB_USERNAME, DB_PASSWORD); - } - } - - @Override - public void resetStore(FullPrunedBlockStore store) throws BlockStoreException { - ((PostgresFullPrunedBlockStore)store).resetStore(); - } - - @Test - public void testFirst100kBlocksWithCustomSchema() throws Exception { - boolean oldSchema = useSchema; - useSchema = true; - try { - super.testFirst100KBlocks(); - } finally { - useSchema = oldSchema; - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionOutputTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionOutputTest.java deleted file mode 100644 index 95d8525a..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionOutputTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.google.common.collect.ImmutableList; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.script.Script; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.dogecoin.dogecoinj.script.ScriptOpCodes; -import com.dogecoin.dogecoinj.testing.TestWithWallet; -import org.hamcrest.CoreMatchers; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class TransactionOutputTest extends TestWithWallet { - - @Before - public void setUp() throws Exception { - super.setUp(); - } - - @After - public void tearDown() throws Exception { - super.tearDown(); - } - - @Test - public void testMultiSigOutputToString() throws Exception { - sendMoneyToWallet(Coin.COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN); - ECKey myKey = new ECKey(); - this.wallet.importKey(myKey); - - // Simulate another signatory - ECKey otherKey = new ECKey(); - - // Create multi-sig transaction - Transaction multiSigTransaction = new Transaction(params); - ImmutableList keys = ImmutableList.of(myKey, otherKey); - - Script scriptPubKey = ScriptBuilder.createMultiSigOutputScript(2, keys); - multiSigTransaction.addOutput(Coin.COIN, scriptPubKey); - - Wallet.SendRequest req = Wallet.SendRequest.forTx(multiSigTransaction); - this.wallet.completeTx(req); - TransactionOutput multiSigTransactionOutput = multiSigTransaction.getOutput(0); - - assertThat(multiSigTransactionOutput.toString(), CoreMatchers.containsString("CHECKMULTISIG")); - } - - @Test - public void testP2SHOutputScript() throws Exception { - String P2SHAddressString = "35b9vsyH1KoFT5a5KtrKusaCcPLkiSo1tU"; - Address P2SHAddress = new Address(MainNetParams.get(), P2SHAddressString); - Script script = ScriptBuilder.createOutputScript(P2SHAddress); - Transaction tx = new Transaction(MainNetParams.get()); - tx.addOutput(Coin.COIN, script); - assertEquals(P2SHAddressString, tx.getOutput(0).getAddressFromP2SH(MainNetParams.get()).toString()); - } - - @Test - public void getAddressTests() throws Exception { - Transaction tx = new Transaction(MainNetParams.get()); - Script script = new ScriptBuilder().op(ScriptOpCodes.OP_RETURN).data("hello world!".getBytes()).build(); - tx.addOutput(Coin.CENT, script); - assertNull(tx.getOutput(0).getAddressFromP2SH(params)); - assertNull(tx.getOutput(0).getAddressFromP2PKHScript(params)); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionTest.java deleted file mode 100644 index 0f69727a..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -/** - * Just check the Transaction.verify() method. Most methods that have complicated logic in Transaction are tested - * elsewhere, e.g. signing and hashing are well exercised by the wallet tests, the full block chain tests and so on. - * The verify method is also exercised by the full block chain tests, but it can also be used by API users alone, - * so we make sure to cover it here as well. - */ -public class TransactionTest { - private static final NetworkParameters PARAMS = UnitTestParams.get(); - private Transaction tx; - private Transaction dummy; - - public static final Address ADDRESS = new ECKey().toAddress(PARAMS); - - @Before - public void setUp() throws Exception { - dummy = FakeTxBuilder.createFakeTx(PARAMS, Coin.COIN, ADDRESS); - tx = new Transaction(PARAMS); - tx.addOutput(Coin.COIN, ADDRESS); - tx.addInput(dummy.getOutput(0)); - } - - @Test(expected = VerificationException.EmptyInputsOrOutputs.class) - public void emptyOutputs() throws Exception { - tx.clearOutputs(); - tx.verify(); - } - - @Test(expected = VerificationException.EmptyInputsOrOutputs.class) - public void emptyInputs() throws Exception { - tx.clearInputs(); - tx.verify(); - } - - @Test(expected = VerificationException.LargerThanMaxBlockSize.class) - public void tooHuge() throws Exception { - tx.addInput(dummy.getOutput(0)).setScriptBytes(new byte[Block.MAX_BLOCK_SIZE]); - tx.verify(); - } - - @Test(expected = VerificationException.DuplicatedOutPoint.class) - public void duplicateOutPoint() throws Exception { - TransactionInput input = tx.getInput(0); - input.setScriptBytes(new byte[1]); - tx.addInput(input.duplicateDetached()); - tx.verify(); - } - - @Test(expected = VerificationException.NegativeValueOutput.class) - public void negativeOutput() throws Exception { - tx.getOutput(0).setValue(Coin.NEGATIVE_SATOSHI); - tx.verify(); - } - - @Test(expected = VerificationException.ExcessiveValue.class) - public void exceedsMaxMoney2() throws Exception { - Coin half = NetworkParameters.MAX_MONEY.divide(2).add(Coin.SATOSHI); - tx.getOutput(0).setValue(half); - tx.addOutput(half, ADDRESS); - tx.verify(); - } - - @Test(expected = VerificationException.UnexpectedCoinbaseInput.class) - public void coinbaseInputInNonCoinbaseTX() throws Exception { - tx.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, new ScriptBuilder().data(new byte[10]).build()); - tx.verify(); - } - - @Test(expected = VerificationException.CoinbaseScriptSizeOutOfRange.class) - public void coinbaseScriptSigTooSmall() throws Exception { - tx.clearInputs(); - tx.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, new ScriptBuilder().build()); - tx.verify(); - } - - @Test(expected = VerificationException.CoinbaseScriptSizeOutOfRange.class) - public void coinbaseScriptSigTooLarge() throws Exception { - tx.clearInputs(); - TransactionInput input = tx.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, new ScriptBuilder().data(new byte[99]).build()); - assertEquals(101, input.getScriptBytes().length); - tx.verify(); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/UtilsTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/UtilsTest.java deleted file mode 100644 index 8bd7f097..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/UtilsTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2011 Thilo Planz - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import java.math.BigInteger; -import java.util.Date; - -import org.junit.Test; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -public class UtilsTest { - - @Test - public void testReverseBytes() { - assertArrayEquals(new byte[]{1, 2, 3, 4, 5}, Utils.reverseBytes(new byte[]{5, 4, 3, 2, 1})); - } - - @Test - public void testReverseDwordBytes() { - assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8}, Utils.reverseDwordBytes(new byte[]{4, 3, 2, 1, 8, 7, 6, 5}, -1)); - assertArrayEquals(new byte[]{1, 2, 3, 4}, Utils.reverseDwordBytes(new byte[]{4, 3, 2, 1, 8, 7, 6, 5}, 4)); - assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[]{4, 3, 2, 1, 8, 7, 6, 5}, 0)); - assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[0], 0)); - } - - @Test - public void testMaxOfMostFreq() throws Exception { - assertEquals(0, Utils.maxOfMostFreq()); - assertEquals(0, Utils.maxOfMostFreq(0, 0, 1)); - assertEquals(2, Utils.maxOfMostFreq(1, 1, 2, 2)); - assertEquals(1, Utils.maxOfMostFreq(1, 1, 2, 2, 1)); - assertEquals(-1, Utils.maxOfMostFreq(-1, -1, 2, 2, -1)); - } - - @Test - public void compactEncoding() throws Exception { - assertEquals(new BigInteger("1234560000", 16), Utils.decodeCompactBits(0x05123456L)); - assertEquals(new BigInteger("c0de000000", 16), Utils.decodeCompactBits(0x0600c0de)); - assertEquals(0x05123456L, Utils.encodeCompactBits(new BigInteger("1234560000", 16))); - assertEquals(0x0600c0deL, Utils.encodeCompactBits(new BigInteger("c0de000000", 16))); - } - - @Test - public void dateTimeFormat() { - assertEquals("2014-11-16T10:54:33Z", Utils.dateTimeFormat(1416135273781L)); - assertEquals("2014-11-16T10:54:33Z", Utils.dateTimeFormat(new Date(1416135273781L))); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/VarIntTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/VarIntTest.java deleted file mode 100644 index f77b3a73..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/VarIntTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * 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.dogecoin.dogecoinj.core; - -import junit.framework.TestCase; - -public class VarIntTest extends TestCase { - public void testBytes() throws Exception { - VarInt a = new VarInt(10); - assertEquals(1, a.getSizeInBytes()); - assertEquals(1, a.encode().length); - assertEquals(10, new VarInt(a.encode(), 0).value); - } - - public void testShorts() throws Exception { - VarInt a = new VarInt(64000); - assertEquals(3, a.getSizeInBytes()); - assertEquals(3, a.encode().length); - assertEquals(64000, new VarInt(a.encode(), 0).value); - } - - public void testShortFFFF() throws Exception { - VarInt a = new VarInt(0xFFFFL); - assertEquals(3, a.getSizeInBytes()); - assertEquals(3, a.encode().length); - assertEquals(0xFFFFL, new VarInt(a.encode(), 0).value); - } - - public void testInts() throws Exception { - VarInt 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); - } - - public void testIntFFFFFFFF() throws Exception { - VarInt a = new VarInt(0xFFFFFFFFL); - assertEquals(5, a.getSizeInBytes()); - assertEquals(5, a.encode().length); - byte[] bytes = a.encode(); - assertEquals(0xFFFFFFFFL, 0xFFFFFFFFL & new VarInt(bytes, 0).value); - } - - public void testLong() throws Exception { - VarInt a = new VarInt(0xCAFEBABEDEADBEEFL); - assertEquals(9, a.getSizeInBytes()); - assertEquals(9, a.encode().length); - byte[] bytes = a.encode(); - assertEquals(0xCAFEBABEDEADBEEFL, new VarInt(bytes, 0).value); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/VersionMessageTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/VersionMessageTest.java deleted file mode 100644 index 13e69342..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/VersionMessageTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 Matt Corallo - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.params.UnitTestParams; -import org.junit.Test; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.assertTrue; - -public class VersionMessageTest { - @Test - // Test that we can decode version messages which miss data which some old nodes may not include - public void testDecode() throws Exception { - NetworkParameters params = UnitTestParams.get(); - - VersionMessage ver = new VersionMessage(params, HEX.decode("71110100000000000000000048e5e95000000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d0000000000000000172f426974436f696e4a3a302e372d534e415053484f542f0004000000")); - assertTrue(!ver.relayTxesBeforeFilter); - assertTrue(ver.bestHeight == 1024); - assertTrue(ver.subVer.equals("/BitCoinJ:0.7-SNAPSHOT/")); - - ver = new VersionMessage(params, HEX.decode("71110100000000000000000048e5e95000000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d0000000000000000172f426974436f696e4a3a302e372d534e415053484f542f00040000")); - assertTrue(ver.relayTxesBeforeFilter); - assertTrue(ver.bestHeight == 1024); - assertTrue(ver.subVer.equals("/BitCoinJ:0.7-SNAPSHOT/")); - - ver = new VersionMessage(params, HEX.decode("71110100000000000000000048e5e95000000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d0000000000000000172f426974436f696e4a3a302e372d534e415053484f542f")); - assertTrue(ver.relayTxesBeforeFilter); - assertTrue(ver.bestHeight == 0); - assertTrue(ver.subVer.equals("/BitCoinJ:0.7-SNAPSHOT/")); - - ver = new VersionMessage(params, HEX.decode("71110100000000000000000048e5e95000000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d0000000000000000")); - assertTrue(ver.relayTxesBeforeFilter); - assertTrue(ver.bestHeight == 0); - assertTrue(ver.subVer.equals("")); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/WalletExtensionsTest.java b/core/src/test/java/com/dogecoin/dogecoinj/core/WalletExtensionsTest.java deleted file mode 100644 index 3d8ccaf2..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/core/WalletExtensionsTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.dogecoin.dogecoinj.core; - -import com.dogecoin.dogecoinj.testing.FooWalletExtension; -import com.dogecoin.dogecoinj.testing.TestWithWallet; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class WalletExtensionsTest extends TestWithWallet { - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - } - - @After - @Override - public void tearDown() throws Exception { - super.tearDown(); - } - - @Test(expected = java.lang.IllegalStateException.class) - public void duplicateWalletExtensionTest() { - wallet.addExtension(new FooWalletExtension("com.whatever.required", true)); - wallet.addExtension(new FooWalletExtension("com.whatever.required", true)); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP32Test.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP32Test.java deleted file mode 100644 index 962d3221..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP32Test.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright 2013 Matija Mazi. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.crypto; - -import com.dogecoin.dogecoinj.core.AddressFormatException; -import com.dogecoin.dogecoinj.core.Base58; -import com.google.common.base.Functions; -import com.google.common.base.Joiner; -import com.google.common.collect.Iterables; -import com.dogecoin.dogecoinj.core.NetworkParameters; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; -import java.util.List; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.assertEquals; - -/** - * A test with test vectors as per BIP 32 spec: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors - */ -public class BIP32Test { - private static final Logger log = LoggerFactory.getLogger(BIP32Test.class); - - HDWTestVector[] tvs = { - new HDWTestVector( - "000102030405060708090a0b0c0d0e0f", - "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - Arrays.asList( - new HDWTestVector.DerivedTestCase( - "Test1 m/0H", - new ChildNumber[]{new ChildNumber(0, true)}, - "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", - "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw" - ), - new HDWTestVector.DerivedTestCase( - "Test1 m/0H/1", - new ChildNumber[]{new ChildNumber(0, true), new ChildNumber(1, false)}, - "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", - "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ" - ), - new HDWTestVector.DerivedTestCase( - "Test1 m/0H/1/2H", - new ChildNumber[]{new ChildNumber(0, true), new ChildNumber(1, false), new ChildNumber(2, true)}, - "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", - "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5" - ), - new HDWTestVector.DerivedTestCase( - "Test1 m/0H/1/2H/2", - new ChildNumber[]{new ChildNumber(0, true), new ChildNumber(1, false), new ChildNumber(2, true), new ChildNumber(2, false)}, - "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", - "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV" - ), - new HDWTestVector.DerivedTestCase( - "Test1 m/0H/1/2H/2/1000000000", - new ChildNumber[]{new ChildNumber(0, true), new ChildNumber(1, false), new ChildNumber(2, true), new ChildNumber(2, false), new ChildNumber(1000000000, false)}, - "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", - "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy" - ) - ) - ), - new HDWTestVector( - "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", - "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", - "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - Arrays.asList( - new HDWTestVector.DerivedTestCase( - "Test2 m/0", - new ChildNumber[]{new ChildNumber(0, false)}, - "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", - "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH" - ), - new HDWTestVector.DerivedTestCase( - "Test2 m/0/2147483647H", - new ChildNumber[]{new ChildNumber(0, false), new ChildNumber(2147483647, true)}, - "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", - "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a" - ), - new HDWTestVector.DerivedTestCase( - "Test2 m/0/2147483647H/1", - new ChildNumber[]{new ChildNumber(0, false), new ChildNumber(2147483647, true), new ChildNumber(1, false)}, - "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", - "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon" - ), - new HDWTestVector.DerivedTestCase( - "Test2 m/0/2147483647H/1/2147483646H", - new ChildNumber[]{new ChildNumber(0, false), new ChildNumber(2147483647, true), new ChildNumber(1, false), new ChildNumber(2147483646, true)}, - "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", - "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL" - ), - new HDWTestVector.DerivedTestCase( - "Test2 m/0/2147483647H/1/2147483646H/2", - new ChildNumber[]{new ChildNumber(0, false), new ChildNumber(2147483647, true), new ChildNumber(1, false), new ChildNumber(2147483646, true), new ChildNumber(2, false)}, - "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", - "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt" - ) - ) - ) - }; - - @Test - public void testVector1() throws Exception { - testVector(0); - } - - @Test - public void testVector2() throws Exception { - testVector(1); - } - - private void testVector(int testCase) throws AddressFormatException { - log.info("======= Test vector {}", testCase); - HDWTestVector tv = tvs[testCase]; - NetworkParameters params = MainNetParams.get(); - DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(HEX.decode(tv.seed)); - assertEquals(testEncode(tv.priv), testEncode(masterPrivateKey.serializePrivB58(params))); - assertEquals(testEncode(tv.pub), testEncode(masterPrivateKey.serializePubB58(params))); - DeterministicHierarchy dh = new DeterministicHierarchy(masterPrivateKey); - for (int i = 0; i < tv.derived.size(); i++) { - HDWTestVector.DerivedTestCase tc = tv.derived.get(i); - log.info("{}", tc.name); - assertEquals(tc.name, String.format("Test%d %s", testCase + 1, tc.getPathDescription())); - int depth = tc.path.length - 1; - DeterministicKey ehkey = dh.deriveChild(Arrays.asList(tc.path).subList(0, depth), false, true, tc.path[depth]); - assertEquals(testEncode(tc.priv), testEncode(ehkey.serializePrivB58(params))); - assertEquals(testEncode(tc.pub), testEncode(ehkey.serializePubB58(params))); - } - } - - private String testEncode(String what) throws AddressFormatException { - return HEX.encode(Base58.decodeChecked(what)); - } - - static class HDWTestVector { - final String seed; - final String priv; - final String pub; - final List derived; - - HDWTestVector(String seed, String priv, String pub, List derived) { - this.seed = seed; - this.priv = priv; - this.pub = pub; - this.derived = derived; - } - - static class DerivedTestCase { - final String name; - final ChildNumber[] path; - final String pub; - final String priv; - - DerivedTestCase(String name, ChildNumber[] path, String priv, String pub) { - this.name = name; - this.path = path; - this.pub = pub; - this.priv = priv; - } - - String getPathDescription() { - return "m/" + Joiner.on("/").join(Iterables.transform(Arrays.asList(path), Functions.toStringFunction())); - } - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP38PrivateKeyTest.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP38PrivateKeyTest.java deleted file mode 100644 index 4c9336bc..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/BIP38PrivateKeyTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.crypto; - -import com.dogecoin.dogecoinj.core.ECKey; -import com.dogecoin.dogecoinj.crypto.BIP38PrivateKey.BadPassphraseException; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; - -public class BIP38PrivateKeyTest { - - private static final MainNetParams MAINNET = MainNetParams.get(); - private static final TestNet3Params TESTNET = TestNet3Params.get(); - - @Test - public void bip38testvector_noCompression_noEcMultiply_test1() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET, - "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg"); - ECKey key = encryptedKey.decrypt("TestingOneTwoThree"); - assertEquals("5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_noCompression_noEcMultiply_test2() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET, - "6PRNFFkZc2NZ6dJqFfhRoFNMR9Lnyj7dYGrzdgXXVMXcxoKTePPX1dWByq"); - ECKey key = encryptedKey.decrypt("Satoshi"); - assertEquals("5HtasZ6ofTHP6HCwTqTkLDuLQisYPah7aUnSKfC7h4hMUVw2gi5", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_noCompression_noEcMultiply_test3() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET, - "6PRW5o9FLp4gJDDVqJQKJFTpMvdsSGJxMYHtHaQBF3ooa8mwD69bapcDQn"); - StringBuilder passphrase = new StringBuilder(); - passphrase.appendCodePoint(0x03d2); // GREEK UPSILON WITH HOOK - passphrase.appendCodePoint(0x0301); // COMBINING ACUTE ACCENT - passphrase.appendCodePoint(0x0000); // NULL - passphrase.appendCodePoint(0x010400); // DESERET CAPITAL LETTER LONG I - passphrase.appendCodePoint(0x01f4a9); // PILE OF POO - ECKey key = encryptedKey.decrypt(passphrase.toString()); - assertEquals("5Jajm8eQ22H3pGWLEVCXyvND8dQZhiQhoLJNKjYXk9roUFTMSZ4", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_compression_noEcMultiply_test1() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(), - "6PYNKZ1EAgYgmQfmNVamxyXVWHzK5s6DGhwP4J5o44cvXdoY7sRzhtpUeo"); - ECKey key = encryptedKey.decrypt("TestingOneTwoThree"); - assertEquals("L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_compression_noEcMultiply_test2() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(), - "6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7"); - ECKey key = encryptedKey.decrypt("Satoshi"); - assertEquals("KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_ecMultiply_noCompression_noLotAndSequence_test1() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(), - "6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX"); - ECKey key = encryptedKey.decrypt("TestingOneTwoThree"); - assertEquals("5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_ecMultiply_noCompression_noLotAndSequence_test2() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(), - "6PfLGnQs6VZnrNpmVKfjotbnQuaJK4KZoPFrAjx1JMJUa1Ft8gnf5WxfKd"); - ECKey key = encryptedKey.decrypt("Satoshi"); - assertEquals("5KJ51SgxWaAYR13zd9ReMhJpwrcX47xTJh2D3fGPG9CM8vkv5sH", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_ecMultiply_noCompression_lotAndSequence_test1() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(), - "6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j"); - ECKey key = encryptedKey.decrypt("MOLON LABE"); - assertEquals("5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bip38testvector_ecMultiply_noCompression_lotAndSequence_test2() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(), - "6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH"); - ECKey key = encryptedKey.decrypt("ΜΟΛΩΝ ΛΑΒΕ"); - assertEquals("5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D", key.getPrivateKeyEncoded(MAINNET) - .toString()); - } - - @Test - public void bitcoinpaperwallet_testnet() throws Exception { - // values taken from bitcoinpaperwallet.com - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(TESTNET, - "6PRPhQhmtw6dQu6jD8E1KS4VphwJxBS9Eh9C8FQELcrwN3vPvskv9NKvuL"); - ECKey key = encryptedKey.decrypt("password"); - assertEquals("93MLfjbY6ugAsLeQfFY6zodDa8izgm1XAwA9cpMbUTwLkDitopg", key.getPrivateKeyEncoded(TESTNET) - .toString()); - } - - @Test - public void bitaddress_testnet() throws Exception { - // values taken from bitaddress.org - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(TESTNET, - "6PfMmVHn153N3x83Yiy4Nf76dHUkXufe2Adr9Fw5bewrunGNeaw2QCpifb"); - ECKey key = encryptedKey.decrypt("password"); - assertEquals("91tCpdaGr4Khv7UAuUxa6aMqeN5GcPVJxzLtNsnZHTCndxkRcz2", key.getPrivateKeyEncoded(TESTNET) - .toString()); - } - - @Test(expected = BadPassphraseException.class) - public void badPassphrase() throws Exception { - BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET, - "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg"); - encryptedKey.decrypt("BAD"); - } - - @Test - public void testJavaSerialization() throws Exception { - BIP38PrivateKey key = new BIP38PrivateKey(TESTNET, "6PfMmVHn153N3x83Yiy4Nf76dHUkXufe2Adr9Fw5bewrunGNeaw2QCpifb"); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - new ObjectOutputStream(os).writeObject(key); - BIP38PrivateKey keyCopy = (BIP38PrivateKey) new ObjectInputStream(new ByteArrayInputStream(os.toByteArray())) - .readObject(); - assertEquals(key, keyCopy); - } - - @Test - public void cloning() throws Exception { - BIP38PrivateKey a = new BIP38PrivateKey(TESTNET, "6PfMmVHn153N3x83Yiy4Nf76dHUkXufe2Adr9Fw5bewrunGNeaw2QCpifb"); - // TODO: Consider overriding clone() in BIP38PrivateKey to narrow the type - BIP38PrivateKey b = (BIP38PrivateKey) a.clone(); - - assertEquals(a, b); - assertNotSame(a, b); - } - -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/ChildKeyDerivationTest.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/ChildKeyDerivationTest.java deleted file mode 100644 index 1ab249c0..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/ChildKeyDerivationTest.java +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright 2013 Matija Mazi. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.crypto; - -import com.dogecoin.dogecoinj.core.ECKey; -import com.dogecoin.dogecoinj.core.NetworkParameters; -import com.dogecoin.dogecoinj.core.Sha256Hash; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import org.junit.Test; -import org.spongycastle.crypto.params.KeyParameter; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.*; - -/** - * This test is adapted from Armory's BIP 32 tests. - */ -public class ChildKeyDerivationTest { - private static final int HDW_CHAIN_EXTERNAL = 0; - private static final int HDW_CHAIN_INTERNAL = 1; - - @Test - public void testChildKeyDerivation() throws Exception { - String ckdTestVectors[] = { - // test case 1: - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "04" + "6a04ab98d9e4774ad806e302dddeb63b" + - "ea16b5cb5f223ee77478e861bb583eb3" + - "36b6fbcb60b5b3d4f1551ac45e5ffc49" + - "36466e7d98f6c7c0ec736539f74691a6", - "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", - - // test case 2: - "be05d9ded0a73f81b814c93792f753b35c575fe446760005d44e0be13ba8935a", - "02" + "b530da16bbff1428c33020e87fc9e699" + - "cc9c753a63b8678ce647b7457397acef", - "7012bc411228495f25d666d55fdce3f10a93908b5f9b9b7baa6e7573603a7bda" - }; - - for(int i = 0; i < 1; i++) { - byte[] priv = HEX.decode(ckdTestVectors[3 * i]); - byte[] pub = HEX.decode(ckdTestVectors[3 * i + 1]); - byte[] chain = HEX.decode(ckdTestVectors[3 * i + 2]); // chain code - - ////////////////////////////////////////////////////////////////////////// - // Start with an extended PRIVATE key - DeterministicKey ekprv = HDKeyDerivation.createMasterPrivKeyFromBytes(priv, chain); - - // Create two accounts - DeterministicKey ekprv_0 = HDKeyDerivation.deriveChildKey(ekprv, 0); - DeterministicKey ekprv_1 = HDKeyDerivation.deriveChildKey(ekprv, 1); - - // Create internal and external chain on Account 0 - DeterministicKey ekprv_0_EX = HDKeyDerivation.deriveChildKey(ekprv_0, HDW_CHAIN_EXTERNAL); - DeterministicKey ekprv_0_IN = HDKeyDerivation.deriveChildKey(ekprv_0, HDW_CHAIN_INTERNAL); - - // Create three addresses on external chain - DeterministicKey ekprv_0_EX_0 = HDKeyDerivation.deriveChildKey(ekprv_0_EX, 0); - DeterministicKey ekprv_0_EX_1 = HDKeyDerivation.deriveChildKey(ekprv_0_EX, 1); - DeterministicKey ekprv_0_EX_2 = HDKeyDerivation.deriveChildKey(ekprv_0_EX, 2); - - // Create three addresses on internal chain - DeterministicKey ekprv_0_IN_0 = HDKeyDerivation.deriveChildKey(ekprv_0_IN, 0); - DeterministicKey ekprv_0_IN_1 = HDKeyDerivation.deriveChildKey(ekprv_0_IN, 1); - DeterministicKey ekprv_0_IN_2 = HDKeyDerivation.deriveChildKey(ekprv_0_IN, 2); - - // Now add a few more addresses with very large indices - DeterministicKey ekprv_1_IN = HDKeyDerivation.deriveChildKey(ekprv_1, HDW_CHAIN_INTERNAL); - DeterministicKey ekprv_1_IN_4095 = HDKeyDerivation.deriveChildKey(ekprv_1_IN, 4095); -// ExtendedHierarchicKey ekprv_1_IN_4bil = HDKeyDerivation.deriveChildKey(ekprv_1_IN, 4294967295L); - - ////////////////////////////////////////////////////////////////////////// - // Repeat the above with PUBLIC key - DeterministicKey ekpub = HDKeyDerivation.createMasterPubKeyFromBytes(HDUtils.toCompressed(pub), chain); - - // Create two accounts - DeterministicKey ekpub_0 = HDKeyDerivation.deriveChildKey(ekpub, 0); - DeterministicKey ekpub_1 = HDKeyDerivation.deriveChildKey(ekpub, 1); - - // Create internal and external chain on Account 0 - DeterministicKey ekpub_0_EX = HDKeyDerivation.deriveChildKey(ekpub_0, HDW_CHAIN_EXTERNAL); - DeterministicKey ekpub_0_IN = HDKeyDerivation.deriveChildKey(ekpub_0, HDW_CHAIN_INTERNAL); - - // Create three addresses on external chain - DeterministicKey ekpub_0_EX_0 = HDKeyDerivation.deriveChildKey(ekpub_0_EX, 0); - DeterministicKey ekpub_0_EX_1 = HDKeyDerivation.deriveChildKey(ekpub_0_EX, 1); - DeterministicKey ekpub_0_EX_2 = HDKeyDerivation.deriveChildKey(ekpub_0_EX, 2); - - // Create three addresses on internal chain - DeterministicKey ekpub_0_IN_0 = HDKeyDerivation.deriveChildKey(ekpub_0_IN, 0); - DeterministicKey ekpub_0_IN_1 = HDKeyDerivation.deriveChildKey(ekpub_0_IN, 1); - DeterministicKey ekpub_0_IN_2 = HDKeyDerivation.deriveChildKey(ekpub_0_IN, 2); - - // Now add a few more addresses with very large indices - DeterministicKey ekpub_1_IN = HDKeyDerivation.deriveChildKey(ekpub_1, HDW_CHAIN_INTERNAL); - DeterministicKey ekpub_1_IN_4095 = HDKeyDerivation.deriveChildKey(ekpub_1_IN, 4095); -// ExtendedHierarchicKey ekpub_1_IN_4bil = HDKeyDerivation.deriveChildKey(ekpub_1_IN, 4294967295L); - - assertEquals(hexEncodePub(ekprv.getPubOnly()), hexEncodePub(ekpub)); - assertEquals(hexEncodePub(ekprv_0.getPubOnly()), hexEncodePub(ekpub_0)); - assertEquals(hexEncodePub(ekprv_1.getPubOnly()), hexEncodePub(ekpub_1)); - assertEquals(hexEncodePub(ekprv_0_IN.getPubOnly()), hexEncodePub(ekpub_0_IN)); - assertEquals(hexEncodePub(ekprv_0_IN_0.getPubOnly()), hexEncodePub(ekpub_0_IN_0)); - assertEquals(hexEncodePub(ekprv_0_IN_1.getPubOnly()), hexEncodePub(ekpub_0_IN_1)); - assertEquals(hexEncodePub(ekprv_0_IN_2.getPubOnly()), hexEncodePub(ekpub_0_IN_2)); - assertEquals(hexEncodePub(ekprv_0_EX_0.getPubOnly()), hexEncodePub(ekpub_0_EX_0)); - assertEquals(hexEncodePub(ekprv_0_EX_1.getPubOnly()), hexEncodePub(ekpub_0_EX_1)); - assertEquals(hexEncodePub(ekprv_0_EX_2.getPubOnly()), hexEncodePub(ekpub_0_EX_2)); - assertEquals(hexEncodePub(ekprv_1_IN.getPubOnly()), hexEncodePub(ekpub_1_IN)); - assertEquals(hexEncodePub(ekprv_1_IN_4095.getPubOnly()), hexEncodePub(ekpub_1_IN_4095)); - //assertEquals(hexEncodePub(ekprv_1_IN_4bil.getPubOnly()), hexEncodePub(ekpub_1_IN_4bil)); - } - } - - @Test - public void inverseEqualsNormal() throws Exception { - DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("Wired / Aug 13th 2014 / Snowden: I Left the NSA Clues, But They Couldn't Find Them".getBytes()); - HDKeyDerivation.RawKeyBytes key2 = HDKeyDerivation.deriveChildKeyBytesFromPublic(key1.getPubOnly(), ChildNumber.ZERO, HDKeyDerivation.PublicDeriveMode.NORMAL); - HDKeyDerivation.RawKeyBytes key3 = HDKeyDerivation.deriveChildKeyBytesFromPublic(key1.getPubOnly(), ChildNumber.ZERO, HDKeyDerivation.PublicDeriveMode.WITH_INVERSION); - assertArrayEquals(key2.keyBytes, key3.keyBytes); - assertArrayEquals(key2.chainCode, key3.chainCode); - } - - @Test - public void encryptedDerivation() throws Exception { - // Check that encrypting a parent key in the heirarchy and then deriving from it yields a DeterministicKey - // with no private key component, and that the private key bytes are derived on demand. - KeyCrypter scrypter = new KeyCrypterScrypt(); - KeyParameter aesKey = scrypter.deriveKey("we never went to the moon"); - - DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("it was all a hoax".getBytes()); - DeterministicKey encryptedKey1 = key1.encrypt(scrypter, aesKey, null); - DeterministicKey decryptedKey1 = encryptedKey1.decrypt(aesKey); - assertEquals(key1, decryptedKey1); - - DeterministicKey key2 = HDKeyDerivation.deriveChildKey(key1, ChildNumber.ZERO); - DeterministicKey derivedKey2 = HDKeyDerivation.deriveChildKey(encryptedKey1, ChildNumber.ZERO); - assertTrue(derivedKey2.isEncrypted()); // parent is encrypted. - DeterministicKey decryptedKey2 = derivedKey2.decrypt(aesKey); - assertFalse(decryptedKey2.isEncrypted()); - assertEquals(key2, decryptedKey2); - - Sha256Hash hash = Sha256Hash.create("the mainstream media won't cover it. why is that?".getBytes()); - try { - derivedKey2.sign(hash); - fail(); - } catch (ECKey.KeyIsEncryptedException e) { - // Ignored. - } - ECKey.ECDSASignature signature = derivedKey2.sign(hash, aesKey); - assertTrue(derivedKey2.verify(hash, signature)); - } - - @Test - public void pubOnlyDerivation() throws Exception { - DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("satoshi lives!".getBytes()); - DeterministicKey key2 = HDKeyDerivation.deriveChildKey(key1, ChildNumber.ZERO_HARDENED); - DeterministicKey key3 = HDKeyDerivation.deriveChildKey(key2, ChildNumber.ZERO); - DeterministicKey pubkey3 = HDKeyDerivation.deriveChildKey(key2.getPubOnly(), ChildNumber.ZERO); - assertEquals(key3.getPubKeyPoint(), pubkey3.getPubKeyPoint()); - } - - @Test - public void testSerializationMainAndTestNetworks() { - DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("satoshi lives!".getBytes()); - NetworkParameters params = MainNetParams.get(); - String pub58 = key1.serializePubB58(params); - String priv58 = key1.serializePrivB58(params); - assertEquals("xpub661MyMwAqRbcF7mq7Aejj5xZNzFfgi3ABamE9FedDHVmViSzSxYTgAQGcATDo2J821q7Y9EAagjg5EP3L7uBZk11PxZU3hikL59dexfLkz3", pub58); - assertEquals("xprv9s21ZrQH143K2dhN197jMx1ppxRBHFKJpMqdLsF1ewxncv7quRED8N5nksxphju3W7naj1arF56L5PUEWfuSk8h73Sb2uh7bSwyXNrjzhAZ", priv58); - params = TestNet3Params.get(); - pub58 = key1.serializePubB58(params); - priv58 = key1.serializePrivB58(params); - assertEquals("tpubD6NzVbkrYhZ4WuxgZMdpw1Hvi7MKg6YDjDMXVohmZCFfF17hXBPYpc56rCY1KXFMovN29ik37nZimQseiykRTBTJTZJmjENyv2k3R12BJ1M", pub58); - assertEquals("tprv8ZgxMBicQKsPdSvtfhyEXbdp95qPWmMK9ukkDHfU8vTGQWrvtnZxe7TEg48Ui7HMsZKMj7CcQRg8YF1ydtFPZBxha5oLa3qeN3iwpYhHPVZ", priv58); - } - - @Test - public void serializeToTextAndBytes() { - DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("satoshi lives!".getBytes()); - DeterministicKey key2 = HDKeyDerivation.deriveChildKey(key1, ChildNumber.ZERO_HARDENED); - - // Creation time can't survive the xpub serialization format unfortunately. - key1.setCreationTimeSeconds(0); - NetworkParameters params = MainNetParams.get(); - - { - final String pub58 = key1.serializePubB58(params); - final String priv58 = key1.serializePrivB58(params); - final byte[] pub = key1.serializePublic(params); - final byte[] priv = key1.serializePrivate(params); - assertEquals("xpub661MyMwAqRbcF7mq7Aejj5xZNzFfgi3ABamE9FedDHVmViSzSxYTgAQGcATDo2J821q7Y9EAagjg5EP3L7uBZk11PxZU3hikL59dexfLkz3", pub58); - assertEquals("xprv9s21ZrQH143K2dhN197jMx1ppxRBHFKJpMqdLsF1ewxncv7quRED8N5nksxphju3W7naj1arF56L5PUEWfuSk8h73Sb2uh7bSwyXNrjzhAZ", priv58); - assertArrayEquals(new byte[]{4, -120, -78, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, -68, 93, -104, -97, 31, -105, -18, 109, 112, 104, 45, -77, -77, 18, 85, -29, -120, 86, -113, 26, 48, -18, -79, -110, -6, -27, 87, 86, 24, 124, 99, 3, 96, -33, -14, 67, -19, -47, 16, 76, -49, -11, -30, -123, 7, 56, 101, 91, 74, 125, -127, 61, 42, -103, 90, -93, 66, -36, 2, -126, -107, 30, 24, -111}, pub); - assertArrayEquals(new byte[]{4, -120, -83, -28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, -68, 93, -104, -97, 31, -105, -18, 109, 112, 104, 45, -77, -77, 18, 85, -29, -120, 86, -113, 26, 48, -18, -79, -110, -6, -27, 87, 86, 24, 124, 99, 0, -96, -75, 47, 90, -49, 92, -74, 92, -128, -125, 23, 38, -10, 97, -66, -19, 50, -112, 30, -111, -57, -124, 118, -86, 126, -35, -4, -51, 19, 109, 67, 116}, priv); - assertEquals(DeterministicKey.deserializeB58(null, priv58, params), key1); - assertEquals(DeterministicKey.deserializeB58(priv58, params), key1); - assertEquals(DeterministicKey.deserializeB58(null, pub58, params).getPubKeyPoint(), key1.getPubKeyPoint()); - assertEquals(DeterministicKey.deserializeB58(pub58, params).getPubKeyPoint(), key1.getPubKeyPoint()); - assertEquals(DeterministicKey.deserialize(params, priv, null), key1); - assertEquals(DeterministicKey.deserialize(params, priv), key1); - assertEquals(DeterministicKey.deserialize(params, pub, null).getPubKeyPoint(), key1.getPubKeyPoint()); - assertEquals(DeterministicKey.deserialize(params, pub).getPubKeyPoint(), key1.getPubKeyPoint()); - } - { - final String pub58 = key2.serializePubB58(params); - final String priv58 = key2.serializePrivB58(params); - final byte[] pub = key2.serializePublic(params); - final byte[] priv = key2.serializePrivate(params); - assertEquals(DeterministicKey.deserializeB58(key1, priv58, params), key2); - assertEquals(DeterministicKey.deserializeB58(key1, pub58, params).getPubKeyPoint(), key2.getPubKeyPoint()); - assertEquals(DeterministicKey.deserialize(params, priv, key1), key2); - assertEquals(DeterministicKey.deserialize(params, pub, key1).getPubKeyPoint(), key2.getPubKeyPoint()); - } - } - - @Test - public void parentlessDeserialization() { - NetworkParameters params = UnitTestParams.get(); - DeterministicKey key1 = HDKeyDerivation.createMasterPrivateKey("satoshi lives!".getBytes()); - DeterministicKey key2 = HDKeyDerivation.deriveChildKey(key1, ChildNumber.ZERO_HARDENED); - DeterministicKey key3 = HDKeyDerivation.deriveChildKey(key2, ChildNumber.ZERO_HARDENED); - DeterministicKey key4 = HDKeyDerivation.deriveChildKey(key3, ChildNumber.ZERO_HARDENED); - assertEquals(key4.getPath().size(), 3); - assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params), key3).getPath().size(), 3); - assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params), null).getPath().size(), 1); - assertEquals(DeterministicKey.deserialize(params, key4.serializePrivate(params)).getPath().size(), 1); - } - - private static String hexEncodePub(DeterministicKey pubKey) { - return HEX.encode(pubKey.getPubKey()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/HDUtilsTest.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/HDUtilsTest.java deleted file mode 100644 index ee861444..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/HDUtilsTest.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2013 Matija Mazi - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.crypto; - -import com.google.common.collect.ImmutableList; -import org.junit.Assert; -import org.junit.Test; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import java.util.List; - -public class HDUtilsTest { - @Test - public void testHmac() throws Exception { - String tv[] = { - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" + - "0b0b0b0b", - "4869205468657265", - "87aa7cdea5ef619d4ff0b4241a1d6cb0" + - "2379f4e2ce4ec2787ad0b30545e17cde" + - "daa833b7d6b8a702038b274eaea3f4e4" + - "be9d914eeb61f1702e696c203a126854", - - "4a656665", - "7768617420646f2079612077616e7420" + - "666f72206e6f7468696e673f", - "164b7a7bfcf819e2e395fbe73b56e0a3" + - "87bd64222e831fd610270cd7ea250554" + - "9758bf75c05a994a6d034f65f8f0e6fd" + - "caeab1a34d4a6b4b636e070a38bce737", - - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaa", - "dddddddddddddddddddddddddddddddd" + - "dddddddddddddddddddddddddddddddd" + - "dddddddddddddddddddddddddddddddd" + - "dddd", - "fa73b0089d56a284efb0f0756c890be9" + - "b1b5dbdd8ee81a3655f83e33b2279d39" + - "bf3e848279a722c806b485a47e67c807" + - "b946a337bee8942674278859e13292fb", - - "0102030405060708090a0b0c0d0e0f10" + - "111213141516171819", - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + - "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + - "cdcd", - "b0ba465637458c6990e5a8c5f61d4af7" + - "e576d97ff94b872de76f8050361ee3db" + - "a91ca5c11aa25eb4d679275cc5788063" + - "a5f19741120c4f2de2adebeb10a298dd", - - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaa", - "54657374205573696e67204c61726765" + - "72205468616e20426c6f636b2d53697a" + - "65204b6579202d2048617368204b6579" + - "204669727374", - "80b24263c7c1a3ebb71493c1dd7be8b4" + - "9b46d1f41b4aeec1121b013783f8f352" + - "6b56d037e05f2598bd0fd2215d6a1e52" + - "95e64f73f63f0aec8b915a985d786598", - - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaa", - "54686973206973206120746573742075" + - "73696e672061206c6172676572207468" + - "616e20626c6f636b2d73697a65206b65" + - "7920616e642061206c61726765722074" + - "68616e20626c6f636b2d73697a652064" + - "6174612e20546865206b6579206e6565" + - "647320746f2062652068617368656420" + - "6265666f7265206265696e6720757365" + - "642062792074686520484d414320616c" + - "676f726974686d2e", - "e37b6a775dc87dbaa4dfa9f96e5e3ffd" + - "debd71f8867289865df5a32d20cdc944" + - "b6022cac3c4982b10d5eeb55c3e4de15" + - "134676fb6de0446065c97440fa8c6a58" - }; - - for (int i = 0; i < tv.length; i += 3) { - Assert.assertArrayEquals("Case " + i, getBytes(tv, i + 2), HDUtils.hmacSha512(getBytes(tv, i), getBytes(tv, i + 1))); - } - } - - private static byte[] getBytes(String[] hmacTestVectors, int i) { - return HEX.decode(hmacTestVectors[i]); - } - - @Test - public void testLongToByteArray() throws Exception { - byte[] bytes = HDUtils.longTo4ByteArray(1026); - Assert.assertEquals("00000402", HEX.encode(bytes)); - } - - - @Test - public void testFormatPath() { - Object tv[] = { - "M/44H/0H/0H/1/1", - ImmutableList.of(new ChildNumber(44, true), new ChildNumber(0, true), new ChildNumber(0, true), - new ChildNumber(1, false), new ChildNumber(1, false)), - - "M/7H/3/3/1H", - ImmutableList.of(new ChildNumber(7, true), new ChildNumber(3, false), new ChildNumber(3, false), - new ChildNumber(1, true)), - - "M/1H/2H/3H", - ImmutableList.of(new ChildNumber(1, true), new ChildNumber(2, true), new ChildNumber(3, true)), - - "M/1/2/3", - ImmutableList.of(new ChildNumber(1, false), new ChildNumber(2, false), new ChildNumber(3, false)) - }; - - for (int i = 0; i < tv.length; i += 2) { - String expectedStrPath = (String) tv[i]; - List path = (List) tv[i+1]; - - String generatedStrPath = HDUtils.formatPath(path); - - Assert.assertEquals(generatedStrPath, expectedStrPath); - } - - } - - @Test - public void testParsePath() { - Object tv[] = { - "M / 44H / 0H / 0H / 1 / 1", - ImmutableList.of(new ChildNumber(44, true), new ChildNumber(0, true), new ChildNumber(0, true), - new ChildNumber(1, false), new ChildNumber(1, false)), - - "M/7H/3/3/1H/", - ImmutableList.of(new ChildNumber(7, true), new ChildNumber(3, false), new ChildNumber(3, false), - new ChildNumber(1, true)), - - "1 H / 2 H / 3 H /", - ImmutableList.of(new ChildNumber(1, true), new ChildNumber(2, true), new ChildNumber(3, true)), - - "1 / 2 / 3 /", - ImmutableList.of(new ChildNumber(1, false), new ChildNumber(2, false), new ChildNumber(3, false)) - }; - - for (int i = 0; i < tv.length; i += 2) { - String strPath = (String) tv[i]; - List expectedPath = (List) tv[i+1]; - - List path = HDUtils.parsePath(strPath); - - Assert.assertEquals(path, expectedPath); - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/KeyCrypterScryptTest.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/KeyCrypterScryptTest.java deleted file mode 100644 index 6efc3562..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/KeyCrypterScryptTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2013 Jim Burton. - * Copyright 2014 Andreas Schildbach - * - * Licensed under the MIT license (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/mit-license.php - * - * 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.dogecoin.dogecoinj.crypto; - -import com.dogecoin.dogecoinj.core.Utils; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.google.protobuf.ByteString; - -import com.dogecoin.dogecoinj.wallet.Protos; -import com.dogecoin.dogecoinj.wallet.Protos.ScryptParameters; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.UnsupportedEncodingException; -import java.security.SecureRandom; -import java.util.Random; -import java.util.UUID; - -import static org.junit.Assert.*; - -public class KeyCrypterScryptTest { - - private static final Logger log = LoggerFactory.getLogger(KeyCrypterScryptTest.class); - - // Nonsense bytes for encryption test. - private static final byte[] TEST_BYTES1 = {0, -101, 2, 103, -4, 105, 6, 107, 8, -109, 10, 111, -12, 113, 14, -115, 16, 117, -18, 119, 20, 121, 22, 123, -24, 125, 26, 127, -28, 29, -30, 31}; - - private static final CharSequence PASSWORD1 = "aTestPassword"; - private static final CharSequence PASSWORD2 = "0123456789"; - - private static final CharSequence WRONG_PASSWORD = "thisIsTheWrongPassword"; - - private ScryptParameters scryptParameters; - - @Before - public void setUp() throws Exception { - byte[] salt = new byte[KeyCrypterScrypt.SALT_LENGTH]; - new SecureRandom().nextBytes(salt); - Protos.ScryptParameters.Builder scryptParametersBuilder = Protos.ScryptParameters.newBuilder().setSalt(ByteString.copyFrom(salt)); - scryptParameters = scryptParametersBuilder.build(); - - BriefLogFormatter.init(); - } - - @Test - public void testKeyCrypterGood1() throws KeyCrypterException { - KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); - - // Encrypt. - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(TEST_BYTES1, keyCrypter.deriveKey(PASSWORD1)); - assertNotNull(encryptedPrivateKey); - - // Decrypt. - byte[] reborn = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); - log.debug("Original: " + Utils.HEX.encode(TEST_BYTES1)); - log.debug("Reborn : " + Utils.HEX.encode(reborn)); - assertEquals(Utils.HEX.encode(TEST_BYTES1), Utils.HEX.encode(reborn)); - } - - /** - * Test with random plain text strings and random passwords. - * UUIDs are used and hence will only cover hex characters (and the separator hyphen). - * @throws KeyCrypterException - * @throws UnsupportedEncodingException - */ - @Test - public void testKeyCrypterGood2() throws Exception { - KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); - - System.out.print("EncrypterDecrypterTest: Trying random UUIDs for plainText and passwords :"); - int numberOfTests = 16; - for (int i = 0; i < numberOfTests; i++) { - // Create a UUID as the plaintext and use another for the password. - String plainText = UUID.randomUUID().toString(); - CharSequence password = UUID.randomUUID().toString(); - - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(plainText.getBytes(), keyCrypter.deriveKey(password)); - - assertNotNull(encryptedPrivateKey); - - byte[] reconstructedPlainBytes = keyCrypter.decrypt(encryptedPrivateKey,keyCrypter.deriveKey(password)); - assertEquals(Utils.HEX.encode(plainText.getBytes()), Utils.HEX.encode(reconstructedPlainBytes)); - System.out.print('.'); - } - System.out.println(" Done."); - } - - @Test - public void testKeyCrypterWrongPassword() throws KeyCrypterException { - KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); - - // create a longer encryption string - StringBuilder stringBuffer = new StringBuilder(); - for (int i = 0; i < 100; i++) { - stringBuffer.append(i).append(" ").append("The quick brown fox"); - } - - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(stringBuffer.toString().getBytes(), keyCrypter.deriveKey(PASSWORD2)); - assertNotNull(encryptedPrivateKey); - - try { - keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(WRONG_PASSWORD)); - // TODO: This test sometimes fails due to relying on padding. - fail("Decrypt with wrong password did not throw exception"); - } catch (KeyCrypterException ede) { - assertTrue(ede.getMessage().contains("Could not decrypt")); - } - } - - @Test - public void testEncryptDecryptBytes1() throws KeyCrypterException { - KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); - - // Encrypt bytes. - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(TEST_BYTES1, keyCrypter.deriveKey(PASSWORD1)); - assertNotNull(encryptedPrivateKey); - log.debug("\nEncrypterDecrypterTest: cipherBytes = \nlength = " + encryptedPrivateKey.encryptedBytes.length + "\n---------------\n" + Utils.HEX.encode(encryptedPrivateKey.encryptedBytes) + "\n---------------\n"); - - byte[] rebornPlainBytes = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); - - log.debug("Original: " + Utils.HEX.encode(TEST_BYTES1)); - log.debug("Reborn1 : " + Utils.HEX.encode(rebornPlainBytes)); - assertEquals(Utils.HEX.encode(TEST_BYTES1), Utils.HEX.encode(rebornPlainBytes)); - } - - @Test - public void testEncryptDecryptBytes2() throws KeyCrypterException { - KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); - - // Encrypt random bytes of various lengths up to length 50. - Random random = new Random(); - - for (int i = 0; i < 50; i++) { - byte[] plainBytes = new byte[i]; - random.nextBytes(plainBytes); - - EncryptedData encryptedPrivateKey = keyCrypter.encrypt(plainBytes, keyCrypter.deriveKey(PASSWORD1)); - assertNotNull(encryptedPrivateKey); - //log.debug("\nEncrypterDecrypterTest: cipherBytes = \nlength = " + cipherBytes.length + "\n---------------\n" + Utils.HEX.encode(cipherBytes) + "\n---------------\n"); - - byte[] rebornPlainBytes = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); - - log.debug("Original: (" + i + ") " + Utils.HEX.encode(plainBytes)); - log.debug("Reborn1 : (" + i + ") " + Utils.HEX.encode(rebornPlainBytes)); - assertEquals(Utils.HEX.encode(plainBytes), Utils.HEX.encode(rebornPlainBytes)); - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/MnemonicCodeTest.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/MnemonicCodeTest.java deleted file mode 100644 index 7b8a9b17..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/MnemonicCodeTest.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2013 Ken Sedgwick - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.crypto; - -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static org.junit.Assert.assertEquals; - -public class MnemonicCodeTest { - // These vectors are from https://github.com/trezor/python-mnemonic/blob/master/vectors.json - String vectors[] = { - "00000000000000000000000000000000", - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", - - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "legal winner thank year wave sausage worth useful legal winner thank yellow", - "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607", - - - "80808080808080808080808080808080", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", - "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8", - - - "ffffffffffffffffffffffffffffffff", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", - "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069", - - - "000000000000000000000000000000000000000000000000", - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", - "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", - - - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", - "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd", - - - "808080808080808080808080808080808080808080808080", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", - "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65", - - - "ffffffffffffffffffffffffffffffffffffffffffffffff", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", - "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528", - - - "0000000000000000000000000000000000000000000000000000000000000000", - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", - "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8", - - - "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", - "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", - "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87", - - - "8080808080808080808080808080808080808080808080808080808080808080", - "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", - "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f", - - - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", - "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad", - - - "77c2b00716cec7213839159e404db50d", - "jelly better achieve collect unaware mountain thought cargo oxygen act hood bridge", - "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f672a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff", - - - "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b", - "renew stay biology evidence goat welcome casual join adapt armor shuffle fault little machine walk stumble urge swap", - "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5", - - - "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982", - "dignity pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic", - "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da20af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67", - - - "0460ef47585604c5660618db2e6a7e7f", - "afford alter spike radar gate glance object seek swamp infant panel yellow", - "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4", - - - "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f", - "indicate race push merry suffer human cruise dwarf pole review arch keep canvas theme poem divorce alter left", - "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349dbe2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba", - - - "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416", - "clutch control vehicle tonight unusual clog visa ice plunge glimpse recipe series open hour vintage deposit universe tip job dress radar refuse motion taste", - "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449", - - - "eaebabb2383351fd31d703840b32e9e2", - "turtle front uncle idea crush write shrug there lottery flower risk shell", - "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c404f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c", - - - "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78", - "kiss carry display unusual confirm curtain upgrade antique rotate hello void custom frequent obey nut hole price segment", - "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79", - - - "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef", - "exile ask congress lamp submit jacket era scheme attend cousin alcohol catch course end lucky hurt sentence oven short ball bird grab wing top", - "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c", - - - "18ab19a9f54a9274f03e5209a2ac8a91", - "board flee heavy tunnel powder denial science ski answer betray cargo cat", - "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe005831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8", - - - "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4", - "board blade invite damage undo sun mimic interest slam gaze truly inherit resist great inject rocket museum chief", - "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf07c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9", - - - "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419", - "beyond stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut", - "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd" - }; - - private MnemonicCode mc; - - @Before - public void setup() throws IOException { - mc = new MnemonicCode(); - } - - @Test - public void testVectors() throws Exception { - for (int ii = 0; ii < vectors.length; ii += 3) { - String vecData = vectors[ii]; - String vecCode = vectors[ii+1]; - String vecSeed = vectors[ii+2]; - - List code = mc.toMnemonic(HEX.decode(vecData)); - byte[] seed = MnemonicCode.toSeed(code, "TREZOR"); - byte[] entropy = mc.toEntropy(split(vecCode)); - - assertEquals(vecData, HEX.encode(entropy)); - assertEquals(vecCode, Joiner.on(' ').join(code)); - assertEquals(vecSeed, HEX.encode(seed)); - } - } - - @Test(expected = MnemonicException.MnemonicLengthException.class) - public void testBadEntropyLength() throws Exception { - byte[] entropy = HEX.decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f"); - mc.toMnemonic(entropy); - } - - @Test(expected = MnemonicException.MnemonicLengthException.class) - public void testBadLength() throws Exception { - List words = split("risk tiger venture dinner age assume float denial penalty hello"); - mc.check(words); - } - - @Test(expected = MnemonicException.MnemonicWordException.class) - public void testBadWord() throws Exception { - List words = split("risk tiger venture dinner xyzzy assume float denial penalty hello game wing"); - mc.check(words); - } - - @Test(expected = MnemonicException.MnemonicChecksumException.class) - public void testBadChecksum() throws Exception { - List words = split("bless cloud wheel regular tiny venue bird web grief security dignity zoo"); - mc.check(words); - } - - @Test(expected = MnemonicException.MnemonicLengthException.class) - public void testEmptyMnemonic() throws Exception { - List words = Lists.newArrayList(); - mc.check(words); - } - - @Test(expected = MnemonicException.MnemonicLengthException.class) - public void testEmptyEntropy() throws Exception { - byte[] entropy = new byte[]{}; - mc.toMnemonic(entropy); - } - - static public List split(String words) { - return new ArrayList(Arrays.asList(words.split("\\s+"))); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/crypto/X509UtilsTest.java b/core/src/test/java/com/dogecoin/dogecoinj/crypto/X509UtilsTest.java deleted file mode 100644 index c22484a9..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/crypto/X509UtilsTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.crypto; - -import org.junit.Test; - -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import static org.junit.Assert.assertEquals; - -public class X509UtilsTest { - - @Test - public void testDisplayName() throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - - X509Certificate clientCert = (X509Certificate) cf.generateCertificate(getClass().getResourceAsStream( - "startssl-client.crt")); - assertEquals("Andreas Schildbach", X509Utils.getDisplayNameFromCertificate(clientCert, false)); - - X509Certificate comodoCert = (X509Certificate) cf.generateCertificate(getClass().getResourceAsStream( - "comodo-smime.crt")); - assertEquals("comodo.com@schildbach.de", X509Utils.getDisplayNameFromCertificate(comodoCert, true)); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/net/NetworkAbstractionTests.java b/core/src/test/java/com/dogecoin/dogecoinj/net/NetworkAbstractionTests.java deleted file mode 100644 index 1e29b6a2..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/net/NetworkAbstractionTests.java +++ /dev/null @@ -1,662 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.net; - -import com.dogecoin.dogecoinj.core.Utils; -import com.google.common.util.concurrent.SettableFuture; -import com.google.protobuf.ByteString; -import org.bitcoin.paymentchannel.Protos; -import org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import javax.net.SocketFactory; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.atomic.AtomicBoolean; - -import static com.google.common.base.Preconditions.checkState; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -@RunWith(value = Parameterized.class) -public class NetworkAbstractionTests { - private static final int CLIENT_MAJOR_VERSION = 1; - private AtomicBoolean fail; - private final int clientType; - private final ClientConnectionManager channels; - - @Parameterized.Parameters - public static Collection parameters() { - return Arrays.asList(new Integer[]{0}, new Integer[]{1}, new Integer[]{2}, new Integer[]{3}); - } - - public NetworkAbstractionTests(Integer clientType) throws Exception { - this.clientType = clientType; - if (clientType == 0) { - channels = new NioClientManager(); - channels.startAsync(); - } else if (clientType == 1) { - channels = new BlockingClientManager(); - channels.startAsync(); - } else - channels = null; - } - - private MessageWriteTarget openConnection(SocketAddress addr, ProtobufParser parser) throws Exception { - if (clientType == 0 || clientType == 1) { - channels.openConnection(addr, parser); - if (parser.writeTarget.get() == null) - Thread.sleep(100); - return (MessageWriteTarget) parser.writeTarget.get(); - } else if (clientType == 2) - return new NioClient(addr, parser, 100); - else if (clientType == 3) - return new BlockingClient(addr, parser, 100, SocketFactory.getDefault(), null); - else - throw new RuntimeException(); - } - - @Before - public void setUp() { - fail = new AtomicBoolean(false); - } - - @After - public void checkFail() { - assertFalse(fail.get()); - } - - @Test - public void testNullGetNewParser() throws Exception { - final SettableFuture client1ConnectionOpened = SettableFuture.create(); - final SettableFuture client1Disconnected = SettableFuture.create(); - final SettableFuture client2MessageReceived = SettableFuture.create(); - final SettableFuture serverConnectionOpen = SettableFuture.create(); - final SettableFuture client2ConnectionOpened = SettableFuture.create(); - final SettableFuture serverConnectionClosed = SettableFuture.create(); - final SettableFuture client2Disconnected = SettableFuture.create(); - NioServer server = new NioServer(new StreamParserFactory() { - boolean finishedFirst = false; - @Override - public ProtobufParser getNewParser(InetAddress inetAddress, int port) { - if (!finishedFirst) { - finishedFirst = true; - return null; - } - - return new ProtobufParser(new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - handler.write(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - serverConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - serverConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - } - }, new InetSocketAddress("localhost", 4243)); - server.startAsync(); - server.awaitRunning(); - - ProtobufParser clientHandler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public synchronized void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - fail.set(true); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - client1ConnectionOpened.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - client1Disconnected.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - openConnection(new InetSocketAddress("localhost", 4243), clientHandler); - - client1ConnectionOpened.get(); - client1Disconnected.get(); - - clientHandler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public synchronized void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - if (client2MessageReceived.isDone()) - fail.set(true); - client2MessageReceived.set(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - client2ConnectionOpened.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - client2Disconnected.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - MessageWriteTarget client = openConnection(new InetSocketAddress("localhost", 4243), clientHandler); - - serverConnectionOpen.get(); - client2ConnectionOpened.get(); - - Protos.TwoWayChannelMessage msg = Protos.TwoWayChannelMessage.newBuilder().setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN).build(); - clientHandler.write(msg); - - assertEquals(msg, client2MessageReceived.get()); - - client.closeConnection(); - serverConnectionClosed.get(); - client2Disconnected.get(); - - server.stopAsync().awaitTerminated(); - } - - @Test - public void basicClientServerTest() throws Exception { - // Tests creating a basic server, opening a client connection and sending a few messages - - final SettableFuture serverConnectionOpen = SettableFuture.create(); - final SettableFuture clientConnectionOpen = SettableFuture.create(); - final SettableFuture serverConnectionClosed = SettableFuture.create(); - final SettableFuture clientConnectionClosed = SettableFuture.create(); - final SettableFuture clientMessage1Received = SettableFuture.create(); - final SettableFuture clientMessage2Received = SettableFuture.create(); - NioServer server = new NioServer(new StreamParserFactory() { - @Override - public ProtobufParser getNewParser(InetAddress inetAddress, int port) { - return new ProtobufParser(new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - handler.write(msg); - handler.write(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - serverConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - serverConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - } - }, new InetSocketAddress("localhost", 4243)); - server.startAsync(); - server.awaitRunning(); - - ProtobufParser clientHandler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public synchronized void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - if (clientMessage1Received.isDone()) - clientMessage2Received.set(msg); - else - clientMessage1Received.set(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - clientConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - clientConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - - MessageWriteTarget client = openConnection(new InetSocketAddress("localhost", 4243), clientHandler); - - clientConnectionOpen.get(); - serverConnectionOpen.get(); - - Protos.TwoWayChannelMessage msg = Protos.TwoWayChannelMessage.newBuilder().setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN).build(); - clientHandler.write(msg); - assertEquals(msg, clientMessage1Received.get()); - assertEquals(msg, clientMessage2Received.get()); - - client.closeConnection(); - serverConnectionClosed.get(); - clientConnectionClosed.get(); - - server.stopAsync(); - server.awaitTerminated(); - assertFalse(server.isRunning()); - } - - @Test - public void basicTimeoutTest() throws Exception { - // Tests various timeout scenarios - - final SettableFuture serverConnection1Open = SettableFuture.create(); - final SettableFuture clientConnection1Open = SettableFuture.create(); - final SettableFuture serverConnection1Closed = SettableFuture.create(); - final SettableFuture clientConnection1Closed = SettableFuture.create(); - - final SettableFuture serverConnection2Open = SettableFuture.create(); - final SettableFuture clientConnection2Open = SettableFuture.create(); - final SettableFuture serverConnection2Closed = SettableFuture.create(); - final SettableFuture clientConnection2Closed = SettableFuture.create(); - NioServer server = new NioServer(new StreamParserFactory() { - @Override - public ProtobufParser getNewParser(InetAddress inetAddress, int port) { - return new ProtobufParser(new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - fail.set(true); - } - - @Override - public synchronized void connectionOpen(ProtobufParser handler) { - if (serverConnection1Open.isDone()) { - handler.setSocketTimeout(0); - serverConnection2Open.set(null); - } else - serverConnection1Open.set(null); - } - - @Override - public synchronized void connectionClosed(ProtobufParser handler) { - if (serverConnection1Closed.isDone()) { - serverConnection2Closed.set(null); - } else - serverConnection1Closed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 10); - } - }, new InetSocketAddress("localhost", 4243)); - server.startAsync(); - server.awaitRunning(); - - openConnection(new InetSocketAddress("localhost", 4243), new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - fail.set(true); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - clientConnection1Open.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - clientConnection1Closed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0)); - - clientConnection1Open.get(); - serverConnection1Open.get(); - long closeDelayStart = System.currentTimeMillis(); - clientConnection1Closed.get(); - serverConnection1Closed.get(); - long closeDelayFinish = System.currentTimeMillis(); - - ProtobufParser client2Handler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - fail.set(true); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - clientConnection2Open.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - clientConnection2Closed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - openConnection(new InetSocketAddress("localhost", 4243), client2Handler); - - clientConnection2Open.get(); - serverConnection2Open.get(); - Thread.sleep((closeDelayFinish - closeDelayStart) * 10); - assertFalse(clientConnection2Closed.isDone() || serverConnection2Closed.isDone()); - - client2Handler.setSocketTimeout(10); - clientConnection2Closed.get(); - serverConnection2Closed.get(); - - server.stopAsync(); - server.awaitTerminated(); - } - - @Test - public void largeDataTest() throws Exception { - /** Test various large-data handling, essentially testing {@link ProtobufParser#receiveBytes(java.nio.ByteBuffer)} */ - final SettableFuture serverConnectionOpen = SettableFuture.create(); - final SettableFuture clientConnectionOpen = SettableFuture.create(); - final SettableFuture serverConnectionClosed = SettableFuture.create(); - final SettableFuture clientConnectionClosed = SettableFuture.create(); - final SettableFuture clientMessage1Received = SettableFuture.create(); - final SettableFuture clientMessage2Received = SettableFuture.create(); - final SettableFuture clientMessage3Received = SettableFuture.create(); - final SettableFuture clientMessage4Received = SettableFuture.create(); - NioServer server = new NioServer(new StreamParserFactory() { - @Override - public ProtobufParser getNewParser(InetAddress inetAddress, int port) { - return new ProtobufParser(new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - handler.write(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - serverConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - serverConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 0x10000, 0); - } - }, new InetSocketAddress("localhost", 4243)); - server.startAsync(); - server.awaitRunning(); - - ProtobufParser clientHandler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public synchronized void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - if (clientMessage1Received.isDone()) { - if (clientMessage2Received.isDone()) { - if (clientMessage3Received.isDone()) { - if (clientMessage4Received.isDone()) - fail.set(true); - clientMessage4Received.set(msg); - } else - clientMessage3Received.set(msg); - } else - clientMessage2Received.set(msg); - } else - clientMessage1Received.set(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - clientConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - clientConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 0x10000, 0); - - MessageWriteTarget client = openConnection(new InetSocketAddress("localhost", 4243), clientHandler); - - clientConnectionOpen.get(); - serverConnectionOpen.get(); - - // Large message that is larger than buffer and equal to maximum message size - Protos.TwoWayChannelMessage msg = Protos.TwoWayChannelMessage.newBuilder() - .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN) - .setClientVersion(Protos.ClientVersion.newBuilder() - .setMajor(CLIENT_MAJOR_VERSION) - .setPreviousChannelContractHash(ByteString.copyFrom(new byte[0x10000 - 12]))) - .build(); - // Small message that fits in the buffer - Protos.TwoWayChannelMessage msg2 = Protos.TwoWayChannelMessage.newBuilder() - .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN) - .setClientVersion(Protos.ClientVersion.newBuilder() - .setMajor(CLIENT_MAJOR_VERSION) - .setPreviousChannelContractHash(ByteString.copyFrom(new byte[1]))) - .build(); - // Break up the message into chunks to simulate packet network (with strange MTUs...) - byte[] messageBytes = msg.toByteArray(); - byte[] messageLength = new byte[4]; - Utils.uint32ToByteArrayBE(messageBytes.length, messageLength, 0); - client.writeBytes(new byte[]{messageLength[0], messageLength[1]}); - Thread.sleep(10); - client.writeBytes(new byte[]{messageLength[2], messageLength[3]}); - Thread.sleep(10); - client.writeBytes(new byte[]{messageBytes[0], messageBytes[1]}); - Thread.sleep(10); - client.writeBytes(Arrays.copyOfRange(messageBytes, 2, messageBytes.length - 1)); - Thread.sleep(10); - - // Now send the end of msg + msg2 + msg3 all at once - byte[] messageBytes2 = msg2.toByteArray(); - byte[] messageLength2 = new byte[4]; - Utils.uint32ToByteArrayBE(messageBytes2.length, messageLength2, 0); - byte[] sendBytes = Arrays.copyOf(new byte[] {messageBytes[messageBytes.length-1]}, 1 + messageBytes2.length*2 + messageLength2.length*2); - System.arraycopy(messageLength2, 0, sendBytes, 1, 4); - System.arraycopy(messageBytes2, 0, sendBytes, 5, messageBytes2.length); - System.arraycopy(messageLength2, 0, sendBytes, 5 + messageBytes2.length, 4); - System.arraycopy(messageBytes2, 0, sendBytes, 9 + messageBytes2.length, messageBytes2.length); - client.writeBytes(sendBytes); - assertEquals(msg, clientMessage1Received.get()); - assertEquals(msg2, clientMessage2Received.get()); - assertEquals(msg2, clientMessage3Received.get()); - - // Now resent msg2 in chunks, by itself - Utils.uint32ToByteArrayBE(messageBytes2.length, messageLength2, 0); - client.writeBytes(new byte[]{messageLength2[0], messageLength2[1]}); - Thread.sleep(10); - client.writeBytes(new byte[]{messageLength2[2], messageLength2[3]}); - Thread.sleep(10); - client.writeBytes(new byte[]{messageBytes2[0], messageBytes2[1]}); - Thread.sleep(10); - client.writeBytes(new byte[]{messageBytes2[2], messageBytes2[3]}); - Thread.sleep(10); - client.writeBytes(Arrays.copyOfRange(messageBytes2, 4, messageBytes2.length)); - assertEquals(msg2, clientMessage4Received.get()); - - Protos.TwoWayChannelMessage msg5 = Protos.TwoWayChannelMessage.newBuilder() - .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN) - .setClientVersion(Protos.ClientVersion.newBuilder() - .setMajor(CLIENT_MAJOR_VERSION) - .setPreviousChannelContractHash(ByteString.copyFrom(new byte[0x10000 - 11]))) - .build(); - try { - clientHandler.write(msg5); - } catch (IllegalStateException e) {} - - // Override max size and make sure the server drops our connection - byte[] messageLength5 = new byte[4]; - Utils.uint32ToByteArrayBE(msg5.toByteArray().length, messageLength5, 0); - client.writeBytes(messageLength5); - - serverConnectionClosed.get(); - clientConnectionClosed.get(); - - server.stopAsync(); - server.awaitTerminated(); - } - - @Test - public void testConnectionEventHandlers() throws Exception { - final SettableFuture serverConnection1Open = SettableFuture.create(); - final SettableFuture serverConnection2Open = SettableFuture.create(); - final SettableFuture serverConnection3Open = SettableFuture.create(); - final SettableFuture client1ConnectionOpen = SettableFuture.create(); - final SettableFuture client2ConnectionOpen = SettableFuture.create(); - final SettableFuture client3ConnectionOpen = SettableFuture.create(); - final SettableFuture serverConnectionClosed1 = SettableFuture.create(); - final SettableFuture serverConnectionClosed2 = SettableFuture.create(); - final SettableFuture serverConnectionClosed3 = SettableFuture.create(); - final SettableFuture client1ConnectionClosed = SettableFuture.create(); - final SettableFuture client2ConnectionClosed = SettableFuture.create(); - final SettableFuture client3ConnectionClosed = SettableFuture.create(); - final SettableFuture client1MessageReceived = SettableFuture.create(); - final SettableFuture client2MessageReceived = SettableFuture.create(); - final SettableFuture client3MessageReceived = SettableFuture.create(); - NioServer server = new NioServer(new StreamParserFactory() { - @Override - public ProtobufParser getNewParser(InetAddress inetAddress, int port) { - return new ProtobufParser(new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - handler.write(msg); - } - - @Override - public synchronized void connectionOpen(ProtobufParser handler) { - if (serverConnection1Open.isDone()) { - if (serverConnection2Open.isDone()) - serverConnection3Open.set(null); - else - serverConnection2Open.set(null); - } else - serverConnection1Open.set(null); - } - - @Override - public synchronized void connectionClosed(ProtobufParser handler) { - if (serverConnectionClosed1.isDone()) { - if (serverConnectionClosed2.isDone()) { - checkState(!serverConnectionClosed3.isDone()); - serverConnectionClosed3.set(null); - } else - serverConnectionClosed2.set(null); - } else - serverConnectionClosed1.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - } - }, new InetSocketAddress("localhost", 4243)); - server.startAsync(); - server.awaitRunning(); - - ProtobufParser client1Handler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - client1MessageReceived.set(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - client1ConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - client1ConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - MessageWriteTarget client1 = openConnection(new InetSocketAddress("localhost", 4243), client1Handler); - - client1ConnectionOpen.get(); - serverConnection1Open.get(); - - ProtobufParser client2Handler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - client2MessageReceived.set(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - client2ConnectionOpen.set(null); - } - - @Override - public void connectionClosed(ProtobufParser handler) { - client2ConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - openConnection(new InetSocketAddress("localhost", 4243), client2Handler); - - client2ConnectionOpen.get(); - serverConnection2Open.get(); - - ProtobufParser client3Handler = new ProtobufParser( - new ProtobufParser.Listener() { - @Override - public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) { - client3MessageReceived.set(msg); - } - - @Override - public void connectionOpen(ProtobufParser handler) { - client3ConnectionOpen.set(null); - } - - @Override - public synchronized void connectionClosed(ProtobufParser handler) { - checkState(!client3ConnectionClosed.isDone()); - client3ConnectionClosed.set(null); - } - }, Protos.TwoWayChannelMessage.getDefaultInstance(), 1000, 0); - NioClient client3 = new NioClient(new InetSocketAddress("localhost", 4243), client3Handler, 0); - - client3ConnectionOpen.get(); - serverConnection3Open.get(); - - Protos.TwoWayChannelMessage msg = Protos.TwoWayChannelMessage.newBuilder().setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN).build(); - client1Handler.write(msg); - assertEquals(msg, client1MessageReceived.get()); - - Protos.TwoWayChannelMessage msg2 = Protos.TwoWayChannelMessage.newBuilder().setType(Protos.TwoWayChannelMessage.MessageType.INITIATE).build(); - client2Handler.write(msg2); - assertEquals(msg2, client2MessageReceived.get()); - - client1.closeConnection(); - serverConnectionClosed1.get(); - client1ConnectionClosed.get(); - - Protos.TwoWayChannelMessage msg3 = Protos.TwoWayChannelMessage.newBuilder().setType(Protos.TwoWayChannelMessage.MessageType.CLOSE).build(); - client3Handler.write(msg3); - assertEquals(msg3, client3MessageReceived.get()); - - // Try to create a race condition by triggering handlerThread closing and client3 closing at the same time - // This often triggers ClosedByInterruptException in handleKey - server.stopAsync(); - server.selector.wakeup(); - client3.closeConnection(); - client3ConnectionClosed.get(); - serverConnectionClosed3.get(); - - server.stopAsync(); - server.awaitTerminated(); - client2ConnectionClosed.get(); - serverConnectionClosed2.get(); - - server.stopAsync(); - server.awaitTerminated(); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/net/discovery/SeedPeersTest.java b/core/src/test/java/com/dogecoin/dogecoinj/net/discovery/SeedPeersTest.java deleted file mode 100644 index cfdc198f..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/net/discovery/SeedPeersTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2011 Micheal Swiggs - * - * 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.dogecoin.dogecoinj.net.discovery; - -import com.dogecoin.dogecoinj.params.MainNetParams; -import org.junit.Test; - -import java.net.InetSocketAddress; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; - -public class SeedPeersTest { - @Test - public void getPeer_one() throws Exception{ - SeedPeers seedPeers = new SeedPeers(MainNetParams.get()); - assertThat(seedPeers.getPeer(), notNullValue()); - } - - @Test - public void getPeer_all() throws Exception{ - SeedPeers seedPeers = new SeedPeers(MainNetParams.get()); - for(int i = 0; i < SeedPeers.seedAddrs.length; ++i){ - assertThat("Failed on index: "+i, seedPeers.getPeer(), notNullValue()); - } - assertThat(seedPeers.getPeer(), equalTo(null)); - } - - @Test - public void getPeers_length() throws Exception{ - SeedPeers seedPeers = new SeedPeers(MainNetParams.get()); - InetSocketAddress[] addresses = seedPeers.getPeers(0, TimeUnit.SECONDS); - assertThat(addresses.length, equalTo(SeedPeers.seedAddrs.length)); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelConnectionTest.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelConnectionTest.java deleted file mode 100644 index 13b615c3..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelConnectionTest.java +++ /dev/null @@ -1,781 +0,0 @@ -/* - * Copyright 2013 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.dogecoin.dogecoinj.protocols.channels; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.store.WalletProtobufSerializer; -import com.dogecoin.dogecoinj.testing.TestWithWallet; -import com.dogecoin.dogecoinj.utils.Threading; -import com.dogecoin.dogecoinj.wallet.WalletFiles; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; -import com.google.protobuf.ByteString; -import org.bitcoin.paymentchannel.Protos; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.spongycastle.crypto.params.KeyParameter; - -import javax.annotation.Nullable; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Arrays; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.protocols.channels.PaymentChannelCloseException.CloseReason; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeBlock; -import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage.MessageType; -import static org.junit.Assert.*; - -public class ChannelConnectionTest extends TestWithWallet { - private static final int CLIENT_MAJOR_VERSION = 1; - private Wallet serverWallet; - private BlockChain serverChain; - private AtomicBoolean fail; - private BlockingQueue broadcasts; - private TransactionBroadcaster mockBroadcaster; - private Semaphore broadcastTxPause; - - private static final TransactionBroadcaster failBroadcaster = new TransactionBroadcaster() { - @Override - public ListenableFuture broadcastTransaction(Transaction tx) { - fail(); - return null; - } - }; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - Utils.setMockClock(); // Use mock clock - sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN); - sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN); - wallet.addExtension(new StoredPaymentChannelClientStates(wallet, failBroadcaster)); - serverWallet = new Wallet(params); - serverWallet.addExtension(new StoredPaymentChannelServerStates(serverWallet, failBroadcaster)); - serverWallet.freshReceiveKey(); - serverChain = new BlockChain(params, serverWallet, blockStore); - // Use an atomic boolean to indicate failure because fail()/assert*() dont work in network threads - fail = new AtomicBoolean(false); - - // Set up a way to monitor broadcast transactions. When you expect a broadcast, you must release a permit - // to the broadcastTxPause semaphore so state can be queried in between. - broadcasts = new LinkedBlockingQueue(); - broadcastTxPause = new Semaphore(0); - mockBroadcaster = new TransactionBroadcaster() { - @Override - public ListenableFuture broadcastTransaction(Transaction tx) { - broadcastTxPause.acquireUninterruptibly(); - SettableFuture future = SettableFuture.create(); - future.set(tx); - broadcasts.add(tx); - return future; - } - }; - - // Because there are no separate threads in the tests here (we call back into client/server in server/client - // handlers), we have lots of lock cycles. A normal user shouldn't have this issue as they are probably not both - // client+server running in the same thread. - Threading.warnOnLockCycles(); - - ECKey.FAKE_SIGNATURES = true; - } - - @After - @Override - public void tearDown() throws Exception { - super.tearDown(); - ECKey.FAKE_SIGNATURES = false; - } - - @After - public void checkFail() { - assertFalse(fail.get()); - Threading.throwOnLockCycles(); - } - - @Test - public void testSimpleChannel() throws Exception { - exectuteSimpleChannelTest(null); - } - - @Test - public void testEncryptedClientWallet() throws Exception { - // Encrypt the client wallet - String mySecretPw = "MySecret"; - wallet.encrypt(mySecretPw); - - KeyParameter userKeySetup = wallet.getKeyCrypter().deriveKey(mySecretPw); - exectuteSimpleChannelTest(userKeySetup); - } - - public void exectuteSimpleChannelTest(KeyParameter userKeySetup) throws Exception { - // Test with network code and without any issues. We'll broadcast two txns: multisig contract and settle transaction. - final SettableFuture> serverCloseFuture = SettableFuture.create(); - final SettableFuture channelOpenFuture = SettableFuture.create(); - final BlockingQueue q = new LinkedBlockingQueue(); - final PaymentChannelServerListener server = new PaymentChannelServerListener(mockBroadcaster, serverWallet, 30, COIN, - new PaymentChannelServerListener.HandlerFactory() { - @Nullable - @Override - public ServerConnectionEventHandler onNewConnection(SocketAddress clientAddress) { - return new ServerConnectionEventHandler() { - @Override - public void channelOpen(Sha256Hash channelId) { - channelOpenFuture.set(channelId); - } - - @Override - public ListenableFuture paymentIncrease(Coin by, Coin to, ByteString info) { - q.add(new ChannelTestUtils.UpdatePair(to, info)); - return Futures.immediateFuture(info); - } - - @Override - public void channelClosed(CloseReason reason) { - serverCloseFuture.set(null); - } - }; - } - }); - server.bindAndStart(4243); - - PaymentChannelClientConnection client = new PaymentChannelClientConnection( - new InetSocketAddress("localhost", 4243), 30, wallet, myKey, COIN, "", PaymentChannelClient.DEFAULT_TIME_WINDOW, userKeySetup); - - // Wait for the multi-sig tx to be transmitted. - broadcastTxPause.release(); - Transaction broadcastMultiSig = broadcasts.take(); - // Wait for the channel to finish opening. - client.getChannelOpenFuture().get(); - assertEquals(broadcastMultiSig.getHash(), channelOpenFuture.get()); - assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, client.state().getValueSpent()); - - // Set up an autosave listener to make sure the server is saving the wallet after each payment increase. - final CountDownLatch latch = new CountDownLatch(3); // Expect 3 calls. - File tempFile = File.createTempFile("channel_connection_test", ".wallet"); - tempFile.deleteOnExit(); - serverWallet.autosaveToFile(tempFile, 0, TimeUnit.SECONDS, new WalletFiles.Listener() { - @Override - public void onBeforeAutoSave(File tempFile) { - latch.countDown(); - } - - @Override - public void onAfterAutoSave(File newlySavedFile) { - } - }); - - Thread.sleep(1250); // No timeouts once the channel is open - Coin amount = client.state().getValueSpent(); - q.take().assertPair(amount, null); - for (String info : new String[] {null, "one", "two"} ) { - final ByteString bytes = (info==null) ? null :ByteString.copyFromUtf8(info); - final PaymentIncrementAck ack = client.incrementPayment(CENT, bytes, userKeySetup).get(); - if (info != null) { - final ByteString ackInfo = ack.getInfo(); - assertNotNull("Ack info is null", ackInfo); - assertEquals("Ack info differs ", info, ackInfo.toStringUtf8()); - } - amount = amount.add(CENT); - q.take().assertPair(amount, bytes); - } - latch.await(); - - StoredPaymentChannelServerStates channels = (StoredPaymentChannelServerStates)serverWallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID); - StoredServerChannel storedServerChannel = channels.getChannel(broadcastMultiSig.getHash()); - PaymentChannelServerState serverState = storedServerChannel.getOrCreateState(serverWallet, mockBroadcaster); - - // Check that you can call settle multiple times with no exceptions. - client.settle(); - client.settle(); - - broadcastTxPause.release(); - Transaction settleTx = broadcasts.take(); - assertEquals(PaymentChannelServerState.State.CLOSED, serverState.getState()); - if (!serverState.getBestValueToMe().equals(amount) || !serverState.getFeePaid().equals(Coin.ZERO)) - fail(); - assertTrue(channels.mapChannels.isEmpty()); - - // Send the settle TX to the client wallet. - sendMoneyToWallet(settleTx, AbstractBlockChain.NewBlockType.BEST_CHAIN); - assertEquals(PaymentChannelClientState.State.CLOSED, client.state().getState()); - - server.close(); - server.close(); - - // Now confirm the settle TX and see if the channel deletes itself from the wallet. - assertEquals(1, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size()); - wallet.notifyNewBestBlock(createFakeBlock(blockStore).storedBlock); - assertEquals(1, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size()); - wallet.notifyNewBestBlock(createFakeBlock(blockStore).storedBlock); - assertEquals(0, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size()); - } - - @Test - public void testServerErrorHandling() throws Exception { - // Gives the server crap and checks proper error responses are sent. - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - PaymentChannelServer server = pair.server; - server.connectionOpen(); - client.connectionOpen(); - - // Make sure we get back a BAD_TRANSACTION if we send a bogus refund transaction. - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND); - server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.PROVIDE_REFUND) - .setProvideRefund( - Protos.ProvideRefund.newBuilder(msg.getProvideRefund()) - .setMultisigKey(ByteString.EMPTY) - .setTx(ByteString.EMPTY) - ).build()); - final Protos.TwoWayChannelMessage errorMsg = pair.serverRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(Protos.Error.ErrorCode.BAD_TRANSACTION, errorMsg.getError().getCode()); - - // Make sure the server closes the socket on CLOSE - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - server = pair.server; - server.connectionOpen(); - client.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.settle(); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLOSE)); - assertEquals(CloseReason.CLIENT_REQUESTED_CLOSE, pair.serverRecorder.q.take()); - - - // Make sure the server closes the socket on ERROR - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - server = pair.server; - server.connectionOpen(); - client.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.ERROR) - .setError(Protos.Error.newBuilder().setCode(Protos.Error.ErrorCode.TIMEOUT)) - .build()); - assertEquals(CloseReason.REMOTE_SENT_ERROR, pair.serverRecorder.q.take()); - } - - @Test - public void testChannelResume() throws Exception { - // Tests various aspects of channel resuming. - Utils.setMockClock(); - - final Sha256Hash someServerId = Sha256Hash.create(new byte[]{}); - - // Open up a normal channel. - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - pair.server.connectionOpen(); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - PaymentChannelServer server = pair.server; - client.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - final Protos.TwoWayChannelMessage initiateMsg = pair.serverRecorder.checkNextMsg(MessageType.INITIATE); - Coin minPayment = Coin.valueOf(initiateMsg.getInitiate().getMinPayment()); - client.receiveMessage(initiateMsg); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.RETURN_REFUND)); - broadcastTxPause.release(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_CONTRACT)); - broadcasts.take(); - pair.serverRecorder.checkTotalPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN)); - Sha256Hash contractHash = (Sha256Hash) pair.serverRecorder.q.take(); - pair.clientRecorder.checkInitiated(); - assertNull(pair.serverRecorder.q.poll()); - assertNull(pair.clientRecorder.q.poll()); - assertEquals(minPayment, client.state().getValueSpent()); - // Send a bitcent. - Coin amount = minPayment.add(CENT); - client.incrementPayment(CENT); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT)); - assertEquals(amount, ((ChannelTestUtils.UpdatePair)pair.serverRecorder.q.take()).amount); - server.close(); - server.connectionClosed(); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CLOSE)); - client.connectionClosed(); - assertFalse(client.connectionOpen); - - // There is now an inactive open channel worth COIN-CENT + minPayment with id Sha256.create(new byte[] {}) - StoredPaymentChannelClientStates clientStoredChannels = - (StoredPaymentChannelClientStates) wallet.getExtensions().get(StoredPaymentChannelClientStates.EXTENSION_ID); - assertEquals(1, clientStoredChannels.mapChannels.size()); - assertFalse(clientStoredChannels.mapChannels.values().iterator().next().active); - - // Check that server-side won't attempt to reopen a nonexistent channel (it will tell the client to re-initiate - // instead). - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - pair.server.connectionOpen(); - pair.server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.CLIENT_VERSION) - .setClientVersion(Protos.ClientVersion.newBuilder() - .setPreviousChannelContractHash(ByteString.copyFrom(Sha256Hash.create(new byte[]{0x03}).getBytes())) - .setMajor(CLIENT_MAJOR_VERSION).setMinor(42)) - .build()); - pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION); - pair.serverRecorder.checkNextMsg(MessageType.INITIATE); - - // Now reopen/resume the channel after round-tripping the wallets. - wallet = roundTripClientWallet(wallet); - serverWallet = roundTripServerWallet(serverWallet); - clientStoredChannels = - (StoredPaymentChannelClientStates) wallet.getExtensions().get(StoredPaymentChannelClientStates.EXTENSION_ID); - - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - server = pair.server; - client.connectionOpen(); - server.connectionOpen(); - // Check the contract hash is sent on the wire correctly. - final Protos.TwoWayChannelMessage clientVersionMsg = pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION); - assertTrue(clientVersionMsg.getClientVersion().hasPreviousChannelContractHash()); - assertEquals(contractHash, new Sha256Hash(clientVersionMsg.getClientVersion().getPreviousChannelContractHash().toByteArray())); - server.receiveMessage(clientVersionMsg); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN)); - assertEquals(contractHash, pair.serverRecorder.q.take()); - pair.clientRecorder.checkOpened(); - assertNull(pair.serverRecorder.q.poll()); - assertNull(pair.clientRecorder.q.poll()); - // Send another bitcent and check 2 were received in total. - client.incrementPayment(CENT); - amount = amount.add(CENT); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT)); - pair.serverRecorder.checkTotalPayment(amount); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK)); - - PaymentChannelClient openClient = client; - ChannelTestUtils.RecordingPair openPair = pair; - - // Now open up a new client with the same id and make sure the server disconnects the previous client. - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - server = pair.server; - client.connectionOpen(); - server.connectionOpen(); - // Check that no prev contract hash is sent on the wire the client notices it's already in use by another - // client attached to the same wallet and refuses to resume. - { - Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION); - assertFalse(msg.getClientVersion().hasPreviousChannelContractHash()); - } - // Make sure the server allows two simultaneous opens. It will close the first and allow resumption of the second. - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - server = pair.server; - client.connectionOpen(); - server.connectionOpen(); - // Swap out the clients version message for a custom one that tries to resume ... - pair.clientRecorder.getNextMsg(); - server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.CLIENT_VERSION) - .setClientVersion(Protos.ClientVersion.newBuilder() - .setPreviousChannelContractHash(ByteString.copyFrom(contractHash.getBytes())) - .setMajor(CLIENT_MAJOR_VERSION).setMinor(42)) - .build()); - // We get the usual resume sequence. - pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION); - pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN); - // Verify the previous one was closed. - openPair.serverRecorder.checkNextMsg(MessageType.CLOSE); - - assertTrue(clientStoredChannels.getChannel(someServerId, contractHash).active); - - // And finally close the first channel too. - openClient.connectionClosed(); - assertFalse(clientStoredChannels.getChannel(someServerId, contractHash).active); - - // Now roll the mock clock and recreate the client object so that it removes the channels and announces refunds. - assertEquals(86640, clientStoredChannels.getSecondsUntilExpiry(someServerId)); - Utils.rollMockClock(60 * 60 * 24 + 60 * 5); // Client announces refund 5 minutes after expire time - StoredPaymentChannelClientStates newClientStates = new StoredPaymentChannelClientStates(wallet, mockBroadcaster); - newClientStates.deserializeWalletExtension(wallet, clientStoredChannels.serializeWalletExtension()); - broadcastTxPause.release(); - assertTrue(broadcasts.take().getOutput(0).getScriptPubKey().isSentToMultiSig()); - broadcastTxPause.release(); - assertEquals(TransactionConfidence.Source.SELF, broadcasts.take().getConfidence().getSource()); - assertTrue(broadcasts.isEmpty()); - assertTrue(newClientStates.mapChannels.isEmpty()); - // Server also knows it's too late. - StoredPaymentChannelServerStates serverStoredChannels = new StoredPaymentChannelServerStates(serverWallet, mockBroadcaster); - Thread.sleep(2000); // TODO: Fix this stupid hack. - assertTrue(serverStoredChannels.mapChannels.isEmpty()); - } - - private static Wallet roundTripClientWallet(Wallet wallet) throws Exception { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - new WalletProtobufSerializer().writeWallet(wallet, bos); - com.dogecoin.dogecoinj.wallet.Protos.Wallet proto = WalletProtobufSerializer.parseToProto(new ByteArrayInputStream(bos.toByteArray())); - StoredPaymentChannelClientStates state = new StoredPaymentChannelClientStates(null, failBroadcaster); - return new WalletProtobufSerializer().readWallet(wallet.getParams(), new WalletExtension[] { state }, proto); - } - - private static Wallet roundTripServerWallet(Wallet wallet) throws Exception { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - new WalletProtobufSerializer().writeWallet(wallet, bos); - StoredPaymentChannelServerStates state = new StoredPaymentChannelServerStates(null, failBroadcaster); - com.dogecoin.dogecoinj.wallet.Protos.Wallet proto = WalletProtobufSerializer.parseToProto(new ByteArrayInputStream(bos.toByteArray())); - return new WalletProtobufSerializer().readWallet(wallet.getParams(), new WalletExtension[] { state }, proto); - } - - @Test - public void testBadResumeHash() throws InterruptedException { - // Check that server-side will reject incorrectly formatted hashes. If anything goes wrong with session resume, - // then the server will start the opening of a new channel automatically, so we expect to see INITIATE here. - ChannelTestUtils.RecordingPair srv = - ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - srv.server.connectionOpen(); - srv.server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.CLIENT_VERSION) - .setClientVersion(Protos.ClientVersion.newBuilder() - .setPreviousChannelContractHash(ByteString.copyFrom(new byte[]{0x00, 0x01})) - .setMajor(CLIENT_MAJOR_VERSION).setMinor(42)) - .build()); - - srv.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION); - srv.serverRecorder.checkNextMsg(MessageType.INITIATE); - assertTrue(srv.serverRecorder.q.isEmpty()); - } - - @Test - public void testClientUnknownVersion() throws Exception { - // Tests client rejects unknown version - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION); - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setServerVersion(Protos.ServerVersion.newBuilder().setMajor(-1)) - .setType(MessageType.SERVER_VERSION).build()); - pair.clientRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(CloseReason.NO_ACCEPTABLE_VERSION, pair.clientRecorder.q.take()); - // Double-check that we cant do anything that requires an open channel - try { - client.incrementPayment(Coin.SATOSHI); - fail(); - } catch (IllegalStateException e) { } - } - - @Test - public void testClientTimeWindowUnacceptable() throws Exception { - // Tests that clients reject too large time windows - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster, 100); - PaymentChannelServer server = pair.server; - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - server.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds() + 60 * 60 * 48) - .setMinAcceptedChannelSize(100) - .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey())) - .setMinPayment(Transaction.MIN_NONDUST_OUTPUT.value)) - .setType(MessageType.INITIATE).build()); - - pair.clientRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(CloseReason.TIME_WINDOW_UNACCEPTABLE, pair.clientRecorder.q.take()); - // Double-check that we cant do anything that requires an open channel - try { - client.incrementPayment(Coin.SATOSHI); - fail(); - } catch (IllegalStateException e) { } - } - - @Test - public void testValuesAreRespected() throws Exception { - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelServer server = pair.server; - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - server.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds()) - .setMinAcceptedChannelSize(COIN.add(SATOSHI).value) - .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey())) - .setMinPayment(Transaction.MIN_NONDUST_OUTPUT.value)) - .setType(MessageType.INITIATE).build()); - pair.clientRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(CloseReason.SERVER_REQUESTED_TOO_MUCH_VALUE, pair.clientRecorder.q.take()); - // Double-check that we cant do anything that requires an open channel - try { - client.incrementPayment(Coin.SATOSHI); - fail(); - } catch (IllegalStateException e) { } - - // Now check that if the server has a lower min size than what we are willing to spend, we do actually open - // a channel of that size. - sendMoneyToWallet(COIN.multiply(10), AbstractBlockChain.NewBlockType.BEST_CHAIN); - - pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - server = pair.server; - final Coin myValue = COIN.multiply(10); - client = new PaymentChannelClient(wallet, myKey, myValue, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - server.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds()) - .setMinAcceptedChannelSize(COIN.add(SATOSHI).value) - .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey())) - .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value)) - .setType(MessageType.INITIATE).build()); - final Protos.TwoWayChannelMessage provideRefund = pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND); - Transaction refund = new Transaction(params, provideRefund.getProvideRefund().getTx().toByteArray()); - assertEquals(myValue, refund.getOutput(0).getValue()); - } - - @Test - public void testEmptyWallet() throws Exception { - Wallet emptyWallet = new Wallet(params); - emptyWallet.freshReceiveKey(); - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelServer server = pair.server; - PaymentChannelClient client = new PaymentChannelClient(emptyWallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - server.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - try { - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds()) - .setMinAcceptedChannelSize(CENT.value) - .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey())) - .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value)) - .setType(MessageType.INITIATE).build()); - fail(); - } catch (InsufficientMoneyException expected) { - // This should be thrown. - } - } - - @Test - public void testClientRefusesNonCanonicalKey() throws Exception { - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelServer server = pair.server; - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - server.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - Protos.TwoWayChannelMessage.Builder initiateMsg = Protos.TwoWayChannelMessage.newBuilder(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - ByteString brokenKey = initiateMsg.getInitiate().getMultisigKey(); - brokenKey = ByteString.copyFrom(Arrays.copyOf(brokenKey.toByteArray(), brokenKey.size() + 1)); - initiateMsg.getInitiateBuilder().setMultisigKey(brokenKey); - client.receiveMessage(initiateMsg.build()); - pair.clientRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(CloseReason.REMOTE_SENT_INVALID_MESSAGE, pair.clientRecorder.q.take()); - } - - @Test - public void testClientResumeNothing() throws Exception { - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelServer server = pair.server; - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - client.connectionOpen(); - server.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.CHANNEL_OPEN).build()); - pair.clientRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(CloseReason.REMOTE_SENT_INVALID_MESSAGE, pair.clientRecorder.q.take()); - } - - @Test - public void testClientRandomMessage() throws Exception { - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder); - - client.connectionOpen(); - pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION); - // Send a CLIENT_VERSION back to the client - ?!?!! - client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() - .setType(MessageType.CLIENT_VERSION).build()); - Protos.TwoWayChannelMessage error = pair.clientRecorder.checkNextMsg(MessageType.ERROR); - assertEquals(Protos.Error.ErrorCode.SYNTAX_ERROR, error.getError().getCode()); - assertEquals(CloseReason.REMOTE_SENT_INVALID_MESSAGE, pair.clientRecorder.q.take()); - } - - @Test - public void testDontResumeEmptyChannels() throws Exception { - // Check that if the client has an empty channel that's being kept around in case we need to broadcast the - // refund, we don't accidentally try to resume it). - - // Open up a normal channel. - Sha256Hash someServerId = Sha256Hash.ZERO_HASH; - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - pair.server.connectionOpen(); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - PaymentChannelServer server = pair.server; - client.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.RETURN_REFUND)); - broadcastTxPause.release(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_CONTRACT)); - broadcasts.take(); - pair.serverRecorder.checkTotalPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN)); - Sha256Hash contractHash = (Sha256Hash) pair.serverRecorder.q.take(); - pair.clientRecorder.checkInitiated(); - assertNull(pair.serverRecorder.q.poll()); - assertNull(pair.clientRecorder.q.poll()); - // Send the whole channel at once. The server will broadcast the final contract and settle the channel for us. - client.incrementPayment(client.state().getValueRefunded()); - broadcastTxPause.release(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT)); - broadcasts.take(); - // The channel is now empty. - assertEquals(Coin.ZERO, client.state().getValueRefunded()); - pair.serverRecorder.q.take(); // Take the Coin. - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CLOSE)); - assertEquals(CloseReason.SERVER_REQUESTED_CLOSE, pair.clientRecorder.q.take()); - client.connectionClosed(); - - // Now try opening a new channel with the same server ID and verify the client asks for a new channel. - client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - client.connectionOpen(); - Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION); - assertFalse(msg.getClientVersion().hasPreviousChannelContractHash()); - } - - @Test - public void repeatedChannels() throws Exception { - // Ensures we're selecting channels correctly. Covers a bug in which we'd always try and fail to resume - // the first channel due to lack of proper closing behaviour. - // Open up a normal channel, but don't spend all of it, then settle it. - { - Sha256Hash someServerId = Sha256Hash.ZERO_HASH; - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - pair.server.connectionOpen(); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - PaymentChannelServer server = pair.server; - client.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.RETURN_REFUND)); - broadcastTxPause.release(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_CONTRACT)); - broadcasts.take(); - pair.serverRecorder.checkTotalPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN)); - Sha256Hash contractHash = (Sha256Hash) pair.serverRecorder.q.take(); - pair.clientRecorder.checkInitiated(); - assertNull(pair.serverRecorder.q.poll()); - assertNull(pair.clientRecorder.q.poll()); - for (int i = 0; i < 3; i++) { - ListenableFuture future = client.incrementPayment(CENT); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT)); - pair.serverRecorder.q.take(); - final Protos.TwoWayChannelMessage msg = pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK); - final Protos.PaymentAck paymentAck = msg.getPaymentAck(); - assertTrue("No PaymentAck.Info", paymentAck.hasInfo()); - assertEquals("Wrong PaymentAck info", ByteString.copyFromUtf8(CENT.toPlainString()), paymentAck.getInfo()); - client.receiveMessage(msg); - assertTrue(future.isDone()); - final PaymentIncrementAck paymentIncrementAck = future.get(); - assertEquals("Wrong value returned from increasePayment", CENT, paymentIncrementAck.getValue()); - assertEquals("Wrong info returned from increasePayment", ByteString.copyFromUtf8(CENT.toPlainString()), paymentIncrementAck.getInfo()); - } - - // Settle it and verify it's considered to be settled. - broadcastTxPause.release(); - client.settle(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLOSE)); - Transaction settlement1 = broadcasts.take(); - // Server sends back the settle TX it just broadcast. - final Protos.TwoWayChannelMessage closeMsg = pair.serverRecorder.checkNextMsg(MessageType.CLOSE); - final Transaction settlement2 = new Transaction(params, closeMsg.getSettlement().getTx().toByteArray()); - assertEquals(settlement1, settlement2); - client.receiveMessage(closeMsg); - assertNotNull(wallet.getTransaction(settlement2.getHash())); // Close TX entered the wallet. - sendMoneyToWallet(settlement1, AbstractBlockChain.NewBlockType.BEST_CHAIN); - client.connectionClosed(); - server.connectionClosed(); - } - // Now open a second channel and don't spend all of it/don't settle it. - { - Sha256Hash someServerId = Sha256Hash.ZERO_HASH; - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - pair.server.connectionOpen(); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - PaymentChannelServer server = pair.server; - client.connectionOpen(); - final Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION); - assertFalse(msg.getClientVersion().hasPreviousChannelContractHash()); - server.receiveMessage(msg); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE)); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.RETURN_REFUND)); - broadcastTxPause.release(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_CONTRACT)); - broadcasts.take(); - pair.serverRecorder.checkTotalPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN)); - Sha256Hash contractHash = (Sha256Hash) pair.serverRecorder.q.take(); - pair.clientRecorder.checkInitiated(); - assertNull(pair.serverRecorder.q.poll()); - assertNull(pair.clientRecorder.q.poll()); - client.incrementPayment(CENT); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT)); - client.connectionClosed(); - server.connectionClosed(); - } - // Now connect again and check we resume the second channel. - { - Sha256Hash someServerId = Sha256Hash.ZERO_HASH; - ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster); - pair.server.connectionOpen(); - PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder); - PaymentChannelServer server = pair.server; - client.connectionOpen(); - server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); - client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CHANNEL_OPEN)); - } - assertEquals(2, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelTestUtils.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelTestUtils.java deleted file mode 100644 index fa81ef7e..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/ChannelTestUtils.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.dogecoin.dogecoinj.protocols.channels; - -import com.dogecoin.dogecoinj.core.Coin; -import com.dogecoin.dogecoinj.core.Sha256Hash; -import com.dogecoin.dogecoinj.core.TransactionBroadcaster; -import com.dogecoin.dogecoinj.core.Wallet; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.protobuf.ByteString; -import org.bitcoin.paymentchannel.Protos; - -import javax.annotation.Nullable; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -import static org.junit.Assert.assertEquals; - -/** - * Various mock objects and utilities for testing payment channels code. - */ -public class ChannelTestUtils { - public static class RecordingServerConnection implements PaymentChannelServer.ServerConnection { - public BlockingQueue q = new LinkedBlockingQueue(); - - @Override - public void sendToClient(Protos.TwoWayChannelMessage msg) { - q.add(msg); - } - - @Override - public void destroyConnection(PaymentChannelCloseException.CloseReason reason) { - q.add(reason); - } - - @Override - public void channelOpen(Sha256Hash contractHash) { - q.add(contractHash); - } - - @Override - public ListenableFuture paymentIncrease(Coin by, Coin to, @Nullable ByteString info) { - q.add(new UpdatePair(to, info)); - return Futures.immediateFuture(ByteString.copyFromUtf8(by.toPlainString())); - } - - public Protos.TwoWayChannelMessage getNextMsg() throws InterruptedException { - return (Protos.TwoWayChannelMessage) q.take(); - } - - public Protos.TwoWayChannelMessage checkNextMsg(Protos.TwoWayChannelMessage.MessageType expectedType) throws InterruptedException { - Protos.TwoWayChannelMessage msg = getNextMsg(); - assertEquals(expectedType, msg.getType()); - return msg; - } - - public void checkTotalPayment(Coin valueSoFar) throws InterruptedException { - Coin lastSeen = ((UpdatePair) q.take()).amount; - assertEquals(lastSeen, valueSoFar); - } - } - - public static class RecordingClientConnection implements PaymentChannelClient.ClientConnection { - public BlockingQueue q = new LinkedBlockingQueue(); - final static int IGNORE_EXPIRE = -1; - private final int maxExpireTime; - - // An arbitrary sentinel object for equality testing. - public static final Object CHANNEL_INITIATED = new Object(); - public static final Object CHANNEL_OPEN = new Object(); - - public RecordingClientConnection(int maxExpireTime) { - this.maxExpireTime = maxExpireTime; - } - - @Override - public void sendToServer(Protos.TwoWayChannelMessage msg) { - q.add(msg); - } - - @Override - public void destroyConnection(PaymentChannelCloseException.CloseReason reason) { - q.add(reason); - } - - @Override - public boolean acceptExpireTime(long expireTime) { - return this.maxExpireTime == IGNORE_EXPIRE || expireTime <= maxExpireTime; - } - - @Override - public void channelOpen(boolean wasInitiated) { - if (wasInitiated) - q.add(CHANNEL_INITIATED); - q.add(CHANNEL_OPEN); - } - - public Protos.TwoWayChannelMessage getNextMsg() throws InterruptedException { - return (Protos.TwoWayChannelMessage) q.take(); - } - - public Protos.TwoWayChannelMessage checkNextMsg(Protos.TwoWayChannelMessage.MessageType expectedType) throws InterruptedException { - Protos.TwoWayChannelMessage msg = getNextMsg(); - assertEquals(expectedType, msg.getType()); - return msg; - } - - public void checkOpened() throws InterruptedException { - assertEquals(CHANNEL_OPEN, q.take()); - } - - public void checkInitiated() throws InterruptedException { - assertEquals(CHANNEL_INITIATED, q.take()); - checkOpened(); - } - } - - public static class RecordingPair { - public PaymentChannelServer server; - public RecordingServerConnection serverRecorder; - public RecordingClientConnection clientRecorder; - } - - public static RecordingPair makeRecorders(final Wallet serverWallet, final TransactionBroadcaster mockBroadcaster) { - return makeRecorders(serverWallet, mockBroadcaster, RecordingClientConnection.IGNORE_EXPIRE); - } - public static RecordingPair makeRecorders(final Wallet serverWallet, final TransactionBroadcaster mockBroadcaster, int maxExpireTime) { - RecordingPair pair = new RecordingPair(); - pair.serverRecorder = new RecordingServerConnection(); - pair.server = new PaymentChannelServer(mockBroadcaster, serverWallet, Coin.COIN, pair.serverRecorder); - pair.clientRecorder = new RecordingClientConnection(maxExpireTime); - return pair; - } - - public static class UpdatePair { - public Coin amount; - public ByteString info; - - public UpdatePair(Coin amount, ByteString info) { - this.amount = amount; - this.info = info; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - UpdatePair that = (UpdatePair) o; - - if (amount != null ? !amount.equals(that.amount) : that.amount != null) return false; - if (info != null ? !info.equals(that.info) : that.info != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = amount != null ? amount.hashCode() : 0; - result = 31 * result + (info != null ? info.hashCode() : 0); - return result; - } - - public void assertPair(Coin amount, ByteString info) { - assertEquals(amount, this.amount); - assertEquals(info, this.info); - } - } - -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelClientTest.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelClientTest.java deleted file mode 100644 index f71327bb..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelClientTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.dogecoin.dogecoinj.protocols.channels; - -import com.dogecoin.dogecoinj.core.*; -import org.bitcoin.paymentchannel.Protos; -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.Before; -import org.junit.Test; -import org.spongycastle.crypto.params.KeyParameter; - -import java.util.HashMap; - -import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage; -import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage.MessageType.*; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.replay; -import static org.junit.Assert.assertEquals; - -public class PaymentChannelClientTest { - - private static final int CLIENT_MAJOR_VERSION = 1; - private Wallet wallet; - private ECKey ecKey; - private Sha256Hash serverHash; - private IPaymentChannelClient.ClientConnection connection; - public Coin maxValue; - public Capture clientVersionCapture; - public int defaultTimeWindow = 86340; - - @Before - public void before() { - wallet = createMock(Wallet.class); - ecKey = createMock(ECKey.class); - maxValue = Coin.COIN; - serverHash = Sha256Hash.create("serverId".getBytes()); - connection = createMock(IPaymentChannelClient.ClientConnection.class); - clientVersionCapture = new Capture(); - } - - @Test - public void shouldSendClientVersionOnChannelOpen() throws Exception { - PaymentChannelClient dut = new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, connection); - connection.sendToServer(capture(clientVersionCapture)); - EasyMock.expect(wallet.getExtensions()).andReturn(new HashMap()); - replay(connection, wallet); - dut.connectionOpen(); - assertClientVersion(defaultTimeWindow); - } - @Test - public void shouldSendTimeWindowInClientVersion() throws Exception { - long timeWindow = 4000; - KeyParameter userKey = null; - PaymentChannelClient dut = - new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, timeWindow, userKey, connection); - connection.sendToServer(capture(clientVersionCapture)); - EasyMock.expect(wallet.getExtensions()).andReturn(new HashMap()); - replay(connection, wallet); - dut.connectionOpen(); - assertClientVersion(4000); - } - - private void assertClientVersion(long expectedTimeWindow) { - final TwoWayChannelMessage response = clientVersionCapture.getValue(); - final TwoWayChannelMessage.MessageType type = response.getType(); - assertEquals("Wrong type " + type, CLIENT_VERSION, type); - final Protos.ClientVersion clientVersion = response.getClientVersion(); - final int major = clientVersion.getMajor(); - assertEquals("Wrong major version " + major, CLIENT_MAJOR_VERSION, major); - final long actualTimeWindow = clientVersion.getTimeWindowSecs(); - assertEquals("Wrong timeWindow " + actualTimeWindow, expectedTimeWindow, actualTimeWindow ); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelServerTest.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelServerTest.java deleted file mode 100644 index 1a112bfa..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelServerTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.dogecoin.dogecoinj.protocols.channels; - -import com.dogecoin.dogecoinj.core.Coin; -import com.dogecoin.dogecoinj.core.TransactionBroadcaster; -import com.dogecoin.dogecoinj.core.Utils; -import com.dogecoin.dogecoinj.core.Wallet; -import org.bitcoin.paymentchannel.Protos; -import org.easymock.Capture; -import org.junit.Before; -import org.junit.Test; - -import static junit.framework.TestCase.assertTrue; -import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage; -import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage.MessageType; -import static org.easymock.EasyMock.*; -import static org.junit.Assert.assertEquals; - -public class PaymentChannelServerTest { - - private static final int CLIENT_MAJOR_VERSION = 1; - private static final long SERVER_MAJOR_VERSION = 1; - public Wallet wallet; - public PaymentChannelServer.ServerConnection connection; - public PaymentChannelServer dut; - public Capture serverVersionCapture; - private TransactionBroadcaster broadcaster; - - @Before - public void setUp() { - broadcaster = createMock(TransactionBroadcaster.class); - wallet = createMock(Wallet.class); - connection = createMock(PaymentChannelServer.ServerConnection.class); - serverVersionCapture = new Capture(); - connection.sendToClient(capture(serverVersionCapture)); - Utils.setMockClock(); - } - - - @Test - public void shouldAcceptDefaultTimeWindow() { - final TwoWayChannelMessage message = createClientVersionMessage(); - final Capture initiateCapture = new Capture(); - connection.sendToClient(capture(initiateCapture)); - replay(connection); - - dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, connection); - - dut.connectionOpen(); - dut.receiveMessage(message); - - long expectedExpire = Utils.currentTimeSeconds() + 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto - assertServerVersion(); - assertExpireTime(expectedExpire, initiateCapture); - } - - @Test - public void shouldTruncateTooSmallTimeWindow() { - final int minTimeWindow = 20000; - final int timeWindow = minTimeWindow - 1; - final TwoWayChannelMessage message = createClientVersionMessage(timeWindow); - final Capture initiateCapture = new Capture(); - connection.sendToClient(capture(initiateCapture)); - - replay(connection); - dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, minTimeWindow, 40000, connection); - - dut.connectionOpen(); - dut.receiveMessage(message); - - long expectedExpire = Utils.currentTimeSeconds() + minTimeWindow; - assertServerVersion(); - assertExpireTime(expectedExpire, initiateCapture); - } - - @Test - public void shouldTruncateTooLargeTimeWindow() { - final int maxTimeWindow = 40000; - final int timeWindow = maxTimeWindow + 1; - final TwoWayChannelMessage message = createClientVersionMessage(timeWindow); - final Capture initiateCapture = new Capture(); - connection.sendToClient(capture(initiateCapture)); - replay(connection); - - dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 20000, maxTimeWindow, connection); - - dut.connectionOpen(); - dut.receiveMessage(message); - - long expectedExpire = Utils.currentTimeSeconds() + maxTimeWindow; - assertServerVersion(); - assertExpireTime(expectedExpire, initiateCapture); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowTimeWindowLessThan2h() { - dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 7199, 40000, connection); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowNegativeTimeWindow() { - dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 40001, 40000, connection); - } - - @Test - public void shouldAllowExactTimeWindow() { - final TwoWayChannelMessage message = createClientVersionMessage(); - final Capture initiateCapture = new Capture(); - connection.sendToClient(capture(initiateCapture)); - replay(connection); - final int expire = 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto - - dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, expire, expire, connection); - dut.connectionOpen(); - long expectedExpire = Utils.currentTimeSeconds() + expire; - dut.receiveMessage(message); - - assertServerVersion(); - assertExpireTime(expectedExpire, initiateCapture); - } - - private void assertServerVersion() { - final TwoWayChannelMessage response = serverVersionCapture.getValue(); - final MessageType type = response.getType(); - assertEquals("Wrong type " + type, MessageType.SERVER_VERSION, type); - final long major = response.getServerVersion().getMajor(); - assertEquals("Wrong major version", SERVER_MAJOR_VERSION, major); - } - - private void assertExpireTime(long expectedExpire, Capture initiateCapture) { - final TwoWayChannelMessage response = initiateCapture.getValue(); - final MessageType type = response.getType(); - assertEquals("Wrong type " + type, MessageType.INITIATE, type); - final long actualExpire = response.getInitiate().getExpireTimeSecs(); - assertTrue("Expire time too small " + expectedExpire + " > " + actualExpire, expectedExpire <= actualExpire); - assertTrue("Expire time too large " + expectedExpire + "<" + actualExpire, expectedExpire >= actualExpire); - } - - private TwoWayChannelMessage createClientVersionMessage() { - final Protos.ClientVersion.Builder clientVersion = Protos.ClientVersion.newBuilder().setMajor(CLIENT_MAJOR_VERSION); - return TwoWayChannelMessage.newBuilder().setType(MessageType.CLIENT_VERSION).setClientVersion(clientVersion).build(); - } - - private TwoWayChannelMessage createClientVersionMessage(long timeWindow) { - final Protos.ClientVersion.Builder clientVersion = Protos.ClientVersion.newBuilder().setMajor(CLIENT_MAJOR_VERSION); - if (timeWindow > 0) clientVersion.setTimeWindowSecs(timeWindow); - return TwoWayChannelMessage.newBuilder().setType(MessageType.CLIENT_VERSION).setClientVersion(clientVersion).build(); - } - -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelStateTest.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelStateTest.java deleted file mode 100644 index 355ed872..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/channels/PaymentChannelStateTest.java +++ /dev/null @@ -1,805 +0,0 @@ -/* - * Copyright 2013 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.dogecoin.dogecoinj.protocols.channels; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.script.Script; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.dogecoin.dogecoinj.testing.TestWithWallet; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Iterator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.LinkedBlockingQueue; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeTx; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.makeSolvedTestBlock; -import static org.junit.Assert.*; - -public class PaymentChannelStateTest extends TestWithWallet { - private ECKey serverKey; - private Coin halfCoin; - private Wallet serverWallet; - private PaymentChannelServerState serverState; - private PaymentChannelClientState clientState; - private TransactionBroadcaster mockBroadcaster; - private BlockingQueue broadcasts; - - private static class TxFuturePair { - Transaction tx; - SettableFuture future; - - public TxFuturePair(Transaction tx, SettableFuture future) { - this.tx = tx; - this.future = future; - } - } - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - wallet.addExtension(new StoredPaymentChannelClientStates(wallet, new TransactionBroadcaster() { - @Override - public ListenableFuture broadcastTransaction(Transaction tx) { - fail(); - return null; - } - })); - sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN); - chain = new BlockChain(params, wallet, blockStore); // Recreate chain as sendMoneyToWallet will confuse it - serverWallet = new Wallet(params); - serverKey = serverWallet.freshReceiveKey(); - chain.addWallet(serverWallet); - halfCoin = valueOf(0, 50); - - broadcasts = new LinkedBlockingQueue(); - mockBroadcaster = new TransactionBroadcaster() { - @Override - public ListenableFuture broadcastTransaction(Transaction tx) { - SettableFuture future = SettableFuture.create(); - broadcasts.add(new TxFuturePair(tx, future)); - return future; - } - }; - } - - @After - @Override - public void tearDown() throws Exception { - super.tearDown(); - } - - @Test - public void stateErrors() throws Exception { - PaymentChannelClientState channelState = new PaymentChannelClientState(wallet, myKey, serverKey, - COIN.multiply(10), 20); - assertEquals(PaymentChannelClientState.State.NEW, channelState.getState()); - try { - channelState.getMultisigContract(); - fail(); - } catch (IllegalStateException e) { - // Expected. - } - try { - channelState.initiate(); - fail(); - } catch (InsufficientMoneyException e) { - } - } - - @Test - public void basic() throws Exception { - // Check it all works when things are normal (no attacks, no problems). - Utils.setMockClock(); // Use mock clock - final long EXPIRE_TIME = Utils.currentTimeSeconds() + 60*60*24; - - serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_REFUND_TRANSACTION, serverState.getState()); - - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), halfCoin, EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - clientState.initiate(); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Send the refund tx from client to server and get back the signature. - Transaction refund = new Transaction(params, clientState.getIncompleteRefundTransaction().bitcoinSerialize()); - byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey()); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState()); - // This verifies that the refund can spend the multi-sig output when run. - clientState.provideRefundSignature(refundSig, null); - assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState()); - clientState.fakeSave(); - assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState()); - - // Validate the multisig contract looks right. - Transaction multisigContract = new Transaction(params, clientState.getMultisigContract().bitcoinSerialize()); - assertEquals(PaymentChannelClientState.State.READY, clientState.getState()); - assertEquals(2, multisigContract.getOutputs().size()); // One multi-sig, one change. - Script script = multisigContract.getOutput(0).getScriptPubKey(); - assertTrue(script.isSentToMultiSig()); - script = multisigContract.getOutput(1).getScriptPubKey(); - assertTrue(script.isSentToAddress()); - assertTrue(wallet.getPendingTransactions().contains(multisigContract)); - - // Provide the server with the multisig contract and simulate successful propagation/acceptance. - serverState.provideMultiSigContract(multisigContract); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_ACCEPTANCE, serverState.getState()); - final TxFuturePair pair = broadcasts.take(); - pair.future.set(pair.tx); - assertEquals(PaymentChannelServerState.State.READY, serverState.getState()); - - // Make sure the refund transaction is not in the wallet and multisig contract's output is not connected to it - assertEquals(2, wallet.getTransactions(false).size()); - Iterator walletTransactionIterator = wallet.getTransactions(false).iterator(); - Transaction clientWalletMultisigContract = walletTransactionIterator.next(); - assertFalse(clientWalletMultisigContract.getHash().equals(clientState.getCompletedRefundTransaction().getHash())); - if (!clientWalletMultisigContract.getHash().equals(multisigContract.getHash())) { - clientWalletMultisigContract = walletTransactionIterator.next(); - assertFalse(clientWalletMultisigContract.getHash().equals(clientState.getCompletedRefundTransaction().getHash())); - } else - assertFalse(walletTransactionIterator.next().getHash().equals(clientState.getCompletedRefundTransaction().getHash())); - assertEquals(multisigContract.getHash(), clientWalletMultisigContract.getHash()); - assertFalse(clientWalletMultisigContract.getInput(0).getConnectedOutput().getSpentBy().getParentTransaction().getHash().equals(refund.getHash())); - - // Both client and server are now in the ready state. Simulate a few micropayments of 0.005 bitcoins. - Coin size = halfCoin.divide(100); - Coin totalPayment = Coin.ZERO; - for (int i = 0; i < 4; i++) { - byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(size); - serverState.incrementPayment(halfCoin.subtract(totalPayment), signature); - } - - // Now confirm the contract transaction and make sure payments still work - chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), multisigContract)); - - byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(size); - serverState.incrementPayment(halfCoin.subtract(totalPayment), signature); - - // And settle the channel. - serverState.close(); - assertEquals(PaymentChannelServerState.State.CLOSING, serverState.getState()); - final TxFuturePair pair2 = broadcasts.take(); - Transaction closeTx = pair2.tx; - pair2.future.set(closeTx); - final Transaction reserializedCloseTx = new Transaction(params, closeTx.bitcoinSerialize()); - assertEquals(PaymentChannelServerState.State.CLOSED, serverState.getState()); - // ... and on the client side. - wallet.receivePending(reserializedCloseTx, null); - assertEquals(PaymentChannelClientState.State.CLOSED, clientState.getState()); - - // Create a block with the payment transaction in it and give it to both wallets - chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), reserializedCloseTx)); - - assertEquals(size.multiply(5), serverWallet.getBalance()); - assertEquals(0, serverWallet.getPendingTransactions().size()); - - assertEquals(COIN.subtract(size.multiply(5)), wallet.getBalance()); - assertEquals(0, wallet.getPendingTransactions().size()); - assertEquals(3, wallet.getTransactions(false).size()); - - walletTransactionIterator = wallet.getTransactions(false).iterator(); - Transaction clientWalletCloseTransaction = walletTransactionIterator.next(); - if (!clientWalletCloseTransaction.getHash().equals(closeTx.getHash())) - clientWalletCloseTransaction = walletTransactionIterator.next(); - if (!clientWalletCloseTransaction.getHash().equals(closeTx.getHash())) - clientWalletCloseTransaction = walletTransactionIterator.next(); - assertEquals(closeTx.getHash(), clientWalletCloseTransaction.getHash()); - assertNotNull(clientWalletCloseTransaction.getInput(0).getConnectedOutput()); - } - - @Test - public void setupDoS() throws Exception { - // Check that if the other side stops after we have provided a signed multisig contract, that after a timeout - // we can broadcast the refund and get our balance back. - - // Spend the client wallet's one coin - Transaction spendCoinTx = wallet.sendCoinsOffline(Wallet.SendRequest.to(new ECKey().toAddress(params), COIN)); - assertEquals(Coin.ZERO, wallet.getBalance()); - chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), spendCoinTx, createFakeTx(params, CENT, myAddress))); - assertEquals(CENT, wallet.getBalance()); - - // Set the wallet's stored states to use our real test PeerGroup - StoredPaymentChannelClientStates stateStorage = new StoredPaymentChannelClientStates(wallet, mockBroadcaster); - wallet.addOrUpdateExtension(stateStorage); - - Utils.setMockClock(); // Use mock clock - final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24; - - serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_REFUND_TRANSACTION, serverState.getState()); - - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), - CENT.divide(2), EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - assertEquals(CENT.divide(2), clientState.getTotalValue()); - clientState.initiate(); - // We will have to pay min_tx_fee twice - both the multisig contract and the refund tx - assertEquals(clientState.getRefundTxFees(), Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(2)); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Send the refund tx from client to server and get back the signature. - Transaction refund = new Transaction(params, clientState.getIncompleteRefundTransaction().bitcoinSerialize()); - byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey()); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState()); - // This verifies that the refund can spend the multi-sig output when run. - clientState.provideRefundSignature(refundSig, null); - assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState()); - clientState.fakeSave(); - assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState()); - - // Validate the multisig contract looks right. - Transaction multisigContract = new Transaction(params, clientState.getMultisigContract().bitcoinSerialize()); - assertEquals(PaymentChannelClientState.State.READY, clientState.getState()); - assertEquals(2, multisigContract.getOutputs().size()); // One multi-sig, one change. - Script script = multisigContract.getOutput(0).getScriptPubKey(); - assertTrue(script.isSentToMultiSig()); - script = multisigContract.getOutput(1).getScriptPubKey(); - assertTrue(script.isSentToAddress()); - assertTrue(wallet.getPendingTransactions().contains(multisigContract)); - - // Provide the server with the multisig contract and simulate successful propagation/acceptance. - serverState.provideMultiSigContract(multisigContract); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_ACCEPTANCE, serverState.getState()); - final TxFuturePair pop = broadcasts.take(); - pop.future.set(pop.tx); - assertEquals(PaymentChannelServerState.State.READY, serverState.getState()); - - // Pay a tiny bit - serverState.incrementPayment(CENT.divide(2).subtract(CENT.divide(10)), - clientState.incrementPaymentBy(CENT.divide(10), null).signature.encodeToBitcoin()); - - // Advance time until our we get close enough to lock time that server should rebroadcast - Utils.rollMockClock(60*60*22); - // ... and store server to get it to broadcast payment transaction - serverState.storeChannelInWallet(null); - TxFuturePair broadcastPaymentPair = broadcasts.take(); - Exception paymentException = new RuntimeException("I'm sorry, but the network really just doesn't like you"); - broadcastPaymentPair.future.setException(paymentException); - try { - serverState.close().get(); - } catch (ExecutionException e) { - assertSame(e.getCause(), paymentException); - } - assertEquals(PaymentChannelServerState.State.ERROR, serverState.getState()); - - // Now advance until client should rebroadcast - Utils.rollMockClock(60 * 60 * 2 + 60 * 5); - - // Now store the client state in a stored state object which handles the rebroadcasting - clientState.doStoreChannelInWallet(Sha256Hash.create(new byte[]{})); - TxFuturePair clientBroadcastedMultiSig = broadcasts.take(); - TxFuturePair broadcastRefund = broadcasts.take(); - assertEquals(clientBroadcastedMultiSig.tx.getHash(), multisigContract.getHash()); - for (TransactionInput input : clientBroadcastedMultiSig.tx.getInputs()) - input.verify(); - clientBroadcastedMultiSig.future.set(clientBroadcastedMultiSig.tx); - - Transaction clientBroadcastedRefund = broadcastRefund.tx; - assertEquals(clientBroadcastedRefund.getHash(), clientState.getCompletedRefundTransaction().getHash()); - for (TransactionInput input : clientBroadcastedRefund.getInputs()) { - // If the multisig output is connected, the wallet will fail to deserialize - if (input.getOutpoint().getHash().equals(clientBroadcastedMultiSig.tx.getHash())) - assertNull(input.getConnectedOutput().getSpentBy()); - input.verify(clientBroadcastedMultiSig.tx.getOutput(0)); - } - broadcastRefund.future.set(clientBroadcastedRefund); - - // Create a block with multisig contract and refund transaction in it and give it to both wallets, - // making getBalance() include the transactions - chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), multisigContract,clientBroadcastedRefund)); - - // Make sure we actually had to pay what initialize() told us we would - assertEquals(wallet.getBalance(), CENT.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(2))); - - try { - // After its expired, we cant still increment payment - clientState.incrementPaymentBy(CENT, null); - fail(); - } catch (IllegalStateException e) { } - } - - @Test - public void checkBadData() throws Exception { - // Check that if signatures/transactions/etc are corrupted, the protocol rejects them correctly. - - // We'll broadcast only one tx: multisig contract - Utils.setMockClock(); // Use mock clock - final long EXPIRE_TIME = Utils.currentTimeSeconds() + 60*60*24; - - serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_REFUND_TRANSACTION, serverState.getState()); - - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), halfCoin, EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - clientState.initiate(); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Test refund transaction with any number of issues - byte[] refundTxBytes = clientState.getIncompleteRefundTransaction().bitcoinSerialize(); - Transaction refund = new Transaction(params, refundTxBytes); - refund.addOutput(Coin.ZERO, new ECKey().toAddress(params)); - try { - serverState.provideRefundTransaction(refund, myKey.getPubKey()); - fail(); - } catch (VerificationException e) {} - - refund = new Transaction(params, refundTxBytes); - refund.addInput(new TransactionInput(params, refund, new byte[] {}, new TransactionOutPoint(params, 42, refund.getHash()))); - try { - serverState.provideRefundTransaction(refund, myKey.getPubKey()); - fail(); - } catch (VerificationException e) {} - - refund = new Transaction(params, refundTxBytes); - refund.setLockTime(0); - try { - serverState.provideRefundTransaction(refund, myKey.getPubKey()); - fail(); - } catch (VerificationException e) {} - - refund = new Transaction(params, refundTxBytes); - refund.getInput(0).setSequenceNumber(TransactionInput.NO_SEQUENCE); - try { - serverState.provideRefundTransaction(refund, myKey.getPubKey()); - fail(); - } catch (VerificationException e) {} - - refund = new Transaction(params, refundTxBytes); - byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey()); - try { serverState.provideRefundTransaction(refund, myKey.getPubKey()); fail(); } catch (IllegalStateException e) {} - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState()); - - byte[] refundSigCopy = Arrays.copyOf(refundSig, refundSig.length); - refundSigCopy[refundSigCopy.length-1] = (byte) (Transaction.SigHash.NONE.ordinal() + 1); - try { - clientState.provideRefundSignature(refundSigCopy, null); - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage().contains("SIGHASH_NONE")); - } - - refundSigCopy = Arrays.copyOf(refundSig, refundSig.length); - refundSigCopy[3] ^= 0x42; // Make the signature fail standard checks - try { - clientState.provideRefundSignature(refundSigCopy, null); - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage().contains("not canonical")); - } - - refundSigCopy = Arrays.copyOf(refundSig, refundSig.length); - refundSigCopy[10] ^= 0x42; // Flip some random bits in the signature (to make it invalid, not just nonstandard) - try { - clientState.provideRefundSignature(refundSigCopy, null); - fail(); - } catch (VerificationException e) { - assertFalse(e.getMessage().contains("not canonical")); - } - - refundSigCopy = Arrays.copyOf(refundSig, refundSig.length); - try { clientState.getCompletedRefundTransaction(); fail(); } catch (IllegalStateException e) {} - clientState.provideRefundSignature(refundSigCopy, null); - try { clientState.provideRefundSignature(refundSigCopy, null); fail(); } catch (IllegalStateException e) {} - assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState()); - clientState.fakeSave(); - assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState()); - - try { clientState.incrementPaymentBy(Coin.SATOSHI, null); fail(); } catch (IllegalStateException e) {} - - byte[] multisigContractSerialized = clientState.getMultisigContract().bitcoinSerialize(); - - Transaction multisigContract = new Transaction(params, multisigContractSerialized); - multisigContract.clearOutputs(); - multisigContract.addOutput(halfCoin, ScriptBuilder.createMultiSigOutputScript(2, Lists.newArrayList(serverKey, myKey))); - try { - serverState.provideMultiSigContract(multisigContract); - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage().contains("client and server in that order")); - } - - multisigContract = new Transaction(params, multisigContractSerialized); - multisigContract.clearOutputs(); - multisigContract.addOutput(Coin.ZERO, ScriptBuilder.createMultiSigOutputScript(2, Lists.newArrayList(myKey, serverKey))); - try { - serverState.provideMultiSigContract(multisigContract); - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage().contains("zero value")); - } - - multisigContract = new Transaction(params, multisigContractSerialized); - multisigContract.clearOutputs(); - multisigContract.addOutput(new TransactionOutput(params, multisigContract, halfCoin, new byte[] {0x01})); - try { - serverState.provideMultiSigContract(multisigContract); - fail(); - } catch (VerificationException e) {} - - multisigContract = new Transaction(params, multisigContractSerialized); - ListenableFuture multisigStateFuture = serverState.provideMultiSigContract(multisigContract); - try { serverState.provideMultiSigContract(multisigContract); fail(); } catch (IllegalStateException e) {} - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_ACCEPTANCE, serverState.getState()); - assertFalse(multisigStateFuture.isDone()); - final TxFuturePair pair = broadcasts.take(); - pair.future.set(pair.tx); - assertEquals(multisigStateFuture.get(), serverState); - assertEquals(PaymentChannelServerState.State.READY, serverState.getState()); - - // Both client and server are now in the ready state. Simulate a few micropayments of 0.005 bitcoins. - Coin size = halfCoin.divide(100); - Coin totalPayment = Coin.ZERO; - try { - clientState.incrementPaymentBy(COIN, null); - fail(); - } catch (ValueOutOfRangeException e) {} - - byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(size); - - byte[] signatureCopy = Arrays.copyOf(signature, signature.length); - signatureCopy[signatureCopy.length - 1] = (byte) ((Transaction.SigHash.NONE.ordinal() + 1) | 0x80); - try { - serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy); - fail(); - } catch (VerificationException e) {} - - signatureCopy = Arrays.copyOf(signature, signature.length); - signatureCopy[2] ^= 0x42; // Make the signature fail standard checks - try { - serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy); - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage().contains("not canonical")); - } - - signatureCopy = Arrays.copyOf(signature, signature.length); - signatureCopy[10] ^= 0x42; // Flip some random bits in the signature (to make it invalid, not just nonstandard) - try { - serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy); - fail(); - } catch (VerificationException e) { - assertFalse(e.getMessage().contains("not canonical")); - } - - serverState.incrementPayment(halfCoin.subtract(totalPayment), signature); - - // Pay the rest (signed with SIGHASH_NONE|SIGHASH_ANYONECANPAY) - byte[] signature2 = clientState.incrementPaymentBy(halfCoin.subtract(totalPayment), null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(halfCoin.subtract(totalPayment)); - assertEquals(totalPayment, halfCoin); - - signatureCopy = Arrays.copyOf(signature, signature.length); - signatureCopy[signatureCopy.length - 1] = (byte) ((Transaction.SigHash.SINGLE.ordinal() + 1) | 0x80); - try { - serverState.incrementPayment(halfCoin.subtract(totalPayment), signatureCopy); - fail(); - } catch (VerificationException e) {} - - serverState.incrementPayment(halfCoin.subtract(totalPayment), signature2); - - // Trying to take reduce the refund size fails. - try { - serverState.incrementPayment(halfCoin.subtract(totalPayment.subtract(size)), signature); - fail(); - } catch (ValueOutOfRangeException e) {} - assertEquals(serverState.getBestValueToMe(), totalPayment); - - try { - clientState.incrementPaymentBy(Coin.SATOSHI.negate(), null); - fail(); - } catch (ValueOutOfRangeException e) {} - - try { - clientState.incrementPaymentBy(halfCoin.subtract(size).add(Coin.SATOSHI), null); - fail(); - } catch (ValueOutOfRangeException e) {} - } - - @Test - public void feesTest() throws Exception { - // Test that transactions are getting the necessary fees - - // Spend the client wallet's one coin - wallet.sendCoinsOffline(Wallet.SendRequest.to(new ECKey().toAddress(params), COIN)); - assertEquals(Coin.ZERO, wallet.getBalance()); - - chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), createFakeTx(params, CENT, myAddress))); - assertEquals(CENT, wallet.getBalance()); - - Utils.setMockClock(); // Use mock clock - final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24; - - serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_REFUND_TRANSACTION, serverState.getState()); - - // Clearly SATOSHI is far too small to be useful - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), Coin.SATOSHI, EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - try { - clientState.initiate(); - fail(); - } catch (ValueOutOfRangeException e) {} - - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), - Transaction.MIN_NONDUST_OUTPUT.subtract(Coin.SATOSHI).add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), - EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - try { - clientState.initiate(); - fail(); - } catch (ValueOutOfRangeException e) {} - - // Verify that MIN_NONDUST_OUTPUT + MIN_TX_FEE is accepted - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), - Transaction.MIN_NONDUST_OUTPUT.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - // We'll have to pay REFERENCE_DEFAULT_MIN_TX_FEE twice (multisig+refund), and we'll end up getting back nearly nothing... - clientState.initiate(); - assertEquals(clientState.getRefundTxFees(), Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(2)); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Now actually use a more useful CENT - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - clientState.initiate(); - assertEquals(clientState.getRefundTxFees(), Coin.ZERO); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Send the refund tx from client to server and get back the signature. - Transaction refund = new Transaction(params, clientState.getIncompleteRefundTransaction().bitcoinSerialize()); - byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey()); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState()); - // This verifies that the refund can spend the multi-sig output when run. - clientState.provideRefundSignature(refundSig, null); - assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState()); - clientState.fakeSave(); - assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState()); - - // Get the multisig contract - Transaction multisigContract = new Transaction(params, clientState.getMultisigContract().bitcoinSerialize()); - assertEquals(PaymentChannelClientState.State.READY, clientState.getState()); - - // Provide the server with the multisig contract and simulate successful propagation/acceptance. - serverState.provideMultiSigContract(multisigContract); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_ACCEPTANCE, serverState.getState()); - TxFuturePair pair = broadcasts.take(); - pair.future.set(pair.tx); - assertEquals(PaymentChannelServerState.State.READY, serverState.getState()); - - // Both client and server are now in the ready state. Simulate a few micropayments - Coin totalPayment = Coin.ZERO; - - // We can send as little as we want - its up to the server to get the fees right - byte[] signature = clientState.incrementPaymentBy(Coin.SATOSHI, null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(Coin.SATOSHI); - serverState.incrementPayment(CENT.subtract(totalPayment), signature); - - // We can't refund more than the contract is worth... - try { - serverState.incrementPayment(CENT.add(SATOSHI), signature); - fail(); - } catch (ValueOutOfRangeException e) {} - - // We cannot send just under the total value - our refund would make it unspendable. So the client - // will correct it for us to be larger than the requested amount, to make the change output zero. - PaymentChannelClientState.IncrementedPayment payment = - clientState.incrementPaymentBy(CENT.subtract(Transaction.MIN_NONDUST_OUTPUT), null); - assertEquals(CENT.subtract(SATOSHI), payment.amount); - totalPayment = totalPayment.add(payment.amount); - - // The server also won't accept it if we do that. - try { - serverState.incrementPayment(Transaction.MIN_NONDUST_OUTPUT.subtract(Coin.SATOSHI), signature); - fail(); - } catch (ValueOutOfRangeException e) {} - - serverState.incrementPayment(CENT.subtract(totalPayment), payment.signature.encodeToBitcoin()); - - // And settle the channel. - serverState.close(); - assertEquals(PaymentChannelServerState.State.CLOSING, serverState.getState()); - pair = broadcasts.take(); // settle - pair.future.set(pair.tx); - assertEquals(PaymentChannelServerState.State.CLOSED, serverState.getState()); - serverState.close(); - assertEquals(PaymentChannelServerState.State.CLOSED, serverState.getState()); - } - - @Test - public void serverAddsFeeTest() throws Exception { - // Test that the server properly adds the necessary fee at the end (or just drops the payment if its not worth it) - - Utils.setMockClock(); // Use mock clock - final long EXPIRE_TIME = Utils.currentTimeMillis()/1000 + 60*60*24; - - serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_REFUND_TRANSACTION, serverState.getState()); - - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME) { - @Override - protected void editContractSendRequest(Wallet.SendRequest req) { - req.coinSelector = wallet.getCoinSelector(); - } - }; - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - clientState.initiate(); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Send the refund tx from client to server and get back the signature. - Transaction refund = new Transaction(params, clientState.getIncompleteRefundTransaction().bitcoinSerialize()); - byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey()); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState()); - // This verifies that the refund can spend the multi-sig output when run. - clientState.provideRefundSignature(refundSig, null); - assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState()); - clientState.fakeSave(); - assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState()); - - // Validate the multisig contract looks right. - Transaction multisigContract = new Transaction(params, clientState.getMultisigContract().bitcoinSerialize()); - assertEquals(PaymentChannelClientState.State.READY, clientState.getState()); - assertEquals(2, multisigContract.getOutputs().size()); // One multi-sig, one change. - Script script = multisigContract.getOutput(0).getScriptPubKey(); - assertTrue(script.isSentToMultiSig()); - script = multisigContract.getOutput(1).getScriptPubKey(); - assertTrue(script.isSentToAddress()); - assertTrue(wallet.getPendingTransactions().contains(multisigContract)); - - // Provide the server with the multisig contract and simulate successful propagation/acceptance. - serverState.provideMultiSigContract(multisigContract); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_ACCEPTANCE, serverState.getState()); - TxFuturePair pair = broadcasts.take(); - pair.future.set(pair.tx); - assertEquals(PaymentChannelServerState.State.READY, serverState.getState()); - - // Both client and server are now in the ready state, split the channel in half - byte[] signature = clientState.incrementPaymentBy(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(Coin.SATOSHI), null) - .signature.encodeToBitcoin(); - Coin totalRefund = CENT.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(SATOSHI)); - serverState.incrementPayment(totalRefund, signature); - - // We need to pay MIN_TX_FEE, but we only have MIN_NONDUST_OUTPUT - try { - serverState.close(); - fail(); - } catch (InsufficientMoneyException e) { - } - - // Now give the server enough coins to pay the fee - StoredBlock block = new StoredBlock(makeSolvedTestBlock(blockStore, new ECKey().toAddress(params)), BigInteger.ONE, 1); - Transaction tx1 = createFakeTx(params, COIN, serverKey.toAddress(params)); - serverWallet.receiveFromBlock(tx1, block, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0); - - // The contract is still not worth redeeming - its worth less than we pay in fee - try { - serverState.close(); - fail(); - } catch (InsufficientMoneyException e) { - assertTrue(e.getMessage().contains("more in fees")); - } - - signature = clientState.incrementPaymentBy(SATOSHI.multiply(2), null).signature.encodeToBitcoin(); - totalRefund = totalRefund.subtract(SATOSHI.multiply(2)); - serverState.incrementPayment(totalRefund, signature); - - // And settle the channel. - serverState.close(); - assertEquals(PaymentChannelServerState.State.CLOSING, serverState.getState()); - pair = broadcasts.take(); - pair.future.set(pair.tx); - assertEquals(PaymentChannelServerState.State.CLOSED, serverState.getState()); - } - - @Test - public void doubleSpendContractTest() throws Exception { - // Tests that if the client double-spends the multisig contract after it is sent, no more payments are accepted - - // Start with a copy of basic().... - Utils.setMockClock(); // Use mock clock - final long EXPIRE_TIME = Utils.currentTimeSeconds() + 60*60*24; - - serverState = new PaymentChannelServerState(mockBroadcaster, serverWallet, serverKey, EXPIRE_TIME); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_REFUND_TRANSACTION, serverState.getState()); - - clientState = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), halfCoin, EXPIRE_TIME); - assertEquals(PaymentChannelClientState.State.NEW, clientState.getState()); - clientState.initiate(); - assertEquals(PaymentChannelClientState.State.INITIATED, clientState.getState()); - - // Send the refund tx from client to server and get back the signature. - Transaction refund = new Transaction(params, clientState.getIncompleteRefundTransaction().bitcoinSerialize()); - byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey()); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState()); - // This verifies that the refund can spend the multi-sig output when run. - clientState.provideRefundSignature(refundSig, null); - assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState()); - clientState.fakeSave(); - assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState()); - - // Validate the multisig contract looks right. - Transaction multisigContract = new Transaction(params, clientState.getMultisigContract().bitcoinSerialize()); - assertEquals(PaymentChannelClientState.State.READY, clientState.getState()); - assertEquals(2, multisigContract.getOutputs().size()); // One multi-sig, one change. - Script script = multisigContract.getOutput(0).getScriptPubKey(); - assertTrue(script.isSentToMultiSig()); - script = multisigContract.getOutput(1).getScriptPubKey(); - assertTrue(script.isSentToAddress()); - assertTrue(wallet.getPendingTransactions().contains(multisigContract)); - - // Provide the server with the multisig contract and simulate successful propagation/acceptance. - serverState.provideMultiSigContract(multisigContract); - assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_ACCEPTANCE, serverState.getState()); - final TxFuturePair pair = broadcasts.take(); - pair.future.set(pair.tx); - assertEquals(PaymentChannelServerState.State.READY, serverState.getState()); - - // Make sure the refund transaction is not in the wallet and multisig contract's output is not connected to it - assertEquals(2, wallet.getTransactions(false).size()); - Iterator walletTransactionIterator = wallet.getTransactions(false).iterator(); - Transaction clientWalletMultisigContract = walletTransactionIterator.next(); - assertFalse(clientWalletMultisigContract.getHash().equals(clientState.getCompletedRefundTransaction().getHash())); - if (!clientWalletMultisigContract.getHash().equals(multisigContract.getHash())) { - clientWalletMultisigContract = walletTransactionIterator.next(); - assertFalse(clientWalletMultisigContract.getHash().equals(clientState.getCompletedRefundTransaction().getHash())); - } else - assertFalse(walletTransactionIterator.next().getHash().equals(clientState.getCompletedRefundTransaction().getHash())); - assertEquals(multisigContract.getHash(), clientWalletMultisigContract.getHash()); - assertFalse(clientWalletMultisigContract.getInput(0).getConnectedOutput().getSpentBy().getParentTransaction().getHash().equals(refund.getHash())); - - // Both client and server are now in the ready state. Simulate a few micropayments of 0.005 bitcoins. - Coin size = halfCoin.divide(100); - Coin totalPayment = Coin.ZERO; - for (int i = 0; i < 5; i++) { - byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(size); - serverState.incrementPayment(halfCoin.subtract(totalPayment), signature); - } - - // Now create a double-spend and send it to the server - Transaction doubleSpendContract = new Transaction(params); - doubleSpendContract.addInput(new TransactionInput(params, doubleSpendContract, new byte[0], - multisigContract.getInput(0).getOutpoint())); - doubleSpendContract.addOutput(halfCoin, myKey); - doubleSpendContract = new Transaction(params, doubleSpendContract.bitcoinSerialize()); - - StoredBlock block = new StoredBlock(params.getGenesisBlock().createNextBlock(myKey.toAddress(params)), BigInteger.TEN, 1); - serverWallet.receiveFromBlock(doubleSpendContract, block, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0); - - // Now if we try to spend again the server will reject it since it saw a double-spend - try { - byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin(); - totalPayment = totalPayment.add(size); - serverState.incrementPayment(halfCoin.subtract(totalPayment), signature); - fail(); - } catch (VerificationException e) { - assertTrue(e.getMessage().contains("double-spent")); - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentProtocolTest.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentProtocolTest.java deleted file mode 100644 index 057750b5..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentProtocolTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.protocols.payments; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.crypto.X509Utils; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.protocols.payments.PaymentProtocol.Output; -import com.dogecoin.dogecoinj.protocols.payments.PaymentProtocol.PkiVerificationData; -import com.dogecoin.dogecoinj.protocols.payments.PaymentProtocolException.PkiVerificationException; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import org.bitcoin.protocols.payments.Protos; -import org.bitcoin.protocols.payments.Protos.Payment; -import org.bitcoin.protocols.payments.Protos.PaymentACK; -import org.bitcoin.protocols.payments.Protos.PaymentRequest; -import org.junit.Before; -import org.junit.Test; - -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.LinkedList; -import java.util.List; - -import static org.junit.Assert.*; - -public class PaymentProtocolTest { - - // static test data - private static final NetworkParameters NETWORK_PARAMS = UnitTestParams.get(); - private static final Coin AMOUNT = Coin.SATOSHI; - private static final Address TO_ADDRESS = new ECKey().toAddress(NETWORK_PARAMS); - private static final String MEMO = "memo"; - private static final String PAYMENT_URL = "https://example.com"; - private static final byte[] MERCHANT_DATA = new byte[] { 0, 1, 2 }; - - private KeyStore caStore; - private X509Certificate caCert; - - @Before - public void setUp() throws Exception { - caStore = X509Utils.loadKeyStore("JKS", "password", getClass().getResourceAsStream("test-cacerts")); - caCert = (X509Certificate) caStore.getCertificate("test-cacert"); - } - - @Test - public void testSignAndVerifyValid() throws Exception { - Protos.PaymentRequest.Builder paymentRequest = minimalPaymentRequest().toBuilder(); - - // Sign - KeyStore keyStore = X509Utils - .loadKeyStore("JKS", "password", getClass().getResourceAsStream("test-valid-cert")); - PrivateKey privateKey = (PrivateKey) keyStore.getKey("test-valid", "password".toCharArray()); - X509Certificate clientCert = (X509Certificate) keyStore.getCertificate("test-valid"); - PaymentProtocol.signPaymentRequest(paymentRequest, new X509Certificate[]{clientCert}, privateKey); - - // Verify - PkiVerificationData verificationData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest.build(), caStore); - assertNotNull(verificationData); - assertEquals(caCert, verificationData.rootAuthority.getTrustedCert()); - } - - @Test(expected = PkiVerificationException.class) - public void testSignAndVerifyExpired() throws Exception { - Protos.PaymentRequest.Builder paymentRequest = minimalPaymentRequest().toBuilder(); - - // Sign - KeyStore keyStore = X509Utils.loadKeyStore("JKS", "password", - getClass().getResourceAsStream("test-expired-cert")); - PrivateKey privateKey = (PrivateKey) keyStore.getKey("test-expired", "password".toCharArray()); - X509Certificate clientCert = (X509Certificate) keyStore.getCertificate("test-expired"); - PaymentProtocol.signPaymentRequest(paymentRequest, new X509Certificate[]{clientCert}, privateKey); - - // Verify - PaymentProtocol.verifyPaymentRequestPki(paymentRequest.build(), caStore); - } - - private Protos.PaymentRequest minimalPaymentRequest() { - Protos.PaymentDetails.Builder paymentDetails = Protos.PaymentDetails.newBuilder(); - paymentDetails.setTime(System.currentTimeMillis()); - Protos.PaymentRequest.Builder paymentRequest = Protos.PaymentRequest.newBuilder(); - paymentRequest.setSerializedPaymentDetails(paymentDetails.build().toByteString()); - return paymentRequest.build(); - } - - @Test - public void testPaymentRequest() throws Exception { - // Create - PaymentRequest paymentRequest = PaymentProtocol.createPaymentRequest(TestNet3Params.get(), AMOUNT, TO_ADDRESS, MEMO, - PAYMENT_URL, MERCHANT_DATA).build(); - byte[] paymentRequestBytes = paymentRequest.toByteArray(); - - // Parse - PaymentSession parsedPaymentRequest = PaymentProtocol.parsePaymentRequest(PaymentRequest - .parseFrom(paymentRequestBytes)); - final List parsedOutputs = parsedPaymentRequest.getOutputs(); - assertEquals(1, parsedOutputs.size()); - assertEquals(AMOUNT, parsedOutputs.get(0).amount); - assertArrayEquals(ScriptBuilder.createOutputScript(TO_ADDRESS).getProgram(), parsedOutputs.get(0).scriptData); - assertEquals(MEMO, parsedPaymentRequest.getMemo()); - assertEquals(PAYMENT_URL, parsedPaymentRequest.getPaymentUrl()); - assertArrayEquals(MERCHANT_DATA, parsedPaymentRequest.getMerchantData()); - } - - @Test - public void testPaymentMessage() throws Exception { - // Create - List transactions = new LinkedList(); - transactions.add(FakeTxBuilder.createFakeTx(NETWORK_PARAMS, AMOUNT, TO_ADDRESS)); - Coin refundAmount = Coin.SATOSHI; - Address refundAddress = new ECKey().toAddress(NETWORK_PARAMS); - Payment payment = PaymentProtocol.createPaymentMessage(transactions, refundAmount, refundAddress, MEMO, - MERCHANT_DATA); - byte[] paymentBytes = payment.toByteArray(); - - // Parse - Payment parsedPayment = Payment.parseFrom(paymentBytes); - List parsedTransactions = PaymentProtocol.parseTransactionsFromPaymentMessage(NETWORK_PARAMS, - parsedPayment); - assertEquals(transactions, parsedTransactions); - assertEquals(1, parsedPayment.getRefundToCount()); - assertEquals(MEMO, parsedPayment.getMemo()); - assertArrayEquals(MERCHANT_DATA, parsedPayment.getMerchantData().toByteArray()); - } - - @Test - public void testPaymentAck() throws Exception { - // Create - Payment paymentMessage = Protos.Payment.newBuilder().build(); - PaymentACK paymentAck = PaymentProtocol.createPaymentAck(paymentMessage, MEMO); - byte[] paymentAckBytes = paymentAck.toByteArray(); - - // Parse - PaymentACK parsedPaymentAck = PaymentACK.parseFrom(paymentAckBytes); - assertEquals(paymentMessage, parsedPaymentAck.getPayment()); - assertEquals(MEMO, parsedPaymentAck.getMemo()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentSessionTest.java b/core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentSessionTest.java deleted file mode 100644 index fb580326..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/protocols/payments/PaymentSessionTest.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright 2013 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.protocols.payments; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.crypto.TrustStoreLoader; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.protobuf.ByteString; -import org.bitcoin.protocols.payments.Protos; -import org.junit.Before; -import org.junit.Test; - -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Date; - -import static com.dogecoin.dogecoinj.core.Coin.COIN; -import static org.junit.Assert.*; - -public class PaymentSessionTest { - private static final NetworkParameters params = TestNet3Params.get(); - private static final String simplePaymentUrl = "http://a.simple.url.com/"; - private static final String paymentRequestMemo = "send coinz noa plz kthx"; - private static final String paymentMemo = "take ze coinz"; - private static final ByteString merchantData = ByteString.copyFromUtf8("merchant data"); - private static final long time = System.currentTimeMillis() / 1000L; - private ECKey serverKey; - private Transaction tx; - private TransactionOutput outputToMe; - private Coin coin = COIN; - - @Before - public void setUp() throws Exception { - serverKey = new ECKey(); - tx = new Transaction(params); - outputToMe = new TransactionOutput(params, tx, coin, serverKey); - tx.addOutput(outputToMe); - } - - @Test - public void testSimplePayment() throws Exception { - // Create a PaymentRequest and make sure the correct values are parsed by the PaymentSession. - MockPaymentSession paymentSession = new MockPaymentSession(newSimplePaymentRequest("test")); - assertEquals(paymentRequestMemo, paymentSession.getMemo()); - assertEquals(coin, paymentSession.getValue()); - assertEquals(simplePaymentUrl, paymentSession.getPaymentUrl()); - assertTrue(new Date(time * 1000L).equals(paymentSession.getDate())); - assertTrue(paymentSession.getSendRequest().tx.equals(tx)); - assertFalse(paymentSession.isExpired()); - - // Send the payment and verify that the correct information is sent. - // Add a dummy input to tx so it is considered valid. - tx.addInput(new TransactionInput(params, tx, outputToMe.getScriptBytes())); - ArrayList txns = new ArrayList(); - txns.add(tx); - Address refundAddr = new Address(params, serverKey.getPubKeyHash()); - paymentSession.sendPayment(txns, refundAddr, paymentMemo); - assertEquals(1, paymentSession.getPaymentLog().size()); - assertEquals(simplePaymentUrl, paymentSession.getPaymentLog().get(0).getUrl().toString()); - Protos.Payment payment = paymentSession.getPaymentLog().get(0).getPayment(); - assertEquals(paymentMemo, payment.getMemo()); - assertEquals(merchantData, payment.getMerchantData()); - assertEquals(1, payment.getRefundToCount()); - assertEquals(coin.value, payment.getRefundTo(0).getAmount()); - TransactionOutput refundOutput = new TransactionOutput(params, null, coin, refundAddr); - ByteString refundScript = ByteString.copyFrom(refundOutput.getScriptBytes()); - assertTrue(refundScript.equals(payment.getRefundTo(0).getScript())); - } - - @Test - public void testDefaults() throws Exception { - Protos.Output.Builder outputBuilder = Protos.Output.newBuilder() - .setScript(ByteString.copyFrom(outputToMe.getScriptBytes())); - Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder() - .setTime(time) - .addOutputs(outputBuilder) - .build(); - Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.newBuilder() - .setSerializedPaymentDetails(paymentDetails.toByteString()) - .build(); - MockPaymentSession paymentSession = new MockPaymentSession(paymentRequest); - assertEquals(Coin.ZERO, paymentSession.getValue()); - assertNull(paymentSession.getPaymentUrl()); - assertNull(paymentSession.getMemo()); - } - - @Test - public void testExpiredPaymentRequest() throws Exception { - MockPaymentSession paymentSession = new MockPaymentSession(newExpiredPaymentRequest()); - assertTrue(paymentSession.isExpired()); - // Send the payment and verify that an exception is thrown. - // Add a dummy input to tx so it is considered valid. - tx.addInput(new TransactionInput(params, tx, outputToMe.getScriptBytes())); - ArrayList txns = new ArrayList(); - txns.add(tx); - try { - paymentSession.sendPayment(txns, null, null); - } catch(PaymentProtocolException.Expired e) { - assertEquals(0, paymentSession.getPaymentLog().size()); - assertEquals(e.getMessage(), "PaymentRequest is expired"); - return; - } - fail("Expected exception due to expired PaymentRequest"); - } - - @Test - public void testPkiVerification() throws Exception { - InputStream in = getClass().getResourceAsStream("pki_test.bitcoinpaymentrequest"); - Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.newBuilder().mergeFrom(in).build(); - PaymentProtocol.PkiVerificationData pkiData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest, - new TrustStoreLoader.DefaultTrustStoreLoader().getKeyStore()); - assertEquals("www.bitcoincore.org", pkiData.displayName); - assertEquals("The USERTRUST Network, Salt Lake City, US", pkiData.rootAuthorityName); - } - - @Test(expected = PaymentProtocolException.InvalidNetwork.class) - public void testWrongNetwork() throws Exception { - // Create a PaymentRequest and make sure the correct values are parsed by the PaymentSession. - MockPaymentSession paymentSession = new MockPaymentSession(newSimplePaymentRequest("main")); - assertEquals(MainNetParams.get(), paymentSession.getNetworkParameters()); - - // Send the payment and verify that the correct information is sent. - // Add a dummy input to tx so it is considered valid. - tx.addInput(new TransactionInput(params, tx, outputToMe.getScriptBytes())); - ArrayList txns = new ArrayList(); - txns.add(tx); - Address refundAddr = new Address(params, serverKey.getPubKeyHash()); - paymentSession.sendPayment(txns, refundAddr, paymentMemo); - assertEquals(1, paymentSession.getPaymentLog().size()); - } - - private Protos.PaymentRequest newSimplePaymentRequest(String netID) { - Protos.Output.Builder outputBuilder = Protos.Output.newBuilder() - .setAmount(coin.value) - .setScript(ByteString.copyFrom(outputToMe.getScriptBytes())); - Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder() - .setNetwork(netID) - .setTime(time) - .setPaymentUrl(simplePaymentUrl) - .addOutputs(outputBuilder) - .setMemo(paymentRequestMemo) - .setMerchantData(merchantData) - .build(); - return Protos.PaymentRequest.newBuilder() - .setPaymentDetailsVersion(1) - .setPkiType("none") - .setSerializedPaymentDetails(paymentDetails.toByteString()) - .build(); - } - - private Protos.PaymentRequest newExpiredPaymentRequest() { - Protos.Output.Builder outputBuilder = Protos.Output.newBuilder() - .setAmount(coin.value) - .setScript(ByteString.copyFrom(outputToMe.getScriptBytes())); - Protos.PaymentDetails paymentDetails = Protos.PaymentDetails.newBuilder() - .setNetwork("test") - .setTime(time - 10) - .setExpires(time - 1) - .setPaymentUrl(simplePaymentUrl) - .addOutputs(outputBuilder) - .setMemo(paymentRequestMemo) - .setMerchantData(merchantData) - .build(); - return Protos.PaymentRequest.newBuilder() - .setPaymentDetailsVersion(1) - .setPkiType("none") - .setSerializedPaymentDetails(paymentDetails.toByteString()) - .build(); - } - - private class MockPaymentSession extends PaymentSession { - private ArrayList paymentLog = new ArrayList(); - - public MockPaymentSession(Protos.PaymentRequest request) throws PaymentProtocolException { - super(request); - } - - public ArrayList getPaymentLog() { - return paymentLog; - } - - @Override - protected ListenableFuture sendPayment(final URL url, final Protos.Payment payment) { - paymentLog.add(new PaymentLogItem(url, payment)); - return null; - } - - public class PaymentLogItem { - private final URL url; - private final Protos.Payment payment; - - PaymentLogItem(final URL url, final Protos.Payment payment) { - this.url = url; - this.payment = payment; - } - - public URL getUrl() { - return url; - } - - public Protos.Payment getPayment() { - return payment; - } - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/script/ScriptChunkTest.java b/core/src/test/java/com/dogecoin/dogecoinj/script/ScriptChunkTest.java deleted file mode 100644 index b7448408..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/script/ScriptChunkTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.script; - -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.OP_PUSHDATA1; -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.OP_PUSHDATA2; -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.OP_PUSHDATA4; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class ScriptChunkTest { - - @Test - public void testShortestPossibleDataPush() { - assertTrue("empty push", new ScriptBuilder().data(new byte[0]).build().getChunks().get(0) - .isShortestPossiblePushData()); - - for (byte i = -1; i < 127; i++) - assertTrue("push of single byte " + i, new ScriptBuilder().data(new byte[] { i }).build().getChunks() - .get(0).isShortestPossiblePushData()); - - for (int len = 2; len < Script.MAX_SCRIPT_ELEMENT_SIZE; len++) - assertTrue("push of " + len + " bytes", new ScriptBuilder().data(new byte[len]).build().getChunks().get(0) - .isShortestPossiblePushData()); - - // non-standard chunks - for (byte i = 1; i <= 16; i++) - assertFalse("push of smallnum " + i, new ScriptChunk(1, new byte[] { i }).isShortestPossiblePushData()); - assertFalse("push of 75 bytes", new ScriptChunk(OP_PUSHDATA1, new byte[75]).isShortestPossiblePushData()); - assertFalse("push of 255 bytes", new ScriptChunk(OP_PUSHDATA2, new byte[255]).isShortestPossiblePushData()); - assertFalse("push of 65535 bytes", new ScriptChunk(OP_PUSHDATA4, new byte[65535]).isShortestPossiblePushData()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/script/ScriptTest.java b/core/src/test/java/com/dogecoin/dogecoinj/script/ScriptTest.java deleted file mode 100644 index 5444ec8e..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/script/ScriptTest.java +++ /dev/null @@ -1,405 +0,0 @@ -/** - * Copyright 2011 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.script; - -import com.dogecoin.dogecoinj.core.*; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.dogecoin.dogecoinj.core.Transaction.SigHash; -import com.dogecoin.dogecoinj.crypto.TransactionSignature; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.dogecoin.dogecoinj.script.Script.VerifyFlag; -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -import org.hamcrest.core.IsNot; -import org.junit.Assert; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.util.*; - -import static com.dogecoin.dogecoinj.core.Utils.HEX; -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.OP_0; -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.OP_INVALIDOPCODE; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.*; - -public class ScriptTest { - // From tx 05e04c26c12fe408a3c1b71aa7996403f6acad1045252b1c62e055496f4d2cb1 on the testnet. - - static final String sigProg = "47304402202b4da291cc39faf8433911988f9f49fc5c995812ca2f94db61468839c228c3e90220628bff3ff32ec95825092fa051cba28558a981fcf59ce184b14f2e215e69106701410414b38f4be3bb9fa0f4f32b74af07152b2f2f630bc02122a491137b6c523e46f18a0d5034418966f93dfc37cc3739ef7b2007213a302b7fba161557f4ad644a1c"; - - static final String pubkeyProg = "76a91433e81a941e64cda12c6a299ed322ddbdd03f8d0e88ac"; - - static final NetworkParameters params = TestNet3Params.get(); - - private static final Logger log = LoggerFactory.getLogger(ScriptTest.class); - - @Test - public void testScriptSig() throws Exception { - byte[] sigProgBytes = HEX.decode(sigProg); - Script script = new Script(sigProgBytes); - // 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(pubkeyBytes); - assertEquals("DUP HASH160 PUSHDATA(20)[33e81a941e64cda12c6a299ed322ddbdd03f8d0e] EQUALVERIFY CHECKSIG", pubkey.toString()); - Address toAddr = new Address(params, pubkey.getPubKeyHash()); - assertEquals("mkFQohBpy2HDXrCwyMrYL5RtfrmeiuuPY2", toAddr.toString()); - } - - @Test - public void testMultiSig() throws Exception { - List keys = Lists.newArrayList(new ECKey(), new ECKey(), new ECKey()); - assertTrue(ScriptBuilder.createMultiSigOutputScript(2, keys).isSentToMultiSig()); - assertTrue(ScriptBuilder.createMultiSigOutputScript(3, keys).isSentToMultiSig()); - assertFalse(ScriptBuilder.createOutputScript(new ECKey()).isSentToMultiSig()); - try { - // Fail if we ask for more signatures than keys. - Script.createMultiSigOutputScript(4, keys); - fail(); - } catch (Throwable e) { - // Expected. - } - try { - // Must have at least one signature required. - Script.createMultiSigOutputScript(0, keys); - } catch (Throwable e) { - // Expected. - } - // Actual execution is tested by the data driven tests. - } - - @Test - public void testP2SHOutputScript() throws Exception { - Address p2shAddress = new Address(MainNetParams.get(), "35b9vsyH1KoFT5a5KtrKusaCcPLkiSo1tU"); - assertTrue(ScriptBuilder.createOutputScript(p2shAddress).isPayToScriptHash()); - } - - @Test - public void testIp() throws Exception { - byte[] bytes = HEX.decode("41043e96222332ea7848323c08116dddafbfa917b8e37f0bdf63841628267148588a09a43540942d58d49717ad3fabfe14978cf4f0a8b84d2435dad16e9aa4d7f935ac"); - Script s = new Script(bytes); - assertTrue(s.isSentToRawPubKey()); - } - - @Test - public void testCreateMultiSigInputScript() throws AddressFormatException { - // Setup transaction and signatures - ECKey key1 = new DumpedPrivateKey(params, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT").getKey(); - ECKey key2 = new DumpedPrivateKey(params, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo").getKey(); - ECKey key3 = new DumpedPrivateKey(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey(); - Script multisigScript = ScriptBuilder.createMultiSigOutputScript(2, Arrays.asList(key1, key2, key3)); - byte[] bytes = HEX.decode("01000000013df681ff83b43b6585fa32dd0e12b0b502e6481e04ee52ff0fdaf55a16a4ef61000000006b483045022100a84acca7906c13c5895a1314c165d33621cdcf8696145080895cbf301119b7cf0220730ff511106aa0e0a8570ff00ee57d7a6f24e30f592a10cae1deffac9e13b990012102b8d567bcd6328fd48a429f9cf4b315b859a58fd28c5088ef3cb1d98125fc4e8dffffffff02364f1c00000000001976a91439a02793b418de8ec748dd75382656453dc99bcb88ac40420f000000000017a9145780b80be32e117f675d6e0ada13ba799bf248e98700000000"); - Transaction transaction = new Transaction(params, bytes); - TransactionOutput output = transaction.getOutput(1); - Transaction spendTx = new Transaction(params); - Address address = new Address(params, "n3CFiCmBXVt5d3HXKQ15EFZyhPz4yj5F3H"); - Script outputScript = ScriptBuilder.createOutputScript(address); - spendTx.addOutput(output.getValue(), outputScript); - spendTx.addInput(output); - Sha256Hash sighash = spendTx.hashForSignature(0, multisigScript, SigHash.ALL, false); - ECKey.ECDSASignature party1Signature = key1.sign(sighash); - ECKey.ECDSASignature party2Signature = key2.sign(sighash); - TransactionSignature party1TransactionSignature = new TransactionSignature(party1Signature, SigHash.ALL, false); - TransactionSignature party2TransactionSignature = new TransactionSignature(party2Signature, SigHash.ALL, false); - - // Create p2sh multisig input script - Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(party1TransactionSignature, party2TransactionSignature), multisigScript); - - // Assert that the input script contains 4 chunks - assertTrue(inputScript.getChunks().size() == 4); - - // Assert that the input script created contains the original multisig - // script as the last chunk - ScriptChunk scriptChunk = inputScript.getChunks().get(inputScript.getChunks().size() - 1); - Assert.assertArrayEquals(scriptChunk.data, multisigScript.getProgram()); - - // Create regular multisig input script - inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(party1TransactionSignature, party2TransactionSignature)); - - // Assert that the input script only contains 3 chunks - assertTrue(inputScript.getChunks().size() == 3); - - // Assert that the input script created does not end with the original - // multisig script - scriptChunk = inputScript.getChunks().get(inputScript.getChunks().size() - 1); - Assert.assertThat(scriptChunk.data, IsNot.not(equalTo(multisigScript.getProgram()))); - } - - @Test - public void createAndUpdateEmptyInputScript() throws Exception { - TransactionSignature dummySig = TransactionSignature.dummy(); - ECKey key = new ECKey(); - - // pay-to-pubkey - Script inputScript = ScriptBuilder.createInputScript(dummySig); - assertThat(inputScript.getChunks().get(0).data, equalTo(dummySig.encodeToBitcoin())); - inputScript = ScriptBuilder.createInputScript(null); - assertThat(inputScript.getChunks().get(0).opcode, equalTo(OP_0)); - - // pay-to-address - inputScript = ScriptBuilder.createInputScript(dummySig, key); - assertThat(inputScript.getChunks().get(0).data, equalTo(dummySig.encodeToBitcoin())); - inputScript = ScriptBuilder.createInputScript(null, key); - assertThat(inputScript.getChunks().get(0).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(1).data, equalTo(key.getPubKey())); - - // pay-to-script-hash - ECKey key2 = new ECKey(); - Script multisigScript = ScriptBuilder.createMultiSigOutputScript(2, Arrays.asList(key, key2)); - inputScript = ScriptBuilder.createP2SHMultiSigInputScript(Arrays.asList(dummySig, dummySig), multisigScript); - assertThat(inputScript.getChunks().get(0).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(1).data, equalTo(dummySig.encodeToBitcoin())); - assertThat(inputScript.getChunks().get(2).data, equalTo(dummySig.encodeToBitcoin())); - assertThat(inputScript.getChunks().get(3).data, equalTo(multisigScript.getProgram())); - - inputScript = ScriptBuilder.createP2SHMultiSigInputScript(null, multisigScript); - assertThat(inputScript.getChunks().get(0).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(1).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(2).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(3).data, equalTo(multisigScript.getProgram())); - - inputScript = ScriptBuilder.updateScriptWithSignature(inputScript, dummySig.encodeToBitcoin(), 0, 1, 1); - assertThat(inputScript.getChunks().get(0).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(1).data, equalTo(dummySig.encodeToBitcoin())); - assertThat(inputScript.getChunks().get(2).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(3).data, equalTo(multisigScript.getProgram())); - - inputScript = ScriptBuilder.updateScriptWithSignature(inputScript, dummySig.encodeToBitcoin(), 1, 1, 1); - assertThat(inputScript.getChunks().get(0).opcode, equalTo(OP_0)); - assertThat(inputScript.getChunks().get(1).data, equalTo(dummySig.encodeToBitcoin())); - assertThat(inputScript.getChunks().get(2).data, equalTo(dummySig.encodeToBitcoin())); - assertThat(inputScript.getChunks().get(3).data, equalTo(multisigScript.getProgram())); - - // updating scriptSig with no missing signatures - try { - ScriptBuilder.updateScriptWithSignature(inputScript, dummySig.encodeToBitcoin(), 1, 1, 1); - fail("Exception expected"); - } catch (Exception e) { - assertEquals(IllegalArgumentException.class, e.getClass()); - } - } - - private Script parseScriptString(String string) throws IOException { - String[] words = string.split("[ \\t\\n]"); - - UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(); - - for(String w : words) { - if (w.equals("")) - continue; - if (w.matches("^-?[0-9]*$")) { - // Number - long val = Long.parseLong(w); - if (val >= -1 && val <= 16) - out.write(Script.encodeToOpN((int)val)); - else - Script.writeBytes(out, Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(val), false))); - } else if (w.matches("^0x[0-9a-fA-F]*$")) { - // Raw hex data, inserted NOT pushed onto stack: - out.write(HEX.decode(w.substring(2).toLowerCase())); - } else if (w.length() >= 2 && w.startsWith("'") && w.endsWith("'")) { - // Single-quoted string, pushed as data. NOTE: this is poor-man's - // parsing, spaces/tabs/newlines in single-quoted strings won't work. - Script.writeBytes(out, w.substring(1, w.length() - 1).getBytes(Charset.forName("UTF-8"))); - } else if (ScriptOpCodes.getOpCode(w) != OP_INVALIDOPCODE) { - // opcode, e.g. OP_ADD or OP_1: - out.write(ScriptOpCodes.getOpCode(w)); - } else if (w.startsWith("OP_") && ScriptOpCodes.getOpCode(w.substring(3)) != OP_INVALIDOPCODE) { - // opcode, e.g. OP_ADD or OP_1: - out.write(ScriptOpCodes.getOpCode(w.substring(3))); - } else { - throw new RuntimeException("Invalid Data"); - } - } - - return new Script(out.toByteArray()); - } - - private Set parseVerifyFlags(String str) { - Set flags = EnumSet.noneOf(VerifyFlag.class); - if (!"NONE".equals(str)) { - for (String flag : str.split(",")) { - try { - flags.add(VerifyFlag.valueOf(flag)); - } catch (IllegalArgumentException x) { - log.debug("Cannot handle verify flag {} -- ignored.", flag); - } - } - } - return flags; - } - - @Test - public void dataDrivenValidScripts() throws Exception { - JsonNode json = new ObjectMapper().readTree(new InputStreamReader(getClass().getResourceAsStream( - "script_valid.json"), Charsets.UTF_8)); - for (JsonNode test : json) { - Script scriptSig = parseScriptString(test.get(0).asText()); - Script scriptPubKey = parseScriptString(test.get(1).asText()); - Set verifyFlags = parseVerifyFlags(test.get(2).asText()); - try { - scriptSig.correctlySpends(new Transaction(params), 0, scriptPubKey, verifyFlags); - } catch (ScriptException e) { - System.err.println(test); - System.err.flush(); - throw e; - } - } - } - - @Test - public void dataDrivenInvalidScripts() throws Exception { - JsonNode json = new ObjectMapper().readTree(new InputStreamReader(getClass().getResourceAsStream( - "script_invalid.json"), Charsets.UTF_8)); - for (JsonNode test : json) { - try { - Script scriptSig = parseScriptString(test.get(0).asText()); - Script scriptPubKey = parseScriptString(test.get(1).asText()); - Set verifyFlags = parseVerifyFlags(test.get(2).asText()); - scriptSig.correctlySpends(new Transaction(params), 0, scriptPubKey, verifyFlags); - System.err.println(test); - System.err.flush(); - fail(); - } catch (VerificationException e) { - // Expected. - } - } - } - - private Map parseScriptPubKeys(JsonNode inputs) throws IOException { - Map scriptPubKeys = new HashMap(); - for (JsonNode input : inputs) { - String hash = input.get(0).asText(); - int index = input.get(1).asInt(); - String script = input.get(2).asText(); - Sha256Hash sha256Hash = new Sha256Hash(HEX.decode(hash)); - scriptPubKeys.put(new TransactionOutPoint(params, index, sha256Hash), parseScriptString(script)); - } - return scriptPubKeys; - } - - @Test - public void dataDrivenValidTransactions() throws Exception { - JsonNode json = new ObjectMapper().readTree(new InputStreamReader(getClass().getResourceAsStream( - "tx_valid.json"), Charsets.UTF_8)); - for (JsonNode test : json) { - if (test.isArray() && test.size() == 1 && test.get(0).isTextual()) - continue; // This is a comment. - Transaction transaction = null; - try { - Map scriptPubKeys = parseScriptPubKeys(test.get(0)); - transaction = new Transaction(params, HEX.decode(test.get(1).asText().toLowerCase())); - transaction.verify(); - Set verifyFlags = parseVerifyFlags(test.get(2).asText()); - - for (int i = 0; i < transaction.getInputs().size(); i++) { - TransactionInput input = transaction.getInputs().get(i); - if (input.getOutpoint().getIndex() == 0xffffffffL) - input.getOutpoint().setIndex(-1); - assertTrue(scriptPubKeys.containsKey(input.getOutpoint())); - input.getScriptSig().correctlySpends(transaction, i, scriptPubKeys.get(input.getOutpoint()), - verifyFlags); - } - } catch (Exception e) { - System.err.println(test); - if (transaction != null) - System.err.println(transaction); - throw e; - } - } - } - - @Test - public void dataDrivenInvalidTransactions() throws Exception { - JsonNode json = new ObjectMapper().readTree(new InputStreamReader(getClass().getResourceAsStream( - "tx_invalid.json"), Charsets.UTF_8)); - for (JsonNode test : json) { - if (test.isArray() && test.size() == 1 && test.get(0).isTextual()) - continue; // This is a comment. - Map scriptPubKeys = parseScriptPubKeys(test.get(0)); - Transaction transaction = new Transaction(params, HEX.decode(test.get(1).asText().toLowerCase())); - Set verifyFlags = parseVerifyFlags(test.get(2).asText()); - - boolean valid = true; - try { - transaction.verify(); - } catch (VerificationException e) { - valid = false; - } - - // The reference client checks this case in CheckTransaction, but we leave it to - // later where we will see an attempt to double-spend, so we explicitly check here - HashSet set = new HashSet(); - for (TransactionInput input : transaction.getInputs()) { - if (set.contains(input.getOutpoint())) - valid = false; - set.add(input.getOutpoint()); - } - - for (int i = 0; i < transaction.getInputs().size() && valid; i++) { - TransactionInput input = transaction.getInputs().get(i); - assertTrue(scriptPubKeys.containsKey(input.getOutpoint())); - try { - input.getScriptSig().correctlySpends(transaction, i, scriptPubKeys.get(input.getOutpoint()), - verifyFlags); - } catch (VerificationException e) { - valid = false; - } - } - - if (valid) - fail(); - } - } - - @Test - public void getToAddress() throws Exception { - // pay to pubkey - ECKey toKey = new ECKey(); - Address toAddress = toKey.toAddress(params); - assertEquals(toAddress, ScriptBuilder.createOutputScript(toKey).getToAddress(params, true)); - // pay to pubkey hash - assertEquals(toAddress, ScriptBuilder.createOutputScript(toAddress).getToAddress(params, true)); - // pay to script hash - Script p2shScript = ScriptBuilder.createP2SHOutputScript(new byte[20]); - Address scriptAddress = Address.fromP2SHScript(params, p2shScript); - assertEquals(scriptAddress, p2shScript.getToAddress(params, true)); - } - - @Test(expected = ScriptException.class) - public void getToAddressNoPubKey() throws Exception { - ScriptBuilder.createOutputScript(new ECKey()).getToAddress(params, false); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/store/SPVBlockStoreTest.java b/core/src/test/java/com/dogecoin/dogecoinj/store/SPVBlockStoreTest.java deleted file mode 100644 index 9f1f1073..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/store/SPVBlockStoreTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2013 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.dogecoin.dogecoinj.store; - -import com.dogecoin.dogecoinj.core.Address; -import com.dogecoin.dogecoinj.core.ECKey; -import com.dogecoin.dogecoinj.core.NetworkParameters; -import com.dogecoin.dogecoinj.core.StoredBlock; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import org.junit.Test; - -import java.io.File; - -import static org.junit.Assert.assertEquals; - -public class SPVBlockStoreTest { - - @Test - public void basics() throws Exception { - NetworkParameters params = UnitTestParams.get(); - File f = File.createTempFile("spvblockstore", null); - f.delete(); - f.deleteOnExit(); - SPVBlockStore store = new SPVBlockStore(params, f); - - Address to = new ECKey().toAddress(params); - // Check the first block in a new store is the genesis block. - StoredBlock genesis = store.getChainHead(); - assertEquals(params.getGenesisBlock(), genesis.getHeader()); - assertEquals(0, genesis.getHeight()); - - - // Build a new block. - StoredBlock b1 = genesis.build(genesis.getHeader().createNextBlock(to).cloneAsHeader()); - store.put(b1); - store.setChainHead(b1); - store.close(); - - // Check we can get it back out again if we rebuild the store object. - store = new SPVBlockStore(params, f); - StoredBlock b2 = store.get(b1.getHeader().getHash()); - assertEquals(b1, b2); - // Check the chain head was stored correctly also. - StoredBlock chainHead = store.getChainHead(); - assertEquals(b1, chainHead); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/store/WalletProtobufSerializerTest.java b/core/src/test/java/com/dogecoin/dogecoinj/store/WalletProtobufSerializerTest.java deleted file mode 100644 index 45669fee..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/store/WalletProtobufSerializerTest.java +++ /dev/null @@ -1,363 +0,0 @@ -/** - * Copyright 2012 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.store; - - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.core.TransactionConfidence.ConfidenceType; -import com.dogecoin.dogecoinj.crypto.DeterministicKey; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.testing.FooWalletExtension; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.dogecoin.dogecoinj.utils.Threading; -import com.dogecoin.dogecoinj.wallet.DeterministicKeyChain; -import com.dogecoin.dogecoinj.wallet.KeyChain; -import com.google.protobuf.ByteString; - -import com.dogecoin.dogecoinj.wallet.MarriedKeyChain; -import com.dogecoin.dogecoinj.wallet.Protos; -import org.junit.Before; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.math.BigInteger; -import java.net.InetAddress; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.Set; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.testing.FakeTxBuilder.createFakeTx; -import static org.junit.Assert.*; - -public class WalletProtobufSerializerTest { - static final NetworkParameters params = UnitTestParams.get(); - private ECKey myKey; - private ECKey myWatchedKey; - private Address myAddress; - private Wallet myWallet; - - public static String WALLET_DESCRIPTION = "The quick brown fox lives in \u4f26\u6566"; // Beijing in Chinese - private long mScriptCreationTime; - - @Before - public void setUp() throws Exception { - BriefLogFormatter.initVerbose(); - myWatchedKey = new ECKey(); - myWallet = new Wallet(params); - myKey = new ECKey(); - myKey.setCreationTimeSeconds(123456789L); - myWallet.importKey(myKey); - myAddress = myKey.toAddress(params); - myWallet = new Wallet(params); - myWallet.importKey(myKey); - mScriptCreationTime = new Date().getTime() / 1000 - 1234; - myWallet.addWatchedAddress(myWatchedKey.toAddress(params), mScriptCreationTime); - myWallet.setDescription(WALLET_DESCRIPTION); - } - - @Test - public void empty() throws Exception { - // Check the base case of a wallet with one key and no transactions. - Wallet wallet1 = roundTrip(myWallet); - assertEquals(0, wallet1.getTransactions(true).size()); - assertEquals(Coin.ZERO, wallet1.getBalance()); - assertArrayEquals(myKey.getPubKey(), - wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey()); - assertArrayEquals(myKey.getPrivKeyBytes(), - wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes()); - assertEquals(myKey.getCreationTimeSeconds(), - wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getCreationTimeSeconds()); - assertEquals(mScriptCreationTime, - wallet1.getWatchedScripts().get(0).getCreationTimeSeconds()); - assertEquals(1, wallet1.getWatchedScripts().size()); - assertEquals(ScriptBuilder.createOutputScript(myWatchedKey.toAddress(params)), - wallet1.getWatchedScripts().get(0)); - assertEquals(WALLET_DESCRIPTION, wallet1.getDescription()); - } - - @Test - public void oneTx() throws Exception { - // Check basic tx serialization. - Coin v1 = COIN; - Transaction t1 = createFakeTx(params, v1, myAddress); - t1.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("1.2.3.4"))); - t1.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("5.6.7.8"))); - t1.getConfidence().setSource(TransactionConfidence.Source.NETWORK); - myWallet.receivePending(t1, null); - Wallet wallet1 = roundTrip(myWallet); - assertEquals(1, wallet1.getTransactions(true).size()); - assertEquals(v1, wallet1.getBalance(Wallet.BalanceType.ESTIMATED)); - Transaction t1copy = wallet1.getTransaction(t1.getHash()); - assertArrayEquals(t1.bitcoinSerialize(), t1copy.bitcoinSerialize()); - assertEquals(2, t1copy.getConfidence().numBroadcastPeers()); - assertEquals(TransactionConfidence.Source.NETWORK, t1copy.getConfidence().getSource()); - - Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(myWallet); - assertEquals(Protos.Key.Type.ORIGINAL, walletProto.getKey(0).getType()); - assertEquals(0, walletProto.getExtensionCount()); - assertEquals(1, walletProto.getTransactionCount()); - assertEquals(6, walletProto.getKeyCount()); - - Protos.Transaction t1p = walletProto.getTransaction(0); - assertEquals(0, t1p.getBlockHashCount()); - assertArrayEquals(t1.getHash().getBytes(), t1p.getHash().toByteArray()); - assertEquals(Protos.Transaction.Pool.PENDING, t1p.getPool()); - assertFalse(t1p.hasLockTime()); - assertFalse(t1p.getTransactionInput(0).hasSequence()); - assertArrayEquals(t1.getInputs().get(0).getOutpoint().getHash().getBytes(), - t1p.getTransactionInput(0).getTransactionOutPointHash().toByteArray()); - assertEquals(0, t1p.getTransactionInput(0).getTransactionOutPointIndex()); - assertEquals(t1p.getTransactionOutput(0).getValue(), v1.value); - } - - @Test - public void doubleSpend() throws Exception { - // Check that we can serialize double spends correctly, as this is a slightly tricky case. - FakeTxBuilder.DoubleSpends doubleSpends = FakeTxBuilder.createFakeDoubleSpendTxns(params, myAddress); - // t1 spends to our wallet. - myWallet.receivePending(doubleSpends.t1, null); - // t2 rolls back t1 and spends somewhere else. - myWallet.receiveFromBlock(doubleSpends.t2, null, BlockChain.NewBlockType.BEST_CHAIN, 0); - Wallet wallet1 = roundTrip(myWallet); - assertEquals(1, wallet1.getTransactions(true).size()); - Transaction t1 = wallet1.getTransaction(doubleSpends.t1.getHash()); - assertEquals(ConfidenceType.DEAD, t1.getConfidence().getConfidenceType()); - assertEquals(Coin.ZERO, wallet1.getBalance()); - - // TODO: Wallet should store overriding transactions even if they are not wallet-relevant. - // assertEquals(doubleSpends.t2, t1.getConfidence().getOverridingTransaction()); - } - - @Test - public void testKeys() throws Exception { - for (int i = 0 ; i < 20 ; i++) { - myKey = new ECKey(); - myAddress = myKey.toAddress(params); - myWallet = new Wallet(params); - myWallet.importKey(myKey); - Wallet wallet1 = roundTrip(myWallet); - assertArrayEquals(myKey.getPubKey(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey()); - assertArrayEquals(myKey.getPrivKeyBytes(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes()); - } - } - - @Test - public void testLastBlockSeenHash() throws Exception { - // Test the lastBlockSeenHash field works. - - // LastBlockSeenHash should be empty if never set. - Wallet wallet = new Wallet(params); - Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(wallet); - ByteString lastSeenBlockHash = walletProto.getLastSeenBlockHash(); - assertTrue(lastSeenBlockHash.isEmpty()); - - // Create a block. - Block block = new Block(params, BlockTest.blockBytes); - Sha256Hash blockHash = block.getHash(); - wallet.setLastBlockSeenHash(blockHash); - wallet.setLastBlockSeenHeight(1); - - // Roundtrip the wallet and check it has stored the blockHash. - Wallet wallet1 = roundTrip(wallet); - assertEquals(blockHash, wallet1.getLastBlockSeenHash()); - assertEquals(1, wallet1.getLastBlockSeenHeight()); - - // Test the Satoshi genesis block (hash of all zeroes) is roundtripped ok. - Block genesisBlock = MainNetParams.get().getGenesisBlock(); - wallet.setLastBlockSeenHash(genesisBlock.getHash()); - Wallet wallet2 = roundTrip(wallet); - assertEquals(genesisBlock.getHash(), wallet2.getLastBlockSeenHash()); - } - - @Test - public void testAppearedAtChainHeightDepthAndWorkDone() throws Exception { - // Test the TransactionConfidence appearedAtChainHeight, depth and workDone field are stored. - - BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params)); - - final ArrayList txns = new ArrayList(2); - myWallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - txns.add(tx); - } - }); - - // Start by building two blocks on top of the genesis block. - Block b1 = params.getGenesisBlock().createNextBlock(myAddress); - BigInteger work1 = b1.getWork(); - assertTrue(work1.signum() > 0); - - Block b2 = b1.createNextBlock(myAddress); - BigInteger work2 = b2.getWork(); - assertTrue(work2.signum() > 0); - - assertTrue(chain.add(b1)); - assertTrue(chain.add(b2)); - - // We now have the following chain: - // genesis -> b1 -> b2 - - // Check the transaction confidence levels are correct before wallet roundtrip. - Threading.waitForUserCode(); - assertEquals(2, txns.size()); - - TransactionConfidence confidence0 = txns.get(0).getConfidence(); - TransactionConfidence confidence1 = txns.get(1).getConfidence(); - - assertEquals(1, confidence0.getAppearedAtChainHeight()); - assertEquals(2, confidence1.getAppearedAtChainHeight()); - - assertEquals(2, confidence0.getDepthInBlocks()); - assertEquals(1, confidence1.getDepthInBlocks()); - - // Roundtrip the wallet and check it has stored the depth and workDone. - Wallet rebornWallet = roundTrip(myWallet); - - Set rebornTxns = rebornWallet.getTransactions(false); - assertEquals(2, rebornTxns.size()); - - // The transactions are not guaranteed to be in the same order so sort them to be in chain height order if required. - Iterator it = rebornTxns.iterator(); - Transaction txA = it.next(); - Transaction txB = it.next(); - - Transaction rebornTx0, rebornTx1; - if (txA.getConfidence().getAppearedAtChainHeight() == 1) { - rebornTx0 = txA; - rebornTx1 = txB; - } else { - rebornTx0 = txB; - rebornTx1 = txA; - } - - TransactionConfidence rebornConfidence0 = rebornTx0.getConfidence(); - TransactionConfidence rebornConfidence1 = rebornTx1.getConfidence(); - - assertEquals(1, rebornConfidence0.getAppearedAtChainHeight()); - assertEquals(2, rebornConfidence1.getAppearedAtChainHeight()); - - assertEquals(2, rebornConfidence0.getDepthInBlocks()); - assertEquals(1, rebornConfidence1.getDepthInBlocks()); - } - - private static Wallet roundTrip(Wallet wallet) throws Exception { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - new WalletProtobufSerializer().writeWallet(wallet, output); - ByteArrayInputStream test = new ByteArrayInputStream(output.toByteArray()); - assertTrue(WalletProtobufSerializer.isWallet(test)); - ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); - return new WalletProtobufSerializer().readWallet(input); - } - - @Test - public void testRoundTripNormalWallet() throws Exception { - Wallet wallet1 = roundTrip(myWallet); - assertEquals(0, wallet1.getTransactions(true).size()); - assertEquals(Coin.ZERO, wallet1.getBalance()); - assertArrayEquals(myKey.getPubKey(), - wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey()); - assertArrayEquals(myKey.getPrivKeyBytes(), - wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes()); - assertEquals(myKey.getCreationTimeSeconds(), - wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getCreationTimeSeconds()); - } - - @Test - public void testRoundTripMarriedWallet() throws Exception { - // create 2-of-2 married wallet - myWallet = new Wallet(params); - final DeterministicKeyChain partnerChain = new DeterministicKeyChain(new SecureRandom()); - DeterministicKey partnerKey = DeterministicKey.deserializeB58(null, partnerChain.getWatchingKey().serializePubB58(params), params); - MarriedKeyChain chain = MarriedKeyChain.builder() - .random(new SecureRandom()) - .followingKeys(partnerKey) - .threshold(2).build(); - myWallet.addAndActivateHDChain(chain); - - myAddress = myWallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - - Wallet wallet1 = roundTrip(myWallet); - assertEquals(0, wallet1.getTransactions(true).size()); - assertEquals(Coin.ZERO, wallet1.getBalance()); - assertEquals(2, wallet1.getActiveKeychain().getSigsRequiredToSpend()); - assertEquals(myAddress, wallet1.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS)); - } - - @Test - public void coinbaseTxns() throws Exception { - // Covers issue 420 where the outpoint index of a coinbase tx input was being mis-serialized. - Block b = params.getGenesisBlock().createNextBlockWithCoinbase(myKey.getPubKey(), FIFTY_COINS); - Transaction coinbase = b.getTransactions().get(0); - assertTrue(coinbase.isCoinBase()); - BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params)); - assertTrue(chain.add(b)); - // Wallet now has a coinbase tx in it. - assertEquals(1, myWallet.getTransactions(true).size()); - assertTrue(myWallet.getTransaction(coinbase.getHash()).isCoinBase()); - Wallet wallet2 = roundTrip(myWallet); - assertEquals(1, wallet2.getTransactions(true).size()); - assertTrue(wallet2.getTransaction(coinbase.getHash()).isCoinBase()); - } - - @Test - public void tags() throws Exception { - myWallet.setTag("foo", ByteString.copyFromUtf8("bar")); - assertEquals("bar", myWallet.getTag("foo").toStringUtf8()); - myWallet = roundTrip(myWallet); - assertEquals("bar", myWallet.getTag("foo").toStringUtf8()); - } - - @Test - public void testExtensions() throws Exception { - myWallet.addExtension(new FooWalletExtension("com.whatever.required", true)); - Protos.Wallet proto = new WalletProtobufSerializer().walletToProto(myWallet); - // Initial extension is mandatory: try to read it back into a wallet that doesn't know about it. - try { - new WalletProtobufSerializer().readWallet(params, null, proto); - fail(); - } catch (UnreadableWalletException e) { - assertTrue(e.getMessage().contains("mandatory")); - } - Wallet wallet = new WalletProtobufSerializer().readWallet(params, - new WalletExtension[]{ new FooWalletExtension("com.whatever.required", true) }, - proto); - assertTrue(wallet.getExtensions().containsKey("com.whatever.required")); - - // Non-mandatory extensions are ignored if the wallet doesn't know how to read them. - Wallet wallet2 = new Wallet(params); - wallet2.addExtension(new FooWalletExtension("com.whatever.optional", false)); - Protos.Wallet proto2 = new WalletProtobufSerializer().walletToProto(wallet2); - Wallet wallet5 = new WalletProtobufSerializer().readWallet(params, null, proto2); - assertEquals(0, wallet5.getExtensions().size()); - } - - @Test(expected = UnreadableWalletException.FutureVersion.class) - public void versions() throws Exception { - Protos.Wallet.Builder proto = Protos.Wallet.newBuilder(new WalletProtobufSerializer().walletToProto(myWallet)); - proto.setVersion(2); - new WalletProtobufSerializer().readWallet(params, null, proto.build()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/uri/BitcoinURITest.java b/core/src/test/java/com/dogecoin/dogecoinj/uri/BitcoinURITest.java deleted file mode 100644 index b4683ccd..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/uri/BitcoinURITest.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2012, 2014 the original author or authors. - * - * 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.dogecoin.dogecoinj.uri; - -import com.dogecoin.dogecoinj.core.Address; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.TestNet3Params; -import com.google.common.collect.ImmutableList; -import org.junit.Test; - -import java.io.UnsupportedEncodingException; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static org.junit.Assert.*; - -public class BitcoinURITest { - private BitcoinURI testObject = null; - - private static final String MAINNET_GOOD_ADDRESS = "1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH"; - - @Test - public void testConvertToBitcoinURI() throws Exception { - Address goodAddress = new Address(MainNetParams.get(), MAINNET_GOOD_ADDRESS); - - // simple example - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello&message=AMessage", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("12.34"), "Hello", "AMessage")); - - // example with spaces, ampersand and plus - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello%20World&message=Mess%20%26%20age%20%2B%20hope", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("12.34"), "Hello World", "Mess & age + hope")); - - // no amount, label present, message present - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?label=Hello&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, null, "Hello", "glory")); - - // amount present, no label, message present - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=0.1&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("0.1"), null, "glory")); - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=0.1&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("0.1"), "", "glory")); - - // amount present, label present, no message - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("12.34"), "Hello", null)); - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("12.34"), "Hello", "")); - - // amount present, no label, no message - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=1000", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("1000"), null, null)); - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=1000", BitcoinURI.convertToBitcoinURI(goodAddress, parseCoin("1000"), "", "")); - - // no amount, label present, no message - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress, null, "Hello", null)); - - // no amount, no label, message present - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?message=Agatha", BitcoinURI.convertToBitcoinURI(goodAddress, null, null, "Agatha")); - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?message=Agatha", BitcoinURI.convertToBitcoinURI(goodAddress, null, "", "Agatha")); - - // no amount, no label, no message - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS, BitcoinURI.convertToBitcoinURI(goodAddress, null, null, null)); - assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS, BitcoinURI.convertToBitcoinURI(goodAddress, null, "", "")); - } - - @Test - public void testGood_Simple() throws BitcoinURIParseException { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS); - assertNotNull(testObject); - assertNull("Unexpected amount", testObject.getAmount()); - assertNull("Unexpected label", testObject.getLabel()); - assertEquals("Unexpected label", 20, testObject.getAddress().getHash160().length); - } - - /** - * Test a broken URI (bad scheme) - */ - @Test - public void testBad_Scheme() { - try { - testObject = new BitcoinURI(MainNetParams.get(), "blimpcoin:" + MAINNET_GOOD_ADDRESS); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - } - } - - /** - * Test a broken URI (bad syntax) - */ - @Test - public void testBad_BadSyntax() { - // Various illegal characters - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + "|" + MAINNET_GOOD_ADDRESS); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("Bad URI syntax")); - } - - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "\\"); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("Bad URI syntax")); - } - - // Separator without field - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":"); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("Bad URI syntax")); - } - } - - /** - * Test a broken URI (missing address) - */ - @Test - public void testBad_Address() { - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - } - } - - /** - * Test a broken URI (bad address type) - */ - @Test - public void testBad_IncorrectAddressType() { - try { - testObject = new BitcoinURI(TestNet3Params.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("Bad address")); - } - } - - /** - * Handles a simple amount - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testGood_Amount() throws BitcoinURIParseException { - // Test the decimal parsing - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=6543210.12345678"); - assertEquals("654321012345678", testObject.getAmount().toString()); - - // Test the decimal parsing - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=.12345678"); - assertEquals("12345678", testObject.getAmount().toString()); - - // Test the integer parsing - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=6543210"); - assertEquals("654321000000000", testObject.getAmount().toString()); - } - - /** - * Handles a simple label - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testGood_Label() throws BitcoinURIParseException { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?label=Hello%20World"); - assertEquals("Hello World", testObject.getLabel()); - } - - /** - * Handles a simple label with an embedded ampersand and plus - * - * @throws BitcoinURIParseException - * If something goes wrong - * @throws UnsupportedEncodingException - */ - @Test - public void testGood_LabelWithAmpersandAndPlus() throws Exception { - String testString = "Hello Earth & Mars + Venus"; - String encodedLabel = BitcoinURI.encodeURLString(testString); - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" - + encodedLabel); - assertEquals(testString, testObject.getLabel()); - } - - /** - * Handles a Russian label (Unicode test) - * - * @throws BitcoinURIParseException - * If something goes wrong - * @throws UnsupportedEncodingException - */ - @Test - public void testGood_LabelWithRussian() throws Exception { - // Moscow in Russian in Cyrillic - String moscowString = "\u041c\u043e\u0441\u043a\u0432\u0430"; - String encodedLabel = BitcoinURI.encodeURLString(moscowString); - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" - + encodedLabel); - assertEquals(moscowString, testObject.getLabel()); - } - - /** - * Handles a simple message - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testGood_Message() throws BitcoinURIParseException { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?message=Hello%20World"); - assertEquals("Hello World", testObject.getMessage()); - } - - /** - * Handles various well-formed combinations - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testGood_Combinations() throws BitcoinURIParseException { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=6543210&label=Hello%20World&message=Be%20well"); - assertEquals( - "BitcoinURI['amount'='654321000000000','label'='Hello World','message'='Be well','address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH']", - testObject.toString()); - } - - /** - * Handles a badly formatted amount field - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testBad_Amount() throws BitcoinURIParseException { - // Missing - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount="); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("amount")); - } - - // Non-decimal (BIP 21) - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=12X4"); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("amount")); - } - } - - @Test - public void testEmpty_Label() throws BitcoinURIParseException { - assertNull(new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?label=").getLabel()); - } - - @Test - public void testEmpty_Message() throws BitcoinURIParseException { - assertNull(new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?message=").getMessage()); - } - - /** - * Handles duplicated fields (sneaky address overwrite attack) - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testBad_Duplicated() throws BitcoinURIParseException { - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?address=aardvark"); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("address")); - } - } - - @Test - public void testGood_ManyEquals() throws BitcoinURIParseException { - assertEquals("aardvark=zebra", new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" - + MAINNET_GOOD_ADDRESS + "?label=aardvark=zebra").getLabel()); - } - - /** - * Handles unknown fields (required and not required) - * - * @throws BitcoinURIParseException - * If something goes wrong - */ - @Test - public void testUnknown() throws BitcoinURIParseException { - // Unknown not required field - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?aardvark=true"); - assertEquals("BitcoinURI['aardvark'='true','address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH']", testObject.toString()); - - assertEquals("true", (String) testObject.getParameterByName("aardvark")); - - // Unknown not required field (isolated) - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?aardvark"); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("no separator")); - } - - // Unknown and required field - try { - testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?req-aardvark=true"); - fail("Expecting BitcoinURIParseException"); - } catch (BitcoinURIParseException e) { - assertTrue(e.getMessage().contains("req-aardvark")); - } - } - - @Test - public void brokenURIs() throws BitcoinURIParseException { - // Check we can parse the incorrectly formatted URIs produced by blockchain.info and its iPhone app. - String str = "bitcoin://1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH?amount=0.01000000"; - BitcoinURI uri = new BitcoinURI(str); - assertEquals("1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH", uri.getAddress().toString()); - assertEquals(CENT, uri.getAmount()); - } - - @Test(expected = BitcoinURIParseException.class) - public void testBad_AmountTooPrecise() throws BitcoinURIParseException { - new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=0.123456789"); - } - - @Test(expected = BitcoinURIParseException.class) - public void testBad_NegativeAmount() throws BitcoinURIParseException { - new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=-1"); - } - - @Test(expected = BitcoinURIParseException.class) - public void testBad_TooLargeAmount() throws BitcoinURIParseException { - new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=100000000"); - } - - @Test - public void testPaymentProtocolReq() throws Exception { - // Non-backwards compatible form ... - BitcoinURI uri = new BitcoinURI(TestNet3Params.get(), "bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin%2Ff.php%3Fh%3Db0f02e7cea67f168e25ec9b9f9d584f9"); - assertEquals("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9", uri.getPaymentRequestUrl()); - assertEquals(ImmutableList.of("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9"), - uri.getPaymentRequestUrls()); - assertNull(uri.getAddress()); - } - - @Test - public void testMultiplePaymentProtocolReq() throws Exception { - BitcoinURI uri = new BitcoinURI(MainNetParams.get(), - "bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin&r1=bt:112233445566"); - assertEquals(ImmutableList.of("bt:112233445566", "https://bitcoincore.org/~gavin"), uri.getPaymentRequestUrls()); - assertEquals("https://bitcoincore.org/~gavin", uri.getPaymentRequestUrl()); - } - - @Test - public void testNoPaymentProtocolReq() throws Exception { - BitcoinURI uri = new BitcoinURI(MainNetParams.get(), "bitcoin:" + MAINNET_GOOD_ADDRESS); - assertNull(uri.getPaymentRequestUrl()); - assertEquals(ImmutableList.of(), uri.getPaymentRequestUrls()); - assertNotNull(uri.getAddress()); - } - - @Test - public void testUnescapedPaymentProtocolReq() throws Exception { - BitcoinURI uri = new BitcoinURI(TestNet3Params.get(), - "bitcoin:?r=https://merchant.com/pay.php?h%3D2a8628fc2fbe"); - assertEquals("https://merchant.com/pay.php?h=2a8628fc2fbe", uri.getPaymentRequestUrl()); - assertEquals(ImmutableList.of("https://merchant.com/pay.php?h=2a8628fc2fbe"), uri.getPaymentRequestUrls()); - assertNull(uri.getAddress()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/utils/BaseTaggableObjectTest.java b/core/src/test/java/com/dogecoin/dogecoinj/utils/BaseTaggableObjectTest.java deleted file mode 100644 index 3dce2e8d..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/utils/BaseTaggableObjectTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dogecoin.dogecoinj.utils; - -import com.google.protobuf.ByteString; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class BaseTaggableObjectTest { - private BaseTaggableObject obj; - - @Before - public void setUp() throws Exception { - obj = new BaseTaggableObject(); - } - - @Test - public void tags() throws Exception { - assertNull(obj.maybeGetTag("foo")); - obj.setTag("foo", ByteString.copyFromUtf8("bar")); - assertEquals("bar", obj.getTag("foo").toStringUtf8()); - } - - @Test(expected = IllegalArgumentException.class) - public void exception() throws Exception { - obj.getTag("non existent"); - } -} \ No newline at end of file diff --git a/core/src/test/java/com/dogecoin/dogecoinj/utils/BtcFormatTest.java b/core/src/test/java/com/dogecoin/dogecoinj/utils/BtcFormatTest.java deleted file mode 100644 index 59c75115..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/utils/BtcFormatTest.java +++ /dev/null @@ -1,1477 +0,0 @@ -/* - * Copyright 2014 Adam Mackler - * - * 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.dogecoin.dogecoinj.utils; - -import com.dogecoin.dogecoinj.core.Coin; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.math.BigDecimal; -import java.text.*; -import java.text.AttributedCharacterIterator.Attribute; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.dogecoin.dogecoinj.core.NetworkParameters.MAX_MONEY; -import static com.dogecoin.dogecoinj.utils.BtcAutoFormat.Style.CODE; -import static com.dogecoin.dogecoinj.utils.BtcAutoFormat.Style.SYMBOL; -import static com.dogecoin.dogecoinj.utils.BtcFixedFormat.REPEATING_DOUBLETS; -import static com.dogecoin.dogecoinj.utils.BtcFixedFormat.REPEATING_TRIPLETS; -import static java.text.NumberFormat.Field.DECIMAL_SEPARATOR; -import static java.util.Locale.*; -import static org.junit.Assert.*; - -@RunWith(Parameterized.class) -public class BtcFormatTest { - - @Parameters - public static Set data() { - Set localeSet = new HashSet(); - for (Locale locale : Locale.getAvailableLocales()) { - localeSet.add(new Locale[]{locale}); - } - return localeSet; - } - - public BtcFormatTest(Locale defaultLocale) { - Locale.setDefault(defaultLocale); - } - - @Test - public void prefixTest() { // prefix b/c symbol is prefixed - BtcFormat usFormat = BtcFormat.getSymbolInstance(Locale.US); - assertEquals("฿1.00", usFormat.format(COIN)); - assertEquals("฿1.01", usFormat.format(101000000)); - assertEquals("₥฿0.01", usFormat.format(1000)); - assertEquals("₥฿1,011.00", usFormat.format(101100000)); - assertEquals("₥฿1,000.01", usFormat.format(100001000)); - assertEquals("µ฿1,000,001.00", usFormat.format(100000100)); - assertEquals("µ฿1,000,000.10", usFormat.format(100000010)); - assertEquals("µ฿1,000,000.01", usFormat.format(100000001)); - assertEquals("µ฿1.00", usFormat.format(100)); - assertEquals("µ฿0.10", usFormat.format(10)); - assertEquals("µ฿0.01", usFormat.format(1)); - } - - @Test - public void suffixTest() { - BtcFormat deFormat = BtcFormat.getSymbolInstance(Locale.GERMANY); - // int - assertEquals("1,00 ฿", deFormat.format(100000000)); - assertEquals("1,01 ฿", deFormat.format(101000000)); - assertEquals("1.011,00 ₥฿", deFormat.format(101100000)); - assertEquals("1.000,01 ₥฿", deFormat.format(100001000)); - assertEquals("1.000.001,00 µ฿", deFormat.format(100000100)); - assertEquals("1.000.000,10 µ฿", deFormat.format(100000010)); - assertEquals("1.000.000,01 µ฿", deFormat.format(100000001)); - } - - @Test - public void defaultLocaleTest() { - assertEquals( - "Default Locale is " + Locale.getDefault().toString(), - BtcFormat.getInstance().pattern(), BtcFormat.getInstance(Locale.getDefault()).pattern() - ); - assertEquals( - "Default Locale is " + Locale.getDefault().toString(), - BtcFormat.getCodeInstance().pattern(), - BtcFormat.getCodeInstance(Locale.getDefault()).pattern() - ); - } - - @Test - public void symbolCollisionTest() { - Locale[] locales = BtcFormat.getAvailableLocales(); - for (int i = 0; i < locales.length; ++i) { - String cs = ((DecimalFormat)NumberFormat.getCurrencyInstance(locales[i])). - getDecimalFormatSymbols().getCurrencySymbol(); - if (cs.contains("฿")) { - BtcFormat bf = BtcFormat.getSymbolInstance(locales[i]); - String coin = bf.format(COIN); - assertTrue(coin.contains("Ƀ")); - assertFalse(coin.contains("฿")); - String milli = bf.format(valueOf(10000)); - assertTrue(milli.contains("₥Ƀ")); - assertFalse(milli.contains("฿")); - String micro = bf.format(valueOf(100)); - assertTrue(micro.contains("µɃ")); - assertFalse(micro.contains("฿")); - BtcFormat ff = BtcFormat.builder().scale(0).locale(locales[i]).pattern("¤#.#").build(); - assertEquals("Ƀ", ((BtcFixedFormat)ff).symbol()); - assertEquals("Ƀ", ff.coinSymbol()); - coin = ff.format(COIN); - assertTrue(coin.contains("Ƀ")); - assertFalse(coin.contains("฿")); - BtcFormat mlff = BtcFormat.builder().scale(3).locale(locales[i]).pattern("¤#.#").build(); - assertEquals("₥Ƀ", ((BtcFixedFormat)mlff).symbol()); - assertEquals("Ƀ", mlff.coinSymbol()); - milli = mlff.format(valueOf(10000)); - assertTrue(milli.contains("₥Ƀ")); - assertFalse(milli.contains("฿")); - BtcFormat mcff = BtcFormat.builder().scale(6).locale(locales[i]).pattern("¤#.#").build(); - assertEquals("µɃ", ((BtcFixedFormat)mcff).symbol()); - assertEquals("Ƀ", mcff.coinSymbol()); - micro = mcff.format(valueOf(100)); - assertTrue(micro.contains("µɃ")); - assertFalse(micro.contains("฿")); - } - if (cs.contains("Ƀ")) { // NB: We don't know of any such existing locale, but check anyway. - BtcFormat bf = BtcFormat.getInstance(locales[i]); - String coin = bf.format(COIN); - assertTrue(coin.contains("฿")); - assertFalse(coin.contains("Ƀ")); - String milli = bf.format(valueOf(10000)); - assertTrue(milli.contains("₥฿")); - assertFalse(milli.contains("Ƀ")); - String micro = bf.format(valueOf(100)); - assertTrue(micro.contains("µ฿")); - assertFalse(micro.contains("Ƀ")); - } - } - } - - @Test - public void argumentTypeTest() { - BtcFormat usFormat = BtcFormat.getSymbolInstance(Locale.US); - // longs are tested above - // Coin - assertEquals("µ฿1,000,000.01", usFormat.format(COIN.add(valueOf(1)))); - // Integer - assertEquals("µ฿21,474,836.47" ,usFormat.format(Integer.MAX_VALUE)); - assertEquals("(µ฿21,474,836.48)" ,usFormat.format(Integer.MIN_VALUE)); - // Long - assertEquals("µ฿92,233,720,368,547,758.07" ,usFormat.format(Long.MAX_VALUE)); - assertEquals("(µ฿92,233,720,368,547,758.08)" ,usFormat.format(Long.MIN_VALUE)); - // BigInteger - assertEquals("µ฿0.10" ,usFormat.format(java.math.BigInteger.TEN)); - assertEquals("฿0.00" ,usFormat.format(java.math.BigInteger.ZERO)); - // BigDecimal - assertEquals("฿1.00" ,usFormat.format(java.math.BigDecimal.ONE)); - assertEquals("฿0.00" ,usFormat.format(java.math.BigDecimal.ZERO)); - // use of Double not encouraged but no way to stop user from converting one to BigDecimal - assertEquals( - "฿179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000.00", - usFormat.format(java.math.BigDecimal.valueOf(Double.MAX_VALUE))); - assertEquals("฿0.00", usFormat.format(java.math.BigDecimal.valueOf(Double.MIN_VALUE))); - assertEquals( - "฿340,282,346,638,528,860,000,000,000,000,000,000,000.00", - usFormat.format(java.math.BigDecimal.valueOf(Float.MAX_VALUE))); - // Bad type - try { - usFormat.format("1"); - fail("should not have tried to format a String"); - } catch (IllegalArgumentException e) { - } - } - - @Test - public void columnAlignmentTest() { - BtcFormat germany = BtcFormat.getCoinInstance(2,BtcFixedFormat.REPEATING_PLACES); - char separator = germany.symbols().getDecimalSeparator(); - Coin[] rows = {MAX_MONEY, MAX_MONEY.subtract(SATOSHI), Coin.parseCoin("1234"), - COIN, COIN.add(SATOSHI), COIN.subtract(SATOSHI), - COIN.divide(1000).add(SATOSHI), COIN.divide(1000), COIN.divide(1000).subtract(SATOSHI), - valueOf(100), valueOf(1000), valueOf(10000), - SATOSHI}; - FieldPosition fp = new FieldPosition(DECIMAL_SEPARATOR); - String[] output = new String[rows.length]; - int[] indexes = new int[rows.length]; - int maxIndex = 0; - for (int i = 0; i < rows.length; i++) { - output[i] = germany.format(rows[i], new StringBuffer(), fp).toString(); - indexes[i] = fp.getBeginIndex(); - if (indexes[i] > maxIndex) maxIndex = indexes[i]; - } - for (int i = 0; i < output.length; i++) { - // uncomment to watch printout - // System.out.println(repeat(" ", (maxIndex - indexes[i])) + output[i]); - assertEquals(output[i].indexOf(separator), indexes[i]); - } - } - - @Test - public void repeatingPlaceTest() { - BtcFormat mega = BtcFormat.getInstance(-6, US); - Coin value = MAX_MONEY.subtract(SATOSHI); - assertEquals("20.99999999999999", mega.format(value, 0, BtcFixedFormat.REPEATING_PLACES)); - assertEquals("20.99999999999999", mega.format(value, 0, BtcFixedFormat.REPEATING_PLACES)); - assertEquals("20.99999999999999", mega.format(value, 1, BtcFixedFormat.REPEATING_PLACES)); - assertEquals("20.99999999999999", mega.format(value, 2, BtcFixedFormat.REPEATING_PLACES)); - assertEquals("20.99999999999999", mega.format(value, 3, BtcFixedFormat.REPEATING_PLACES)); - assertEquals("20.99999999999999", mega.format(value, 0, BtcFixedFormat.REPEATING_DOUBLETS)); - assertEquals("20.99999999999999", mega.format(value, 1, BtcFixedFormat.REPEATING_DOUBLETS)); - assertEquals("20.99999999999999", mega.format(value, 2, BtcFixedFormat.REPEATING_DOUBLETS)); - assertEquals("20.99999999999999", mega.format(value, 3, BtcFixedFormat.REPEATING_DOUBLETS)); - assertEquals("20.99999999999999", mega.format(value, 0, BtcFixedFormat.REPEATING_TRIPLETS)); - assertEquals("20.99999999999999", mega.format(value, 1, BtcFixedFormat.REPEATING_TRIPLETS)); - assertEquals("20.99999999999999", mega.format(value, 2, BtcFixedFormat.REPEATING_TRIPLETS)); - assertEquals("20.99999999999999", mega.format(value, 3, BtcFixedFormat.REPEATING_TRIPLETS)); - assertEquals("1.00000005", BtcFormat.getCoinInstance(US). - format(COIN.add(Coin.valueOf(5)), 0, BtcFixedFormat.REPEATING_PLACES)); - } - - @Test - public void characterIteratorTest() { - BtcFormat usFormat = BtcFormat.getInstance(Locale.US); - AttributedCharacterIterator i = usFormat.formatToCharacterIterator(parseCoin("1234.5")); - java.util.Set a = i.getAllAttributeKeys(); - assertTrue("Missing currency attribute", a.contains(NumberFormat.Field.CURRENCY)); - assertTrue("Missing integer attribute", a.contains(NumberFormat.Field.INTEGER)); - assertTrue("Missing fraction attribute", a.contains(NumberFormat.Field.FRACTION)); - assertTrue("Missing decimal separator attribute", a.contains(NumberFormat.Field.DECIMAL_SEPARATOR)); - assertTrue("Missing grouping separator attribute", a.contains(NumberFormat.Field.GROUPING_SEPARATOR)); - assertTrue("Missing currency attribute", a.contains(NumberFormat.Field.CURRENCY)); - - char c; - i = BtcFormat.getCodeInstance(Locale.US).formatToCharacterIterator(new BigDecimal("0.19246362747414458")); - // formatted as "µBTC 192,463.63" - assertEquals(0, i.getBeginIndex()); - assertEquals(15, i.getEndIndex()); - int n = 0; - for(c = i.first(); i.getAttribute(NumberFormat.Field.CURRENCY) != null; c = i.next()) { - n++; - } - assertEquals(4, n); - n = 0; - for(i.next(); i.getAttribute(NumberFormat.Field.INTEGER) != null && i.getAttribute(NumberFormat.Field.GROUPING_SEPARATOR) != NumberFormat.Field.GROUPING_SEPARATOR; c = i.next()) { - n++; - } - assertEquals(3, n); - assertEquals(NumberFormat.Field.INTEGER, i.getAttribute(NumberFormat.Field.INTEGER)); - n = 0; - for(c = i.next(); i.getAttribute(NumberFormat.Field.INTEGER) != null; c = i.next()) { - n++; - } - assertEquals(3, n); - assertEquals(NumberFormat.Field.DECIMAL_SEPARATOR, i.getAttribute(NumberFormat.Field.DECIMAL_SEPARATOR)); - n = 0; - for(c = i.next(); c != CharacterIterator.DONE; c = i.next()) { - n++; - assertNotNull(i.getAttribute(NumberFormat.Field.FRACTION)); - } - assertEquals(2,n); - - // immutability check - BtcFormat fa = BtcFormat.getSymbolInstance(US); - BtcFormat fb = BtcFormat.getSymbolInstance(US); - assertEquals(fa, fb); - assertEquals(fa.hashCode(), fb.hashCode()); - fa.formatToCharacterIterator(COIN.multiply(1000000)); - assertEquals(fa, fb); - assertEquals(fa.hashCode(), fb.hashCode()); - fb.formatToCharacterIterator(COIN.divide(1000000)); - assertEquals(fa, fb); - assertEquals(fa.hashCode(), fb.hashCode()); - } - - @Test - public void parseTest() throws java.text.ParseException { - BtcFormat us = BtcFormat.getSymbolInstance(Locale.US); - BtcFormat usCoded = BtcFormat.getCodeInstance(Locale.US); - // Coins - assertEquals(valueOf(200000000), us.parseObject("BTC2")); - assertEquals(valueOf(200000000), us.parseObject("XBT2")); - assertEquals(valueOf(200000000), us.parseObject("฿2")); - assertEquals(valueOf(200000000), us.parseObject("Ƀ2")); - assertEquals(valueOf(200000000), us.parseObject("2")); - assertEquals(valueOf(200000000), usCoded.parseObject("BTC 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("XBT 2")); - assertEquals(valueOf(200000000), us.parseObject("฿2.0")); - assertEquals(valueOf(200000000), us.parseObject("Ƀ2.0")); - assertEquals(valueOf(200000000), us.parseObject("2.0")); - assertEquals(valueOf(200000000), us.parseObject("BTC2.0")); - assertEquals(valueOf(200000000), us.parseObject("XBT2.0")); - assertEquals(valueOf(200000000), usCoded.parseObject("฿ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("Ƀ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject(" 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("BTC 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("XBT 2")); - assertEquals(valueOf(202222420000000L), us.parseObject("2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("฿2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("Ƀ2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("BTC2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("XBT2,022,224.20")); - assertEquals(valueOf(220200000000L), us.parseObject("2,202.0")); - assertEquals(valueOf(2100000000000000L), us.parseObject("21000000.00000000")); - // MilliCoins - assertEquals(valueOf(200000), usCoded.parseObject("mBTC 2")); - assertEquals(valueOf(200000), usCoded.parseObject("mXBT 2")); - assertEquals(valueOf(200000), usCoded.parseObject("m฿ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("mɃ 2")); - assertEquals(valueOf(200000), us.parseObject("mBTC2")); - assertEquals(valueOf(200000), us.parseObject("mXBT2")); - assertEquals(valueOf(200000), us.parseObject("₥฿2")); - assertEquals(valueOf(200000), us.parseObject("₥Ƀ2")); - assertEquals(valueOf(200000), us.parseObject("₥2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥BTC 2.00")); - assertEquals(valueOf(200000), usCoded.parseObject("₥XBT 2.00")); - assertEquals(valueOf(200000), usCoded.parseObject("₥BTC 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥XBT 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥฿ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥Ƀ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥ 2")); - assertEquals(valueOf(202222400000L), us.parseObject("₥฿2,022,224")); - assertEquals(valueOf(202222420000L), us.parseObject("₥Ƀ2,022,224.20")); - assertEquals(valueOf(202222400000L), us.parseObject("m฿2,022,224")); - assertEquals(valueOf(202222420000L), us.parseObject("mɃ2,022,224.20")); - assertEquals(valueOf(202222400000L), us.parseObject("₥BTC2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("₥XBT2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("mBTC2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("mXBT2,022,224")); - assertEquals(valueOf(202222420000L), us.parseObject("₥2,022,224.20")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥฿ 2,022,224")); - assertEquals(valueOf(202222420000L), usCoded.parseObject("₥Ƀ 2,022,224.20")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("m฿ 2,022,224")); - assertEquals(valueOf(202222420000L), usCoded.parseObject("mɃ 2,022,224.20")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥BTC 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥XBT 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("mBTC 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("mXBT 2,022,224")); - assertEquals(valueOf(202222420000L), usCoded.parseObject("₥ 2,022,224.20")); - // Microcoins - assertEquals(valueOf(435), us.parseObject("µ฿4.35")); - assertEquals(valueOf(435), us.parseObject("uɃ4.35")); - assertEquals(valueOf(435), us.parseObject("u฿4.35")); - assertEquals(valueOf(435), us.parseObject("µɃ4.35")); - assertEquals(valueOf(435), us.parseObject("uBTC4.35")); - assertEquals(valueOf(435), us.parseObject("uXBT4.35")); - assertEquals(valueOf(435), us.parseObject("µBTC4.35")); - assertEquals(valueOf(435), us.parseObject("µXBT4.35")); - assertEquals(valueOf(435), usCoded.parseObject("uBTC 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("uXBT 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("µBTC 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("µXBT 4.35")); - // fractional satoshi; round up - assertEquals(valueOf(435), us.parseObject("uBTC4.345")); - assertEquals(valueOf(435), us.parseObject("uXBT4.345")); - // negative with mu symbol - assertEquals(valueOf(-1), usCoded.parseObject("(µ฿ 0.01)")); - assertEquals(valueOf(-10), us.parseObject("(µBTC0.100)")); - assertEquals(valueOf(-10), us.parseObject("(µXBT0.100)")); - - // Same thing with addition of custom code, symbol - us = BtcFormat.builder().locale(US).style(SYMBOL).symbol("£").code("XYZ").build(); - usCoded = BtcFormat.builder().locale(US).scale(0).symbol("£").code("XYZ"). - pattern("¤ #,##0.00").build(); - // Coins - assertEquals(valueOf(200000000), us.parseObject("XYZ2")); - assertEquals(valueOf(200000000), us.parseObject("BTC2")); - assertEquals(valueOf(200000000), us.parseObject("XBT2")); - assertEquals(valueOf(200000000), us.parseObject("£2")); - assertEquals(valueOf(200000000), us.parseObject("฿2")); - assertEquals(valueOf(200000000), us.parseObject("Ƀ2")); - assertEquals(valueOf(200000000), us.parseObject("2")); - assertEquals(valueOf(200000000), usCoded.parseObject("XYZ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("BTC 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("XBT 2")); - assertEquals(valueOf(200000000), us.parseObject("£2.0")); - assertEquals(valueOf(200000000), us.parseObject("฿2.0")); - assertEquals(valueOf(200000000), us.parseObject("Ƀ2.0")); - assertEquals(valueOf(200000000), us.parseObject("2.0")); - assertEquals(valueOf(200000000), us.parseObject("XYZ2.0")); - assertEquals(valueOf(200000000), us.parseObject("BTC2.0")); - assertEquals(valueOf(200000000), us.parseObject("XBT2.0")); - assertEquals(valueOf(200000000), usCoded.parseObject("£ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("฿ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("Ƀ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject(" 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("XYZ 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("BTC 2")); - assertEquals(valueOf(200000000), usCoded.parseObject("XBT 2")); - assertEquals(valueOf(202222420000000L), us.parseObject("2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("£2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("฿2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("Ƀ2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("XYZ2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("BTC2,022,224.20")); - assertEquals(valueOf(202222420000000L), us.parseObject("XBT2,022,224.20")); - assertEquals(valueOf(220200000000L), us.parseObject("2,202.0")); - assertEquals(valueOf(2100000000000000L), us.parseObject("21000000.00000000")); - // MilliCoins - assertEquals(valueOf(200000), usCoded.parseObject("mXYZ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("mBTC 2")); - assertEquals(valueOf(200000), usCoded.parseObject("mXBT 2")); - assertEquals(valueOf(200000), usCoded.parseObject("m£ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("m฿ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("mɃ 2")); - assertEquals(valueOf(200000), us.parseObject("mXYZ2")); - assertEquals(valueOf(200000), us.parseObject("mBTC2")); - assertEquals(valueOf(200000), us.parseObject("mXBT2")); - assertEquals(valueOf(200000), us.parseObject("₥£2")); - assertEquals(valueOf(200000), us.parseObject("₥฿2")); - assertEquals(valueOf(200000), us.parseObject("₥Ƀ2")); - assertEquals(valueOf(200000), us.parseObject("₥2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥XYZ 2.00")); - assertEquals(valueOf(200000), usCoded.parseObject("₥BTC 2.00")); - assertEquals(valueOf(200000), usCoded.parseObject("₥XBT 2.00")); - assertEquals(valueOf(200000), usCoded.parseObject("₥XYZ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥BTC 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥XBT 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥£ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥฿ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥Ƀ 2")); - assertEquals(valueOf(200000), usCoded.parseObject("₥ 2")); - assertEquals(valueOf(202222400000L), us.parseObject("₥£2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("₥฿2,022,224")); - assertEquals(valueOf(202222420000L), us.parseObject("₥Ƀ2,022,224.20")); - assertEquals(valueOf(202222400000L), us.parseObject("m£2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("m฿2,022,224")); - assertEquals(valueOf(202222420000L), us.parseObject("mɃ2,022,224.20")); - assertEquals(valueOf(202222400000L), us.parseObject("₥XYZ2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("₥BTC2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("₥XBT2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("mXYZ2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("mBTC2,022,224")); - assertEquals(valueOf(202222400000L), us.parseObject("mXBT2,022,224")); - assertEquals(valueOf(202222420000L), us.parseObject("₥2,022,224.20")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥£ 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥฿ 2,022,224")); - assertEquals(valueOf(202222420000L), usCoded.parseObject("₥Ƀ 2,022,224.20")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("m£ 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("m฿ 2,022,224")); - assertEquals(valueOf(202222420000L), usCoded.parseObject("mɃ 2,022,224.20")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥XYZ 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥BTC 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("₥XBT 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("mXYZ 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("mBTC 2,022,224")); - assertEquals(valueOf(202222400000L), usCoded.parseObject("mXBT 2,022,224")); - assertEquals(valueOf(202222420000L), usCoded.parseObject("₥ 2,022,224.20")); - // Microcoins - assertEquals(valueOf(435), us.parseObject("µ£4.35")); - assertEquals(valueOf(435), us.parseObject("µ฿4.35")); - assertEquals(valueOf(435), us.parseObject("uɃ4.35")); - assertEquals(valueOf(435), us.parseObject("u£4.35")); - assertEquals(valueOf(435), us.parseObject("u฿4.35")); - assertEquals(valueOf(435), us.parseObject("µɃ4.35")); - assertEquals(valueOf(435), us.parseObject("uXYZ4.35")); - assertEquals(valueOf(435), us.parseObject("uBTC4.35")); - assertEquals(valueOf(435), us.parseObject("uXBT4.35")); - assertEquals(valueOf(435), us.parseObject("µXYZ4.35")); - assertEquals(valueOf(435), us.parseObject("µBTC4.35")); - assertEquals(valueOf(435), us.parseObject("µXBT4.35")); - assertEquals(valueOf(435), usCoded.parseObject("uXYZ 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("uBTC 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("uXBT 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("µXYZ 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("µBTC 4.35")); - assertEquals(valueOf(435), usCoded.parseObject("µXBT 4.35")); - // fractional satoshi; round up - assertEquals(valueOf(435), us.parseObject("uXYZ4.345")); - assertEquals(valueOf(435), us.parseObject("uBTC4.345")); - assertEquals(valueOf(435), us.parseObject("uXBT4.345")); - // negative with mu symbol - assertEquals(valueOf(-1), usCoded.parseObject("µ£ -0.01")); - assertEquals(valueOf(-1), usCoded.parseObject("µ฿ -0.01")); - assertEquals(valueOf(-10), us.parseObject("(µXYZ0.100)")); - assertEquals(valueOf(-10), us.parseObject("(µBTC0.100)")); - assertEquals(valueOf(-10), us.parseObject("(µXBT0.100)")); - - // parse() method as opposed to parseObject - try { - BtcFormat.getInstance().parse("abc"); - fail("bad parse must raise exception"); - } catch (ParseException e) {} - } - - @Test - public void parseMetricTest() throws ParseException { - BtcFormat cp = BtcFormat.getCodeInstance(Locale.US); - BtcFormat sp = BtcFormat.getSymbolInstance(Locale.US); - // coin - assertEquals(parseCoin("1"), cp.parseObject("BTC 1.00")); - assertEquals(parseCoin("1"), sp.parseObject("BTC1.00")); - assertEquals(parseCoin("1"), cp.parseObject("฿ 1.00")); - assertEquals(parseCoin("1"), sp.parseObject("฿1.00")); - assertEquals(parseCoin("1"), cp.parseObject("B⃦ 1.00")); - assertEquals(parseCoin("1"), sp.parseObject("B⃦1.00")); - assertEquals(parseCoin("1"), cp.parseObject("Ƀ 1.00")); - assertEquals(parseCoin("1"), sp.parseObject("Ƀ1.00")); - // milli - assertEquals(parseCoin("0.001"), cp.parseObject("mBTC 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("mBTC1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("m฿ 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("m฿1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("mB⃦ 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("mB⃦1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("mɃ 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("mɃ1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("₥BTC 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("₥BTC1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("₥฿ 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("₥฿1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("₥B⃦ 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("₥B⃦1.00")); - assertEquals(parseCoin("0.001"), cp.parseObject("₥Ƀ 1.00")); - assertEquals(parseCoin("0.001"), sp.parseObject("₥Ƀ1.00")); - // micro - assertEquals(parseCoin("0.000001"), cp.parseObject("uBTC 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("uBTC1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("u฿ 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("u฿1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("uB⃦ 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("uB⃦1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("uɃ 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("uɃ1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("µBTC 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("µBTC1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("µ฿ 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("µ฿1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("µB⃦ 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("µB⃦1.00")); - assertEquals(parseCoin("0.000001"), cp.parseObject("µɃ 1.00")); - assertEquals(parseCoin("0.000001"), sp.parseObject("µɃ1.00")); - // satoshi - assertEquals(parseCoin("0.00000001"), cp.parseObject("uBTC 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("uBTC0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("u฿ 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("u฿0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("uB⃦ 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("uB⃦0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("uɃ 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("uɃ0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("µBTC 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("µBTC0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("µ฿ 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("µ฿0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("µB⃦ 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("µB⃦0.01")); - assertEquals(parseCoin("0.00000001"), cp.parseObject("µɃ 0.01")); - assertEquals(parseCoin("0.00000001"), sp.parseObject("µɃ0.01")); - // cents - assertEquals(parseCoin("0.01234567"), cp.parseObject("cBTC 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("cBTC1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("c฿ 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("c฿1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("cB⃦ 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("cB⃦1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("cɃ 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("cɃ1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("¢BTC 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("¢BTC1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("¢฿ 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("¢฿1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("¢B⃦ 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("¢B⃦1.234567")); - assertEquals(parseCoin("0.01234567"), cp.parseObject("¢Ƀ 1.234567")); - assertEquals(parseCoin("0.01234567"), sp.parseObject("¢Ƀ1.234567")); - // dekacoins - assertEquals(parseCoin("12.34567"), cp.parseObject("daBTC 1.234567")); - assertEquals(parseCoin("12.34567"), sp.parseObject("daBTC1.234567")); - assertEquals(parseCoin("12.34567"), cp.parseObject("da฿ 1.234567")); - assertEquals(parseCoin("12.34567"), sp.parseObject("da฿1.234567")); - assertEquals(parseCoin("12.34567"), cp.parseObject("daB⃦ 1.234567")); - assertEquals(parseCoin("12.34567"), sp.parseObject("daB⃦1.234567")); - assertEquals(parseCoin("12.34567"), cp.parseObject("daɃ 1.234567")); - assertEquals(parseCoin("12.34567"), sp.parseObject("daɃ1.234567")); - // hectocoins - assertEquals(parseCoin("123.4567"), cp.parseObject("hBTC 1.234567")); - assertEquals(parseCoin("123.4567"), sp.parseObject("hBTC1.234567")); - assertEquals(parseCoin("123.4567"), cp.parseObject("h฿ 1.234567")); - assertEquals(parseCoin("123.4567"), sp.parseObject("h฿1.234567")); - assertEquals(parseCoin("123.4567"), cp.parseObject("hB⃦ 1.234567")); - assertEquals(parseCoin("123.4567"), sp.parseObject("hB⃦1.234567")); - assertEquals(parseCoin("123.4567"), cp.parseObject("hɃ 1.234567")); - assertEquals(parseCoin("123.4567"), sp.parseObject("hɃ1.234567")); - // kilocoins - assertEquals(parseCoin("1234.567"), cp.parseObject("kBTC 1.234567")); - assertEquals(parseCoin("1234.567"), sp.parseObject("kBTC1.234567")); - assertEquals(parseCoin("1234.567"), cp.parseObject("k฿ 1.234567")); - assertEquals(parseCoin("1234.567"), sp.parseObject("k฿1.234567")); - assertEquals(parseCoin("1234.567"), cp.parseObject("kB⃦ 1.234567")); - assertEquals(parseCoin("1234.567"), sp.parseObject("kB⃦1.234567")); - assertEquals(parseCoin("1234.567"), cp.parseObject("kɃ 1.234567")); - assertEquals(parseCoin("1234.567"), sp.parseObject("kɃ1.234567")); - // megacoins - assertEquals(parseCoin("1234567"), cp.parseObject("MBTC 1.234567")); - assertEquals(parseCoin("1234567"), sp.parseObject("MBTC1.234567")); - assertEquals(parseCoin("1234567"), cp.parseObject("M฿ 1.234567")); - assertEquals(parseCoin("1234567"), sp.parseObject("M฿1.234567")); - assertEquals(parseCoin("1234567"), cp.parseObject("MB⃦ 1.234567")); - assertEquals(parseCoin("1234567"), sp.parseObject("MB⃦1.234567")); - assertEquals(parseCoin("1234567"), cp.parseObject("MɃ 1.234567")); - assertEquals(parseCoin("1234567"), sp.parseObject("MɃ1.234567")); - } - - @Test - public void parsePositionTest() { - BtcFormat usCoded = BtcFormat.getCodeInstance(Locale.US); - // Test the field constants - FieldPosition intField = new FieldPosition(NumberFormat.Field.INTEGER); - assertEquals( - "987,654,321", - usCoded.format(valueOf(98765432123L), new StringBuffer(), intField). - substring(intField.getBeginIndex(), intField.getEndIndex()) - ); - FieldPosition fracField = new FieldPosition(NumberFormat.Field.FRACTION); - assertEquals( - "23", - usCoded.format(valueOf(98765432123L), new StringBuffer(), fracField). - substring(fracField.getBeginIndex(), fracField.getEndIndex()) - ); - - // for currency we use a locale that puts the units at the end - BtcFormat de = BtcFormat.getSymbolInstance(Locale.GERMANY); - BtcFormat deCoded = BtcFormat.getCodeInstance(Locale.GERMANY); - FieldPosition currField = new FieldPosition(NumberFormat.Field.CURRENCY); - assertEquals( - "µ฿", - de.format(valueOf(98765432123L), new StringBuffer(), currField). - substring(currField.getBeginIndex(), currField.getEndIndex()) - ); - assertEquals( - "µBTC", - deCoded.format(valueOf(98765432123L), new StringBuffer(), currField). - substring(currField.getBeginIndex(), currField.getEndIndex()) - ); - assertEquals( - "₥฿", - de.format(valueOf(98765432000L), new StringBuffer(), currField). - substring(currField.getBeginIndex(), currField.getEndIndex()) - ); - assertEquals( - "mBTC", - deCoded.format(valueOf(98765432000L), new StringBuffer(), currField). - substring(currField.getBeginIndex(), currField.getEndIndex()) - ); - assertEquals( - "฿", - de.format(valueOf(98765000000L), new StringBuffer(), currField). - substring(currField.getBeginIndex(), currField.getEndIndex()) - ); - assertEquals( - "BTC", - deCoded.format(valueOf(98765000000L), new StringBuffer(), currField). - substring(currField.getBeginIndex(), currField.getEndIndex()) - ); - } - - @Test - public void currencyCodeTest() { - /* Insert needed space AFTER currency-code */ - BtcFormat usCoded = BtcFormat.getCodeInstance(Locale.US); - assertEquals("µBTC 0.01", usCoded.format(1)); - assertEquals("BTC 1.00", usCoded.format(COIN)); - - /* Do not insert unneeded space BEFORE currency-code */ - BtcFormat frCoded = BtcFormat.getCodeInstance(Locale.FRANCE); - assertEquals("0,01 µBTC", frCoded.format(1)); - assertEquals("1,00 BTC", frCoded.format(COIN)); - - /* Insert needed space BEFORE currency-code: no known currency pattern does this? */ - - /* Do not insert unneeded space AFTER currency-code */ - BtcFormat deCoded = BtcFormat.getCodeInstance(Locale.ITALY); - assertEquals("µBTC 0,01", deCoded.format(1)); - assertEquals("BTC 1,00", deCoded.format(COIN)); - } - - @Test - public void coinScaleTest() throws Exception { - BtcFormat coinFormat = BtcFormat.getCoinInstance(Locale.US); - assertEquals("1.00", coinFormat.format(Coin.COIN)); - assertEquals("-1.00", coinFormat.format(Coin.COIN.negate())); - assertEquals(Coin.parseCoin("1"), coinFormat.parseObject("1.00")); - assertEquals(valueOf(1000000), coinFormat.parseObject("0.01")); - assertEquals(Coin.parseCoin("1000"), coinFormat.parseObject("1,000.00")); - assertEquals(Coin.parseCoin("1000"), coinFormat.parseObject("1000")); - } - - @Test - public void millicoinScaleTest() throws Exception { - BtcFormat coinFormat = BtcFormat.getMilliInstance(Locale.US); - assertEquals("1,000.00", coinFormat.format(Coin.COIN)); - assertEquals("-1,000.00", coinFormat.format(Coin.COIN.negate())); - assertEquals(Coin.parseCoin("0.001"), coinFormat.parseObject("1.00")); - assertEquals(valueOf(1000), coinFormat.parseObject("0.01")); - assertEquals(Coin.parseCoin("1"), coinFormat.parseObject("1,000.00")); - assertEquals(Coin.parseCoin("1"), coinFormat.parseObject("1000")); - } - - @Test - public void microcoinScaleTest() throws Exception { - BtcFormat coinFormat = BtcFormat.getMicroInstance(Locale.US); - assertEquals("1,000,000.00", coinFormat.format(Coin.COIN)); - assertEquals("-1,000,000.00", coinFormat.format(Coin.COIN.negate())); - assertEquals("1,000,000.10", coinFormat.format(Coin.COIN.add(valueOf(10)))); - assertEquals(Coin.parseCoin("0.000001"), coinFormat.parseObject("1.00")); - assertEquals(valueOf(1), coinFormat.parseObject("0.01")); - assertEquals(Coin.parseCoin("0.001"), coinFormat.parseObject("1,000.00")); - assertEquals(Coin.parseCoin("0.001"), coinFormat.parseObject("1000")); - } - - @Test - public void testGrouping() throws Exception { - BtcFormat usCoin = BtcFormat.getInstance(0, Locale.US, 1, 2, 3); - assertEquals("0.1", usCoin.format(Coin.parseCoin("0.1"))); - assertEquals("0.010", usCoin.format(Coin.parseCoin("0.01"))); - assertEquals("0.001", usCoin.format(Coin.parseCoin("0.001"))); - assertEquals("0.000100", usCoin.format(Coin.parseCoin("0.0001"))); - assertEquals("0.000010", usCoin.format(Coin.parseCoin("0.00001"))); - assertEquals("0.000001", usCoin.format(Coin.parseCoin("0.000001"))); - - // no more than two fractional decimal places for the default coin-denomination - assertEquals("0.01", BtcFormat.getCoinInstance(Locale.US).format(Coin.parseCoin("0.005"))); - - BtcFormat usMilli = BtcFormat.getInstance(3, Locale.US, 1, 2, 3); - assertEquals("0.1", usMilli.format(Coin.parseCoin("0.0001"))); - assertEquals("0.010", usMilli.format(Coin.parseCoin("0.00001"))); - assertEquals("0.001", usMilli.format(Coin.parseCoin("0.000001"))); - // even though last group is 3, that would result in fractional satoshis, which we don't do - assertEquals("0.00010", usMilli.format(Coin.valueOf(10))); - assertEquals("0.00001", usMilli.format(Coin.valueOf(1))); - - BtcFormat usMicro = BtcFormat.getInstance(6, Locale.US, 1, 2, 3); - assertEquals("0.1", usMicro.format(Coin.valueOf(10))); - // even though second group is 2, that would result in fractional satoshis, which we don't do - assertEquals("0.01", usMicro.format(Coin.valueOf(1))); - } - - - /* These just make sure factory methods don't raise exceptions. - * Other tests inspect their return values. */ - @Test - public void factoryTest() { - BtcFormat coded = BtcFormat.getInstance(0, 1, 2, 3); - BtcFormat.getInstance(BtcAutoFormat.Style.CODE); - BtcAutoFormat symbolic = (BtcAutoFormat)BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL); - assertEquals(2, symbolic.fractionPlaces()); - BtcFormat.getInstance(BtcAutoFormat.Style.CODE, 3); - assertEquals(3, ((BtcAutoFormat)BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL, 3)).fractionPlaces()); - BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL, Locale.US, 3); - BtcFormat.getInstance(BtcAutoFormat.Style.CODE, Locale.US); - BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL, Locale.US); - BtcFormat.getCoinInstance(2, BtcFixedFormat.REPEATING_PLACES); - BtcFormat.getMilliInstance(1, 2, 3); - BtcFormat.getInstance(2); - BtcFormat.getInstance(2, Locale.US); - BtcFormat.getCodeInstance(3); - BtcFormat.getSymbolInstance(3); - BtcFormat.getCodeInstance(Locale.US, 3); - BtcFormat.getSymbolInstance(Locale.US, 3); - try { - BtcFormat.getInstance(SMALLEST_UNIT_EXPONENT + 1); - fail("should not have constructed an instance with denomination less than satoshi"); - } catch (IllegalArgumentException e) {} - } - @Test - public void factoryArgumentsTest() { - Locale locale; - if (Locale.getDefault().equals(GERMANY)) locale = FRANCE; - else locale = GERMANY; - assertEquals(BtcFormat.getInstance(), BtcFormat.getCodeInstance()); - assertEquals(BtcFormat.getInstance(locale), BtcFormat.getCodeInstance(locale)); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.CODE), BtcFormat.getCodeInstance()); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL), BtcFormat.getSymbolInstance()); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.CODE,3), BtcFormat.getCodeInstance(3)); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL,3), BtcFormat.getSymbolInstance(3)); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.CODE,locale), BtcFormat.getCodeInstance(locale)); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL,locale), BtcFormat.getSymbolInstance(locale)); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.CODE,locale,3), BtcFormat.getCodeInstance(locale,3)); - assertEquals(BtcFormat.getInstance(BtcAutoFormat.Style.SYMBOL,locale,3), BtcFormat.getSymbolInstance(locale,3)); - assertEquals(BtcFormat.getCoinInstance(), BtcFormat.getInstance(0)); - assertEquals(BtcFormat.getMilliInstance(), BtcFormat.getInstance(3)); - assertEquals(BtcFormat.getMicroInstance(), BtcFormat.getInstance(6)); - assertEquals(BtcFormat.getCoinInstance(3), BtcFormat.getInstance(0,3)); - assertEquals(BtcFormat.getMilliInstance(3), BtcFormat.getInstance(3,3)); - assertEquals(BtcFormat.getMicroInstance(3), BtcFormat.getInstance(6,3)); - assertEquals(BtcFormat.getCoinInstance(3,4,5), BtcFormat.getInstance(0,3,4,5)); - assertEquals(BtcFormat.getMilliInstance(3,4,5), BtcFormat.getInstance(3,3,4,5)); - assertEquals(BtcFormat.getMicroInstance(3,4,5), BtcFormat.getInstance(6,3,4,5)); - assertEquals(BtcFormat.getCoinInstance(locale), BtcFormat.getInstance(0,locale)); - assertEquals(BtcFormat.getMilliInstance(locale), BtcFormat.getInstance(3,locale)); - assertEquals(BtcFormat.getMicroInstance(locale), BtcFormat.getInstance(6,locale)); - assertEquals(BtcFormat.getCoinInstance(locale,4,5), BtcFormat.getInstance(0,locale,4,5)); - assertEquals(BtcFormat.getMilliInstance(locale,4,5), BtcFormat.getInstance(3,locale,4,5)); - assertEquals(BtcFormat.getMicroInstance(locale,4,5), BtcFormat.getInstance(6,locale,4,5)); - } - - @Test - public void autoDecimalTest() { - BtcFormat codedZero = BtcFormat.getCodeInstance(Locale.US, 0); - BtcFormat symbolZero = BtcFormat.getSymbolInstance(Locale.US, 0); - assertEquals("฿1", symbolZero.format(COIN)); - assertEquals("BTC 1", codedZero.format(COIN)); - assertEquals("µ฿1,000,000", symbolZero.format(COIN.subtract(SATOSHI))); - assertEquals("µBTC 1,000,000", codedZero.format(COIN.subtract(SATOSHI))); - assertEquals("µ฿1,000,000", symbolZero.format(COIN.subtract(Coin.valueOf(50)))); - assertEquals("µBTC 1,000,000", codedZero.format(COIN.subtract(Coin.valueOf(50)))); - assertEquals("µ฿999,999", symbolZero.format(COIN.subtract(Coin.valueOf(51)))); - assertEquals("µBTC 999,999", codedZero.format(COIN.subtract(Coin.valueOf(51)))); - assertEquals("฿1,000", symbolZero.format(COIN.multiply(1000))); - assertEquals("BTC 1,000", codedZero.format(COIN.multiply(1000))); - assertEquals("µ฿1", symbolZero.format(Coin.valueOf(100))); - assertEquals("µBTC 1", codedZero.format(Coin.valueOf(100))); - assertEquals("µ฿1", symbolZero.format(Coin.valueOf(50))); - assertEquals("µBTC 1", codedZero.format(Coin.valueOf(50))); - assertEquals("µ฿0", symbolZero.format(Coin.valueOf(49))); - assertEquals("µBTC 0", codedZero.format(Coin.valueOf(49))); - assertEquals("µ฿0", symbolZero.format(Coin.valueOf(1))); - assertEquals("µBTC 0", codedZero.format(Coin.valueOf(1))); - assertEquals("µ฿500,000", symbolZero.format(Coin.valueOf(49999999))); - assertEquals("µBTC 500,000", codedZero.format(Coin.valueOf(49999999))); - - assertEquals("µ฿499,500", symbolZero.format(Coin.valueOf(49950000))); - assertEquals("µBTC 499,500", codedZero.format(Coin.valueOf(49950000))); - assertEquals("µ฿499,500", symbolZero.format(Coin.valueOf(49949999))); - assertEquals("µBTC 499,500", codedZero.format(Coin.valueOf(49949999))); - assertEquals("µ฿500,490", symbolZero.format(Coin.valueOf(50049000))); - assertEquals("µBTC 500,490", codedZero.format(Coin.valueOf(50049000))); - assertEquals("µ฿500,490", symbolZero.format(Coin.valueOf(50049001))); - assertEquals("µBTC 500,490", codedZero.format(Coin.valueOf(50049001))); - assertEquals("µ฿500,000", symbolZero.format(Coin.valueOf(49999950))); - assertEquals("µBTC 500,000", codedZero.format(Coin.valueOf(49999950))); - assertEquals("µ฿499,999", symbolZero.format(Coin.valueOf(49999949))); - assertEquals("µBTC 499,999", codedZero.format(Coin.valueOf(49999949))); - assertEquals("µ฿500,000", symbolZero.format(Coin.valueOf(50000049))); - assertEquals("µBTC 500,000", codedZero.format(Coin.valueOf(50000049))); - assertEquals("µ฿500,001", symbolZero.format(Coin.valueOf(50000050))); - assertEquals("µBTC 500,001", codedZero.format(Coin.valueOf(50000050))); - - BtcFormat codedTwo = BtcFormat.getCodeInstance(Locale.US, 2); - BtcFormat symbolTwo = BtcFormat.getSymbolInstance(Locale.US, 2); - assertEquals("฿1.00", symbolTwo.format(COIN)); - assertEquals("BTC 1.00", codedTwo.format(COIN)); - assertEquals("µ฿999,999.99", symbolTwo.format(COIN.subtract(SATOSHI))); - assertEquals("µBTC 999,999.99", codedTwo.format(COIN.subtract(SATOSHI))); - assertEquals("฿1,000.00", symbolTwo.format(COIN.multiply(1000))); - assertEquals("BTC 1,000.00", codedTwo.format(COIN.multiply(1000))); - assertEquals("µ฿1.00", symbolTwo.format(Coin.valueOf(100))); - assertEquals("µBTC 1.00", codedTwo.format(Coin.valueOf(100))); - assertEquals("µ฿0.50", symbolTwo.format(Coin.valueOf(50))); - assertEquals("µBTC 0.50", codedTwo.format(Coin.valueOf(50))); - assertEquals("µ฿0.49", symbolTwo.format(Coin.valueOf(49))); - assertEquals("µBTC 0.49", codedTwo.format(Coin.valueOf(49))); - assertEquals("µ฿0.01", symbolTwo.format(Coin.valueOf(1))); - assertEquals("µBTC 0.01", codedTwo.format(Coin.valueOf(1))); - - BtcFormat codedThree = BtcFormat.getCodeInstance(Locale.US, 3); - BtcFormat symbolThree = BtcFormat.getSymbolInstance(Locale.US, 3); - assertEquals("฿1.000", symbolThree.format(COIN)); - assertEquals("BTC 1.000", codedThree.format(COIN)); - assertEquals("µ฿999,999.99", symbolThree.format(COIN.subtract(SATOSHI))); - assertEquals("µBTC 999,999.99", codedThree.format(COIN.subtract(SATOSHI))); - assertEquals("฿1,000.000", symbolThree.format(COIN.multiply(1000))); - assertEquals("BTC 1,000.000", codedThree.format(COIN.multiply(1000))); - assertEquals("₥฿0.001", symbolThree.format(Coin.valueOf(100))); - assertEquals("mBTC 0.001", codedThree.format(Coin.valueOf(100))); - assertEquals("µ฿0.50", symbolThree.format(Coin.valueOf(50))); - assertEquals("µBTC 0.50", codedThree.format(Coin.valueOf(50))); - assertEquals("µ฿0.49", symbolThree.format(Coin.valueOf(49))); - assertEquals("µBTC 0.49", codedThree.format(Coin.valueOf(49))); - assertEquals("µ฿0.01", symbolThree.format(Coin.valueOf(1))); - assertEquals("µBTC 0.01", codedThree.format(Coin.valueOf(1))); - } - - - @Test - public void symbolsCodesTest() { - BtcFixedFormat coin = (BtcFixedFormat)BtcFormat.getCoinInstance(US); - assertEquals("BTC", coin.code()); - assertEquals("฿", coin.symbol()); - BtcFixedFormat cent = (BtcFixedFormat)BtcFormat.getInstance(2, US); - assertEquals("cBTC", cent.code()); - assertEquals("¢฿", cent.symbol()); - BtcFixedFormat milli = (BtcFixedFormat)BtcFormat.getInstance(3, US); - assertEquals("mBTC", milli.code()); - assertEquals("₥฿", milli.symbol()); - BtcFixedFormat micro = (BtcFixedFormat)BtcFormat.getInstance(6, US); - assertEquals("µBTC", micro.code()); - assertEquals("µ฿", micro.symbol()); - BtcFixedFormat deka = (BtcFixedFormat)BtcFormat.getInstance(-1, US); - assertEquals("daBTC", deka.code()); - assertEquals("da฿", deka.symbol()); - BtcFixedFormat hecto = (BtcFixedFormat)BtcFormat.getInstance(-2, US); - assertEquals("hBTC", hecto.code()); - assertEquals("h฿", hecto.symbol()); - BtcFixedFormat kilo = (BtcFixedFormat)BtcFormat.getInstance(-3, US); - assertEquals("kBTC", kilo.code()); - assertEquals("k฿", kilo.symbol()); - BtcFixedFormat mega = (BtcFixedFormat)BtcFormat.getInstance(-6, US); - assertEquals("MBTC", mega.code()); - assertEquals("M฿", mega.symbol()); - BtcFixedFormat noSymbol = (BtcFixedFormat)BtcFormat.getInstance(4, US); - try { - noSymbol.symbol(); - fail("non-standard denomination has no symbol()"); - } catch (IllegalStateException e) {} - try { - noSymbol.code(); - fail("non-standard denomination has no code()"); - } catch (IllegalStateException e) {} - - BtcFixedFormat symbolCoin = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(0). - symbol("B\u20e6").build(); - assertEquals("BTC", symbolCoin.code()); - assertEquals("B⃦", symbolCoin.symbol()); - BtcFixedFormat symbolCent = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(2). - symbol("B\u20e6").build(); - assertEquals("cBTC", symbolCent.code()); - assertEquals("¢B⃦", symbolCent.symbol()); - BtcFixedFormat symbolMilli = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(3). - symbol("B\u20e6").build(); - assertEquals("mBTC", symbolMilli.code()); - assertEquals("₥B⃦", symbolMilli.symbol()); - BtcFixedFormat symbolMicro = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(6). - symbol("B\u20e6").build(); - assertEquals("µBTC", symbolMicro.code()); - assertEquals("µB⃦", symbolMicro.symbol()); - BtcFixedFormat symbolDeka = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-1). - symbol("B\u20e6").build(); - assertEquals("daBTC", symbolDeka.code()); - assertEquals("daB⃦", symbolDeka.symbol()); - BtcFixedFormat symbolHecto = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-2). - symbol("B\u20e6").build(); - assertEquals("hBTC", symbolHecto.code()); - assertEquals("hB⃦", symbolHecto.symbol()); - BtcFixedFormat symbolKilo = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-3). - symbol("B\u20e6").build(); - assertEquals("kBTC", symbolKilo.code()); - assertEquals("kB⃦", symbolKilo.symbol()); - BtcFixedFormat symbolMega = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-6). - symbol("B\u20e6").build(); - assertEquals("MBTC", symbolMega.code()); - assertEquals("MB⃦", symbolMega.symbol()); - - BtcFixedFormat codeCoin = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(0). - code("XBT").build(); - assertEquals("XBT", codeCoin.code()); - assertEquals("฿", codeCoin.symbol()); - BtcFixedFormat codeCent = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(2). - code("XBT").build(); - assertEquals("cXBT", codeCent.code()); - assertEquals("¢฿", codeCent.symbol()); - BtcFixedFormat codeMilli = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(3). - code("XBT").build(); - assertEquals("mXBT", codeMilli.code()); - assertEquals("₥฿", codeMilli.symbol()); - BtcFixedFormat codeMicro = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(6). - code("XBT").build(); - assertEquals("µXBT", codeMicro.code()); - assertEquals("µ฿", codeMicro.symbol()); - BtcFixedFormat codeDeka = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-1). - code("XBT").build(); - assertEquals("daXBT", codeDeka.code()); - assertEquals("da฿", codeDeka.symbol()); - BtcFixedFormat codeHecto = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-2). - code("XBT").build(); - assertEquals("hXBT", codeHecto.code()); - assertEquals("h฿", codeHecto.symbol()); - BtcFixedFormat codeKilo = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-3). - code("XBT").build(); - assertEquals("kXBT", codeKilo.code()); - assertEquals("k฿", codeKilo.symbol()); - BtcFixedFormat codeMega = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-6). - code("XBT").build(); - assertEquals("MXBT", codeMega.code()); - assertEquals("M฿", codeMega.symbol()); - - BtcFixedFormat symbolCodeCoin = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(0). - symbol("B\u20e6").code("XBT").build(); - assertEquals("XBT", symbolCodeCoin.code()); - assertEquals("B⃦", symbolCodeCoin.symbol()); - BtcFixedFormat symbolCodeCent = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(2). - symbol("B\u20e6").code("XBT").build(); - assertEquals("cXBT", symbolCodeCent.code()); - assertEquals("¢B⃦", symbolCodeCent.symbol()); - BtcFixedFormat symbolCodeMilli = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(3). - symbol("B\u20e6").code("XBT").build(); - assertEquals("mXBT", symbolCodeMilli.code()); - assertEquals("₥B⃦", symbolCodeMilli.symbol()); - BtcFixedFormat symbolCodeMicro = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(6). - symbol("B\u20e6").code("XBT").build(); - assertEquals("µXBT", symbolCodeMicro.code()); - assertEquals("µB⃦", symbolCodeMicro.symbol()); - BtcFixedFormat symbolCodeDeka = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-1). - symbol("B\u20e6").code("XBT").build(); - assertEquals("daXBT", symbolCodeDeka.code()); - assertEquals("daB⃦", symbolCodeDeka.symbol()); - BtcFixedFormat symbolCodeHecto = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-2). - symbol("B\u20e6").code("XBT").build(); - assertEquals("hXBT", symbolCodeHecto.code()); - assertEquals("hB⃦", symbolCodeHecto.symbol()); - BtcFixedFormat symbolCodeKilo = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-3). - symbol("B\u20e6").code("XBT").build(); - assertEquals("kXBT", symbolCodeKilo.code()); - assertEquals("kB⃦", symbolCodeKilo.symbol()); - BtcFixedFormat symbolCodeMega = (BtcFixedFormat)BtcFormat.builder().locale(US).scale(-6). - symbol("B\u20e6").code("XBT").build(); - assertEquals("MXBT", symbolCodeMega.code()); - assertEquals("MB⃦", symbolCodeMega.symbol()); - } - - /* copied from CoinFormatTest.java and modified */ - @Test - public void parse() throws Exception { - BtcFormat coin = BtcFormat.getCoinInstance(Locale.US); - assertEquals(Coin.COIN, coin.parseObject("1")); - assertEquals(Coin.COIN, coin.parseObject("1.")); - assertEquals(Coin.COIN, coin.parseObject("1.0")); - assertEquals(Coin.COIN, BtcFormat.getCoinInstance(Locale.GERMANY).parseObject("1,0")); - assertEquals(Coin.COIN, coin.parseObject("01.0000000000")); - // TODO work with express positive sign - // assertEquals(Coin.COIN, coin.parseObject("+1.0")); - assertEquals(Coin.COIN.negate(), coin.parseObject("-1")); - assertEquals(Coin.COIN.negate(), coin.parseObject("-1.0")); - - assertEquals(Coin.CENT, coin.parseObject(".01")); - - BtcFormat milli = BtcFormat.getMilliInstance(Locale.US); - assertEquals(Coin.MILLICOIN, milli.parseObject("1")); - assertEquals(Coin.MILLICOIN, milli.parseObject("1.0")); - assertEquals(Coin.MILLICOIN, milli.parseObject("01.0000000000")); - // TODO work with express positive sign - //assertEquals(Coin.MILLICOIN, milli.parseObject("+1.0")); - assertEquals(Coin.MILLICOIN.negate(), milli.parseObject("-1")); - assertEquals(Coin.MILLICOIN.negate(), milli.parseObject("-1.0")); - - BtcFormat micro = BtcFormat.getMicroInstance(Locale.US); - assertEquals(Coin.MICROCOIN, micro.parseObject("1")); - assertEquals(Coin.MICROCOIN, micro.parseObject("1.0")); - assertEquals(Coin.MICROCOIN, micro.parseObject("01.0000000000")); - // TODO work with express positive sign - // assertEquals(Coin.MICROCOIN, micro.parseObject("+1.0")); - assertEquals(Coin.MICROCOIN.negate(), micro.parseObject("-1")); - assertEquals(Coin.MICROCOIN.negate(), micro.parseObject("-1.0")); - } - - /* Copied (and modified) from CoinFormatTest.java */ - @Test - public void btcRounding() throws Exception { - BtcFormat coinFormat = BtcFormat.getCoinInstance(Locale.US); - assertEquals("0", BtcFormat.getCoinInstance(Locale.US, 0).format(ZERO)); - assertEquals("0", coinFormat.format(ZERO, 0)); - assertEquals("0.00", BtcFormat.getCoinInstance(Locale.US, 2).format(ZERO)); - assertEquals("0.00", coinFormat.format(ZERO, 2)); - - assertEquals("1", BtcFormat.getCoinInstance(Locale.US, 0).format(COIN)); - assertEquals("1", coinFormat.format(COIN, 0)); - assertEquals("1.0", BtcFormat.getCoinInstance(Locale.US, 1).format(COIN)); - assertEquals("1.0", coinFormat.format(COIN, 1)); - assertEquals("1.00", BtcFormat.getCoinInstance(Locale.US, 2, 2).format(COIN)); - assertEquals("1.00", coinFormat.format(COIN, 2, 2)); - assertEquals("1.00", BtcFormat.getCoinInstance(Locale.US, 2, 2, 2).format(COIN)); - assertEquals("1.00", coinFormat.format(COIN, 2, 2, 2)); - assertEquals("1.00", BtcFormat.getCoinInstance(Locale.US, 2, 2, 2, 2).format(COIN)); - assertEquals("1.00", coinFormat.format(COIN, 2, 2, 2, 2)); - assertEquals("1.000", BtcFormat.getCoinInstance(Locale.US, 3).format(COIN)); - assertEquals("1.000", coinFormat.format(COIN, 3)); - assertEquals("1.0000", BtcFormat.getCoinInstance(US, 4).format(COIN)); - assertEquals("1.0000", coinFormat.format(COIN, 4)); - - final Coin justNot = COIN.subtract(SATOSHI); - assertEquals("1", BtcFormat.getCoinInstance(US, 0).format(justNot)); - assertEquals("1", coinFormat.format(justNot, 0)); - assertEquals("1.0", BtcFormat.getCoinInstance(US, 1).format(justNot)); - assertEquals("1.0", coinFormat.format(justNot, 1)); - final Coin justNotUnder = Coin.valueOf(99995000); - assertEquals("1.00", BtcFormat.getCoinInstance(US, 2, 2).format(justNot)); - assertEquals("1.00", coinFormat.format(justNot, 2, 2)); - assertEquals("1.00", BtcFormat.getCoinInstance(US, 2, 2).format(justNotUnder)); - assertEquals("1.00", coinFormat.format(justNotUnder, 2, 2)); - assertEquals("1.00", BtcFormat.getCoinInstance(US, 2, 2, 2).format(justNot)); - assertEquals("1.00", coinFormat.format(justNot, 2, 2, 2)); - assertEquals("0.999950", BtcFormat.getCoinInstance(US, 2, 2, 2).format(justNotUnder)); - assertEquals("0.999950", coinFormat.format(justNotUnder, 2, 2, 2)); - assertEquals("0.99999999", BtcFormat.getCoinInstance(US, 2, 2, 2, 2).format(justNot)); - assertEquals("0.99999999", coinFormat.format(justNot, 2, 2, 2, 2)); - assertEquals("0.99999999", BtcFormat.getCoinInstance(US, 2, REPEATING_DOUBLETS).format(justNot)); - assertEquals("0.99999999", coinFormat.format(justNot, 2, REPEATING_DOUBLETS)); - assertEquals("0.999950", BtcFormat.getCoinInstance(US, 2, 2, 2, 2).format(justNotUnder)); - assertEquals("0.999950", coinFormat.format(justNotUnder, 2, 2, 2, 2)); - assertEquals("0.999950", BtcFormat.getCoinInstance(US, 2, REPEATING_DOUBLETS).format(justNotUnder)); - assertEquals("0.999950", coinFormat.format(justNotUnder, 2, REPEATING_DOUBLETS)); - assertEquals("1.000", BtcFormat.getCoinInstance(US, 3).format(justNot)); - assertEquals("1.000", coinFormat.format(justNot, 3)); - assertEquals("1.0000", BtcFormat.getCoinInstance(US, 4).format(justNot)); - assertEquals("1.0000", coinFormat.format(justNot, 4)); - - final Coin slightlyMore = COIN.add(SATOSHI); - assertEquals("1", BtcFormat.getCoinInstance(US, 0).format(slightlyMore)); - assertEquals("1", coinFormat.format(slightlyMore, 0)); - assertEquals("1.0", BtcFormat.getCoinInstance(US, 1).format(slightlyMore)); - assertEquals("1.0", coinFormat.format(slightlyMore, 1)); - assertEquals("1.00", BtcFormat.getCoinInstance(US, 2, 2).format(slightlyMore)); - assertEquals("1.00", coinFormat.format(slightlyMore, 2, 2)); - assertEquals("1.00", BtcFormat.getCoinInstance(US, 2, 2, 2).format(slightlyMore)); - assertEquals("1.00", coinFormat.format(slightlyMore, 2, 2, 2)); - assertEquals("1.00000001", BtcFormat.getCoinInstance(US, 2, 2, 2, 2).format(slightlyMore)); - assertEquals("1.00000001", coinFormat.format(slightlyMore, 2, 2, 2, 2)); - assertEquals("1.00000001", BtcFormat.getCoinInstance(US, 2, REPEATING_DOUBLETS).format(slightlyMore)); - assertEquals("1.00000001", coinFormat.format(slightlyMore, 2, REPEATING_DOUBLETS)); - assertEquals("1.000", BtcFormat.getCoinInstance(US, 3).format(slightlyMore)); - assertEquals("1.000", coinFormat.format(slightlyMore, 3)); - assertEquals("1.0000", BtcFormat.getCoinInstance(US, 4).format(slightlyMore)); - assertEquals("1.0000", coinFormat.format(slightlyMore, 4)); - - final Coin pivot = COIN.add(SATOSHI.multiply(5)); - assertEquals("1.00000005", BtcFormat.getCoinInstance(US, 8).format(pivot)); - assertEquals("1.00000005", coinFormat.format(pivot, 8)); - assertEquals("1.00000005", BtcFormat.getCoinInstance(US, 7, 1).format(pivot)); - assertEquals("1.00000005", coinFormat.format(pivot, 7, 1)); - assertEquals("1.0000001", BtcFormat.getCoinInstance(US, 7).format(pivot)); - assertEquals("1.0000001", coinFormat.format(pivot, 7)); - - final Coin value = Coin.valueOf(1122334455667788l); - assertEquals("11,223,345", BtcFormat.getCoinInstance(US, 0).format(value)); - assertEquals("11,223,345", coinFormat.format(value, 0)); - assertEquals("11,223,344.6", BtcFormat.getCoinInstance(US, 1).format(value)); - assertEquals("11,223,344.6", coinFormat.format(value, 1)); - assertEquals("11,223,344.5567", BtcFormat.getCoinInstance(US, 2, 2).format(value)); - assertEquals("11,223,344.5567", coinFormat.format(value, 2, 2)); - assertEquals("11,223,344.556678", BtcFormat.getCoinInstance(US, 2, 2, 2).format(value)); - assertEquals("11,223,344.556678", coinFormat.format(value, 2, 2, 2)); - assertEquals("11,223,344.55667788", BtcFormat.getCoinInstance(US, 2, 2, 2, 2).format(value)); - assertEquals("11,223,344.55667788", coinFormat.format(value, 2, 2, 2, 2)); - assertEquals("11,223,344.55667788", BtcFormat.getCoinInstance(US, 2, REPEATING_DOUBLETS).format(value)); - assertEquals("11,223,344.55667788", coinFormat.format(value, 2, REPEATING_DOUBLETS)); - assertEquals("11,223,344.557", BtcFormat.getCoinInstance(US, 3).format(value)); - assertEquals("11,223,344.557", coinFormat.format(value, 3)); - assertEquals("11,223,344.5567", BtcFormat.getCoinInstance(US, 4).format(value)); - assertEquals("11,223,344.5567", coinFormat.format(value, 4)); - - BtcFormat megaFormat = BtcFormat.getInstance(-6, US); - assertEquals("21.00", megaFormat.format(MAX_MONEY)); - assertEquals("21", megaFormat.format(MAX_MONEY, 0)); - assertEquals("11.22334455667788", megaFormat.format(value, 0, REPEATING_DOUBLETS)); - assertEquals("11.223344556677", megaFormat.format(Coin.valueOf(1122334455667700l), 0, REPEATING_DOUBLETS)); - assertEquals("11.22334455667788", megaFormat.format(value, 0, REPEATING_TRIPLETS)); - assertEquals("11.223344556677", megaFormat.format(Coin.valueOf(1122334455667700l), 0, REPEATING_TRIPLETS)); - } - - @Test - public void negativeTest() throws Exception { - assertEquals("-1,00 BTC", BtcFormat.getInstance(FRANCE).format(COIN.multiply(-1))); - assertEquals("BTC -1,00", BtcFormat.getInstance(ITALY).format(COIN.multiply(-1))); - assertEquals("฿ -1,00", BtcFormat.getSymbolInstance(ITALY).format(COIN.multiply(-1))); - assertEquals("BTC -1.00", BtcFormat.getInstance(JAPAN).format(COIN.multiply(-1))); - assertEquals("฿-1.00", BtcFormat.getSymbolInstance(JAPAN).format(COIN.multiply(-1))); - assertEquals("(BTC 1.00)", BtcFormat.getInstance(US).format(COIN.multiply(-1))); - assertEquals("(฿1.00)", BtcFormat.getSymbolInstance(US).format(COIN.multiply(-1))); - // assertEquals("BTC -१.००", BtcFormat.getInstance(Locale.forLanguageTag("hi-IN")).format(COIN.multiply(-1))); - assertEquals("BTC -๑.๐๐", BtcFormat.getInstance(new Locale("th","TH","TH")).format(COIN.multiply(-1))); - assertEquals("Ƀ-๑.๐๐", BtcFormat.getSymbolInstance(new Locale("th","TH","TH")).format(COIN.multiply(-1))); - } - - /* Warning: these tests assume the state of Locale data extant on the platform on which - * they were written: openjdk 7u21-2.3.9-5 */ - @Test - public void equalityTest() throws Exception { - // First, autodenominator - assertEquals(BtcFormat.getInstance(), BtcFormat.getInstance()); - assertEquals(BtcFormat.getInstance().hashCode(), BtcFormat.getInstance().hashCode()); - - assertNotEquals(BtcFormat.getCodeInstance(), BtcFormat.getSymbolInstance()); - assertNotEquals(BtcFormat.getCodeInstance().hashCode(), BtcFormat.getSymbolInstance().hashCode()); - - assertEquals(BtcFormat.getSymbolInstance(5), BtcFormat.getSymbolInstance(5)); - assertEquals(BtcFormat.getSymbolInstance(5).hashCode(), BtcFormat.getSymbolInstance(5).hashCode()); - - assertNotEquals(BtcFormat.getSymbolInstance(5), BtcFormat.getSymbolInstance(4)); - assertNotEquals(BtcFormat.getSymbolInstance(5).hashCode(), BtcFormat.getSymbolInstance(4).hashCode()); - - /* The underlying formatter is mutable, and its currency code - * and symbol may be reset each time a number is - * formatted or parsed. Here we check to make sure that state is - * ignored when comparing for equality */ - // when formatting - BtcAutoFormat a = (BtcAutoFormat)BtcFormat.getSymbolInstance(US); - BtcAutoFormat b = (BtcAutoFormat)BtcFormat.getSymbolInstance(US); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - a.format(COIN.multiply(1000000)); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - b.format(COIN.divide(1000000)); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - // when parsing - a = (BtcAutoFormat)BtcFormat.getSymbolInstance(US); - b = (BtcAutoFormat)BtcFormat.getSymbolInstance(US); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - a.parseObject("mBTC2"); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - b.parseObject("µ฿4.35"); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - - // FRANCE and GERMANY have different pattterns - assertNotEquals(BtcFormat.getInstance(FRANCE).hashCode(), BtcFormat.getInstance(GERMANY).hashCode()); - // TAIWAN and CHINA differ only in the Locale and Currency, i.e. the patterns and symbols are - // all the same (after setting the currency symbols to bitcoins) - assertNotEquals(BtcFormat.getInstance(TAIWAN), BtcFormat.getInstance(CHINA)); - // but they hash the same because of the DecimalFormatSymbols.hashCode() implementation - - assertEquals(BtcFormat.getSymbolInstance(4), BtcFormat.getSymbolInstance(4)); - assertEquals(BtcFormat.getSymbolInstance(4).hashCode(), BtcFormat.getSymbolInstance(4).hashCode()); - - assertNotEquals(BtcFormat.getSymbolInstance(4), BtcFormat.getSymbolInstance(5)); - assertNotEquals(BtcFormat.getSymbolInstance(4).hashCode(), BtcFormat.getSymbolInstance(5).hashCode()); - - // Fixed-denomination - assertEquals(BtcFormat.getCoinInstance(), BtcFormat.getCoinInstance()); - assertEquals(BtcFormat.getCoinInstance().hashCode(), BtcFormat.getCoinInstance().hashCode()); - - assertEquals(BtcFormat.getMilliInstance(), BtcFormat.getMilliInstance()); - assertEquals(BtcFormat.getMilliInstance().hashCode(), BtcFormat.getMilliInstance().hashCode()); - - assertEquals(BtcFormat.getMicroInstance(), BtcFormat.getMicroInstance()); - assertEquals(BtcFormat.getMicroInstance().hashCode(), BtcFormat.getMicroInstance().hashCode()); - - assertEquals(BtcFormat.getInstance(-6), BtcFormat.getInstance(-6)); - assertEquals(BtcFormat.getInstance(-6).hashCode(), BtcFormat.getInstance(-6).hashCode()); - - assertNotEquals(BtcFormat.getCoinInstance(), BtcFormat.getMilliInstance()); - assertNotEquals(BtcFormat.getCoinInstance().hashCode(), BtcFormat.getMilliInstance().hashCode()); - - assertNotEquals(BtcFormat.getCoinInstance(), BtcFormat.getMicroInstance()); - assertNotEquals(BtcFormat.getCoinInstance().hashCode(), BtcFormat.getMicroInstance().hashCode()); - - assertNotEquals(BtcFormat.getMilliInstance(), BtcFormat.getMicroInstance()); - assertNotEquals(BtcFormat.getMilliInstance().hashCode(), BtcFormat.getMicroInstance().hashCode()); - - assertNotEquals(BtcFormat.getInstance(SMALLEST_UNIT_EXPONENT), - BtcFormat.getInstance(SMALLEST_UNIT_EXPONENT - 1)); - assertNotEquals(BtcFormat.getInstance(SMALLEST_UNIT_EXPONENT).hashCode(), - BtcFormat.getInstance(SMALLEST_UNIT_EXPONENT - 1).hashCode()); - - assertNotEquals(BtcFormat.getCoinInstance(TAIWAN), BtcFormat.getCoinInstance(CHINA)); - - assertNotEquals(BtcFormat.getCoinInstance(2,3), BtcFormat.getCoinInstance(2,4)); - assertNotEquals(BtcFormat.getCoinInstance(2,3).hashCode(), BtcFormat.getCoinInstance(2,4).hashCode()); - - assertNotEquals(BtcFormat.getCoinInstance(2,3), BtcFormat.getCoinInstance(2,3,3)); - assertNotEquals(BtcFormat.getCoinInstance(2,3).hashCode(), BtcFormat.getCoinInstance(2,3,3).hashCode()); - - - } - - @Test - public void attributeTest() throws Exception { - String codePat = BtcFormat.getCodeInstance(Locale.US).pattern(); - assertTrue(codePat.contains("BTC") && ! codePat.contains("(^|[^฿])฿([^฿]|$)") && ! codePat.contains("(^|[^¤])¤([^¤]|$)")); - String symPat = BtcFormat.getSymbolInstance(Locale.US).pattern(); - assertTrue(symPat.contains("฿") && !symPat.contains("BTC") && !symPat.contains("¤¤")); - - assertEquals("BTC #,##0.00;(BTC #,##0.00)", BtcFormat.getCodeInstance(Locale.US).pattern()); - assertEquals("฿#,##0.00;(฿#,##0.00)", BtcFormat.getSymbolInstance(Locale.US).pattern()); - assertEquals('0', BtcFormat.getInstance(Locale.US).symbols().getZeroDigit()); - // assertEquals('०', BtcFormat.getInstance(Locale.forLanguageTag("hi-IN")).symbols().getZeroDigit()); - // TODO will this next line work with other JREs? - assertEquals('๐', BtcFormat.getInstance(new Locale("th","TH","TH")).symbols().getZeroDigit()); - } - - @Test - public void toStringTest() { - assertEquals("Auto-format ฿#,##0.00;(฿#,##0.00)", BtcFormat.getSymbolInstance(Locale.US).toString()); - assertEquals("Auto-format ฿#,##0.0000;(฿#,##0.0000)", BtcFormat.getSymbolInstance(Locale.US, 4).toString()); - assertEquals("Auto-format BTC #,##0.00;(BTC #,##0.00)", BtcFormat.getCodeInstance(Locale.US).toString()); - assertEquals("Auto-format BTC #,##0.0000;(BTC #,##0.0000)", BtcFormat.getCodeInstance(Locale.US, 4).toString()); - assertEquals("Coin-format #,##0.00", BtcFormat.getCoinInstance(Locale.US).toString()); - assertEquals("Millicoin-format #,##0.00", BtcFormat.getMilliInstance(Locale.US).toString()); - assertEquals("Microcoin-format #,##0.00", BtcFormat.getMicroInstance(Locale.US).toString()); - assertEquals("Coin-format #,##0.000", BtcFormat.getCoinInstance(Locale.US,3).toString()); - assertEquals("Coin-format #,##0.000(####)(#######)", BtcFormat.getCoinInstance(Locale.US,3,4,7).toString()); - assertEquals("Kilocoin-format #,##0.000", BtcFormat.getInstance(-3,Locale.US,3).toString()); - assertEquals("Kilocoin-format #,##0.000(####)(#######)", BtcFormat.getInstance(-3,Locale.US,3,4,7).toString()); - assertEquals("Decicoin-format #,##0.000", BtcFormat.getInstance(1,Locale.US,3).toString()); - assertEquals("Decicoin-format #,##0.000(####)(#######)", BtcFormat.getInstance(1,Locale.US,3,4,7).toString()); - assertEquals("Dekacoin-format #,##0.000", BtcFormat.getInstance(-1,Locale.US,3).toString()); - assertEquals("Dekacoin-format #,##0.000(####)(#######)", BtcFormat.getInstance(-1,Locale.US,3,4,7).toString()); - assertEquals("Hectocoin-format #,##0.000", BtcFormat.getInstance(-2,Locale.US,3).toString()); - assertEquals("Hectocoin-format #,##0.000(####)(#######)", BtcFormat.getInstance(-2,Locale.US,3,4,7).toString()); - assertEquals("Megacoin-format #,##0.000", BtcFormat.getInstance(-6,Locale.US,3).toString()); - assertEquals("Megacoin-format #,##0.000(####)(#######)", BtcFormat.getInstance(-6,Locale.US,3,4,7).toString()); - assertEquals("Fixed (-4) format #,##0.000", BtcFormat.getInstance(-4,Locale.US,3).toString()); - assertEquals("Fixed (-4) format #,##0.000(####)", BtcFormat.getInstance(-4,Locale.US,3,4).toString()); - assertEquals("Fixed (-4) format #,##0.000(####)(#######)", - BtcFormat.getInstance(-4, Locale.US, 3, 4, 7).toString()); - - assertEquals("Auto-format ฿#,##0.00;(฿#,##0.00)", - BtcFormat.builder().style(SYMBOL).code("USD").locale(US).build().toString()); - assertEquals("Auto-format #.##0,00 $", - BtcFormat.builder().style(SYMBOL).symbol("$").locale(GERMANY).build().toString()); - assertEquals("Auto-format #.##0,0000 $", - BtcFormat.builder().style(SYMBOL).symbol("$").fractionDigits(4).locale(GERMANY).build().toString()); - assertEquals("Auto-format BTC#,00฿;BTC-#,00฿", - BtcFormat.builder().style(SYMBOL).locale(GERMANY).pattern("¤¤#¤").build().toString()); - assertEquals("Coin-format BTC#,00฿;BTC-#,00฿", - BtcFormat.builder().scale(0).locale(GERMANY).pattern("¤¤#¤").build().toString()); - assertEquals("Millicoin-format BTC#.00฿;BTC-#.00฿", - BtcFormat.builder().scale(3).locale(US).pattern("¤¤#¤").build().toString()); - } - - @Test - public void patternDecimalPlaces() { - /* The pattern format provided by DecimalFormat includes specification of fractional digits, - * but we ignore that because we have alternative mechanism for specifying that.. */ - BtcFormat f = BtcFormat.builder().locale(US).scale(3).pattern("¤¤ #.0").fractionDigits(3).build(); - assertEquals("Millicoin-format BTC #.000;BTC -#.000", f.toString()); - assertEquals("mBTC 1000.000", f.format(COIN)); - } - - @Test - public void builderTest() { - Locale locale; - if (Locale.getDefault().equals(GERMANY)) locale = FRANCE; - else locale = GERMANY; - - assertEquals(BtcFormat.builder().build(), BtcFormat.getCoinInstance()); - try { - BtcFormat.builder().scale(0).style(CODE); - fail("Invoking both scale() and style() on a Builder should raise exception"); - } catch (IllegalStateException e) {} - try { - BtcFormat.builder().style(CODE).scale(0); - fail("Invoking both style() and scale() on a Builder should raise exception"); - } catch (IllegalStateException e) {} - - BtcFormat built = BtcFormat.builder().style(BtcAutoFormat.Style.CODE).fractionDigits(4).build(); - assertEquals(built, BtcFormat.getCodeInstance(4)); - built = BtcFormat.builder().style(BtcAutoFormat.Style.SYMBOL).fractionDigits(4).build(); - assertEquals(built, BtcFormat.getSymbolInstance(4)); - - built = BtcFormat.builder().scale(0).build(); - assertEquals(built, BtcFormat.getCoinInstance()); - built = BtcFormat.builder().scale(3).build(); - assertEquals(built, BtcFormat.getMilliInstance()); - built = BtcFormat.builder().scale(6).build(); - assertEquals(built, BtcFormat.getMicroInstance()); - - built = BtcFormat.builder().locale(locale).scale(0).build(); - assertEquals(built, BtcFormat.getCoinInstance(locale)); - built = BtcFormat.builder().locale(locale).scale(3).build(); - assertEquals(built, BtcFormat.getMilliInstance(locale)); - built = BtcFormat.builder().locale(locale).scale(6).build(); - assertEquals(built, BtcFormat.getMicroInstance(locale)); - - built = BtcFormat.builder().minimumFractionDigits(3).scale(0).build(); - assertEquals(built, BtcFormat.getCoinInstance(3)); - built = BtcFormat.builder().minimumFractionDigits(3).scale(3).build(); - assertEquals(built, BtcFormat.getMilliInstance(3)); - built = BtcFormat.builder().minimumFractionDigits(3).scale(6).build(); - assertEquals(built, BtcFormat.getMicroInstance(3)); - - built = BtcFormat.builder().fractionGroups(3,4).scale(0).build(); - assertEquals(built, BtcFormat.getCoinInstance(2,3,4)); - built = BtcFormat.builder().fractionGroups(3,4).scale(3).build(); - assertEquals(built, BtcFormat.getMilliInstance(2,3,4)); - built = BtcFormat.builder().fractionGroups(3,4).scale(6).build(); - assertEquals(built, BtcFormat.getMicroInstance(2,3,4)); - - built = BtcFormat.builder().pattern("#,####.#").scale(6).locale(GERMANY).build(); - assertEquals("100.0000,00", built.format(COIN)); - built = BtcFormat.builder().pattern("#,####.#").scale(6).locale(GERMANY).build(); - assertEquals("-100.0000,00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().localizedPattern("#.####,#").scale(6).locale(GERMANY).build(); - assertEquals("100.0000,00", built.format(COIN)); - - built = BtcFormat.builder().pattern("¤#,####.#").style(CODE).locale(GERMANY).build(); - assertEquals("฿-1,00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("¤¤ #,####.#").style(SYMBOL).locale(GERMANY).build(); - assertEquals("BTC -1,00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("¤¤##,###.#").scale(3).locale(US).build(); - assertEquals("mBTC1,000.00", built.format(COIN)); - built = BtcFormat.builder().pattern("¤ ##,###.#").scale(3).locale(US).build(); - assertEquals("₥฿ 1,000.00", built.format(COIN)); - - try { - BtcFormat.builder().pattern("¤¤##,###.#").scale(4).locale(US).build().format(COIN); - fail("Pattern with currency sign and non-standard denomination should raise exception"); - } catch (IllegalStateException e) {} - - try { - BtcFormat.builder().localizedPattern("¤¤##,###.#").scale(4).locale(US).build().format(COIN); - fail("Localized pattern with currency sign and non-standard denomination should raise exception"); - } catch (IllegalStateException e) {} - - built = BtcFormat.builder().style(SYMBOL).symbol("B\u20e6").locale(US).build(); - assertEquals("B⃦1.00", built.format(COIN)); - built = BtcFormat.builder().style(CODE).code("XBT").locale(US).build(); - assertEquals("XBT 1.00", built.format(COIN)); - built = BtcFormat.builder().style(SYMBOL).symbol("$").locale(GERMANY).build(); - assertEquals("1,00 $", built.format(COIN)); - // Setting the currency code on a DecimalFormatSymbols object can affect the currency symbol. - built = BtcFormat.builder().style(SYMBOL).code("USD").locale(US).build(); - assertEquals("฿1.00", built.format(COIN)); - - built = BtcFormat.builder().style(SYMBOL).symbol("B\u20e6").locale(US).build(); - assertEquals("₥B⃦1.00", built.format(COIN.divide(1000))); - built = BtcFormat.builder().style(CODE).code("XBT").locale(US).build(); - assertEquals("mXBT 1.00", built.format(COIN.divide(1000))); - - built = BtcFormat.builder().style(SYMBOL).symbol("B\u20e6").locale(US).build(); - assertEquals("µB⃦1.00", built.format(valueOf(100))); - built = BtcFormat.builder().style(CODE).code("XBT").locale(US).build(); - assertEquals("µXBT 1.00", built.format(valueOf(100))); - - /* The prefix of a pattern can have number symbols in quotes. - * Make sure our custom negative-subpattern creator handles this. */ - built = BtcFormat.builder().pattern("'#'¤#0").scale(0).locale(US).build(); - assertEquals("#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("'#0'¤#0").scale(0).locale(US).build(); - assertEquals("#0฿-1.00", built.format(COIN.multiply(-1))); - // this is an escaped quote between two hash marks in one set of quotes, not - // two adjacent quote-enclosed hash-marks: - built = BtcFormat.builder().pattern("'#''#'¤#0").scale(0).locale(US).build(); - assertEquals("#'#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("'#0''#'¤#0").scale(0).locale(US).build(); - assertEquals("#0'#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("'#0#'¤#0").scale(0).locale(US).build(); - assertEquals("#0#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("'#0'E'#'¤#0").scale(0).locale(US).build(); - assertEquals("#0E#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("E'#0''#'¤#0").scale(0).locale(US).build(); - assertEquals("E#0'#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("E'#0#'¤#0").scale(0).locale(US).build(); - assertEquals("E#0#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("E'#0''''#'¤#0").scale(0).locale(US).build(); - assertEquals("E#0''#฿-1.00", built.format(COIN.multiply(-1))); - built = BtcFormat.builder().pattern("''#0").scale(0).locale(US).build(); - assertEquals("'-1.00", built.format(COIN.multiply(-1))); - - // immutability check for fixed-denomination formatters, w/ & w/o custom pattern - BtcFormat a = BtcFormat.builder().scale(3).build(); - BtcFormat b = BtcFormat.builder().scale(3).build(); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - a.format(COIN.multiply(1000000)); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - b.format(COIN.divide(1000000)); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - a = BtcFormat.builder().scale(3).pattern("¤#.#").build(); - b = BtcFormat.builder().scale(3).pattern("¤#.#").build(); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - a.format(COIN.multiply(1000000)); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - b.format(COIN.divide(1000000)); - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - - } - -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/utils/ExchangeRateTest.java b/core/src/test/java/com/dogecoin/dogecoinj/utils/ExchangeRateTest.java deleted file mode 100644 index 7a5d8355..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/utils/ExchangeRateTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.utils; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import com.dogecoin.dogecoinj.core.Coin; - -public class ExchangeRateTest { - - @Test - public void normalRate() throws Exception { - ExchangeRate rate = new ExchangeRate(Fiat.parseFiat("EUR", "500")); - assertEquals("0.5", rate.coinToFiat(Coin.MILLICOIN).toPlainString()); - assertEquals("0.002", rate.fiatToCoin(Fiat.parseFiat("EUR", "1")).toPlainString()); - } - - @Test - public void bigRate() throws Exception { - ExchangeRate rate = new ExchangeRate(Coin.parseCoin("0.0001"), Fiat.parseFiat("BYR", "5320387.3")); - assertEquals("53203873000", rate.coinToFiat(Coin.COIN).toPlainString()); - assertEquals("0", rate.fiatToCoin(Fiat.parseFiat("BYR", "1")).toPlainString()); // Tiny value! - } - - @Test - public void smallRate() throws Exception { - ExchangeRate rate = new ExchangeRate(Coin.parseCoin("1000"), Fiat.parseFiat("XXX", "0.0001")); - assertEquals("0", rate.coinToFiat(Coin.COIN).toPlainString()); // Tiny value! - assertEquals("10000000", rate.fiatToCoin(Fiat.parseFiat("XXX", "1")).toPlainString()); - } - - @Test(expected = IllegalArgumentException.class) - public void currencyCodeMismatch() throws Exception { - ExchangeRate rate = new ExchangeRate(Fiat.parseFiat("EUR", "500")); - rate.fiatToCoin(Fiat.parseFiat("USD", "1")); - } - - @Test(expected = ArithmeticException.class) - public void fiatToCoinTooLarge() throws Exception { - ExchangeRate rate = new ExchangeRate(Fiat.parseFiat("XXX", "1")); - rate.fiatToCoin(Fiat.parseFiat("XXX", "21000001")); - } - - @Test(expected = ArithmeticException.class) - public void fiatToCoinTooSmall() throws Exception { - ExchangeRate rate = new ExchangeRate(Fiat.parseFiat("XXX", "1")); - rate.fiatToCoin(Fiat.parseFiat("XXX", "-21000001")); - } - - @Test(expected = ArithmeticException.class) - public void coinToFiatTooLarge() throws Exception { - ExchangeRate rate = new ExchangeRate(Fiat.parseFiat("XXX", "1000000000")); - rate.coinToFiat(Coin.parseCoin("1000000")); - } - - @Test(expected = ArithmeticException.class) - public void coinToFiatTooSmall() throws Exception { - ExchangeRate rate = new ExchangeRate(Fiat.parseFiat("XXX", "1000000000")); - rate.coinToFiat(Coin.parseCoin("-1000000")); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/utils/ExponentialBackoffTest.java b/core/src/test/java/com/dogecoin/dogecoinj/utils/ExponentialBackoffTest.java deleted file mode 100644 index c59d6c7b..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/utils/ExponentialBackoffTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2013 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.dogecoin.dogecoinj.utils; - -import com.dogecoin.dogecoinj.core.Utils; - -import org.junit.Before; -import org.junit.Test; - -import java.util.PriorityQueue; - -import static org.junit.Assert.assertEquals; - -/** - */ -public class ExponentialBackoffTest { - private ExponentialBackoff.Params params; - private ExponentialBackoff backoff; - - @Before - public void setUp() { - Utils.setMockClock(System.currentTimeMillis() / 1000); - params = new ExponentialBackoff.Params(); - backoff = new ExponentialBackoff(params); - } - - @Test - public void testSuccess() { - assertEquals(Utils.currentTimeMillis(), backoff.getRetryTime()); - - backoff.trackFailure(); - backoff.trackFailure(); - backoff.trackSuccess(); - - assertEquals(Utils.currentTimeMillis(), backoff.getRetryTime()); - } - - @Test - public void testFailure() { - assertEquals(Utils.currentTimeMillis(), backoff.getRetryTime()); - - backoff.trackFailure(); - backoff.trackFailure(); - backoff.trackFailure(); - - assertEquals(Utils.currentTimeMillis() + 121, backoff.getRetryTime()); - } - - @Test - public void testInQueue() { - PriorityQueue queue = new PriorityQueue(); - ExponentialBackoff backoff1 = new ExponentialBackoff(params); - backoff.trackFailure(); - backoff.trackFailure(); - backoff1.trackFailure(); - backoff1.trackFailure(); - backoff1.trackFailure(); - queue.offer(backoff); - queue.offer(backoff1); - - assertEquals(queue.poll(), backoff); // The one with soonest retry time - assertEquals(queue.peek(), backoff1); - - queue.offer(backoff); - assertEquals(queue.poll(), backoff); // Still the same one - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/utils/FiatTest.java b/core/src/test/java/com/dogecoin/dogecoinj/utils/FiatTest.java deleted file mode 100644 index 3d5a5635..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/utils/FiatTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.utils; - -import static com.dogecoin.dogecoinj.utils.Fiat.parseFiat; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class FiatTest { - - @Test - public void testParseAndValueOf() { - assertEquals(Fiat.valueOf("EUR", 10000), parseFiat("EUR", "1")); - assertEquals(Fiat.valueOf("EUR", 100), parseFiat("EUR", "0.01")); - assertEquals(Fiat.valueOf("EUR", 1), parseFiat("EUR", "0.0001")); - assertEquals(Fiat.valueOf("EUR", -10000), parseFiat("EUR", "-1")); - } - - @Test - public void testToFriendlyString() { - assertEquals("1.00 EUR", parseFiat("EUR", "1").toFriendlyString()); - assertEquals("1.23 EUR", parseFiat("EUR", "1.23").toFriendlyString()); - assertEquals("0.0010 EUR", parseFiat("EUR", "0.001").toFriendlyString()); - assertEquals("-1.23 EUR", parseFiat("EUR", "-1.23").toFriendlyString()); - } - - @Test - public void testToPlainString() { - assertEquals("0.0015", Fiat.valueOf("EUR", 15).toPlainString()); - assertEquals("1.23", parseFiat("EUR", "1.23").toPlainString()); - - assertEquals("0.1", parseFiat("EUR", "0.1").toPlainString()); - assertEquals("1.1", parseFiat("EUR", "1.1").toPlainString()); - assertEquals("21.12", parseFiat("EUR", "21.12").toPlainString()); - assertEquals("321.123", parseFiat("EUR", "321.123").toPlainString()); - assertEquals("4321.1234", parseFiat("EUR", "4321.1234").toPlainString()); - - // check there are no trailing zeros - assertEquals("1", parseFiat("EUR", "1.0").toPlainString()); - assertEquals("2", parseFiat("EUR", "2.00").toPlainString()); - assertEquals("3", parseFiat("EUR", "3.000").toPlainString()); - assertEquals("4", parseFiat("EUR", "4.0000").toPlainString()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/utils/MonetaryFormatTest.java b/core/src/test/java/com/dogecoin/dogecoinj/utils/MonetaryFormatTest.java deleted file mode 100644 index a741139f..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/utils/MonetaryFormatTest.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.utils; - -import static com.dogecoin.dogecoinj.core.Coin.CENT; -import static com.dogecoin.dogecoinj.core.Coin.COIN; -import static com.dogecoin.dogecoinj.core.Coin.SATOSHI; -import static com.dogecoin.dogecoinj.core.Coin.ZERO; -import static org.junit.Assert.assertEquals; - -import java.util.Locale; - -import org.junit.Test; - -import com.dogecoin.dogecoinj.core.Coin; - -public class MonetaryFormatTest { - - private static final MonetaryFormat NO_CODE = MonetaryFormat.BTC.noCode(); - - @Test - public void testSigns() throws Exception { - assertEquals("-1.00", NO_CODE.format(Coin.COIN.negate()).toString()); - assertEquals("@1.00", NO_CODE.negativeSign('@').format(Coin.COIN.negate()).toString()); - assertEquals("1.00", NO_CODE.format(Coin.COIN).toString()); - assertEquals("+1.00", NO_CODE.positiveSign('+').format(Coin.COIN).toString()); - } - - @Test - public void testDigits() throws Exception { - assertEquals("١٢.٣٤٥٦٧٨٩٠", NO_CODE.digits('\u0660').format(Coin.valueOf(1234567890l)).toString()); - } - - @Test - public void testDecimalMark() throws Exception { - assertEquals("1.00", NO_CODE.format(Coin.COIN).toString()); - assertEquals("1,00", NO_CODE.decimalMark(',').format(Coin.COIN).toString()); - } - - @Test - public void testGrouping() throws Exception { - assertEquals("0.1", format(Coin.parseCoin("0.1"), 0, 1, 2, 3)); - assertEquals("0.010", format(Coin.parseCoin("0.01"), 0, 1, 2, 3)); - assertEquals("0.001", format(Coin.parseCoin("0.001"), 0, 1, 2, 3)); - assertEquals("0.000100", format(Coin.parseCoin("0.0001"), 0, 1, 2, 3)); - assertEquals("0.000010", format(Coin.parseCoin("0.00001"), 0, 1, 2, 3)); - assertEquals("0.000001", format(Coin.parseCoin("0.000001"), 0, 1, 2, 3)); - } - - @Test - public void btcRounding() throws Exception { - assertEquals("0", format(ZERO, 0, 0)); - assertEquals("0.00", format(ZERO, 0, 2)); - - assertEquals("1", format(COIN, 0, 0)); - assertEquals("1.0", format(COIN, 0, 1)); - assertEquals("1.00", format(COIN, 0, 2, 2)); - assertEquals("1.00", format(COIN, 0, 2, 2, 2)); - assertEquals("1.00", format(COIN, 0, 2, 2, 2, 2)); - assertEquals("1.000", format(COIN, 0, 3)); - assertEquals("1.0000", format(COIN, 0, 4)); - - final Coin justNot = COIN.subtract(SATOSHI); - assertEquals("1", format(justNot, 0, 0)); - assertEquals("1.0", format(justNot, 0, 1)); - assertEquals("1.00", format(justNot, 0, 2, 2)); - assertEquals("1.00", format(justNot, 0, 2, 2, 2)); - assertEquals("0.99999999", format(justNot, 0, 2, 2, 2, 2)); - assertEquals("1.000", format(justNot, 0, 3)); - assertEquals("1.0000", format(justNot, 0, 4)); - - final Coin slightlyMore = COIN.add(SATOSHI); - assertEquals("1", format(slightlyMore, 0, 0)); - assertEquals("1.0", format(slightlyMore, 0, 1)); - assertEquals("1.00", format(slightlyMore, 0, 2, 2)); - assertEquals("1.00", format(slightlyMore, 0, 2, 2, 2)); - assertEquals("1.00000001", format(slightlyMore, 0, 2, 2, 2, 2)); - assertEquals("1.000", format(slightlyMore, 0, 3)); - assertEquals("1.0000", format(slightlyMore, 0, 4)); - - final Coin pivot = COIN.add(SATOSHI.multiply(5)); - assertEquals("1.00000005", format(pivot, 0, 8)); - assertEquals("1.00000005", format(pivot, 0, 7, 1)); - assertEquals("1.0000001", format(pivot, 0, 7)); - - final Coin value = Coin.valueOf(1122334455667788l); - assertEquals("11223345", format(value, 0, 0)); - assertEquals("11223344.6", format(value, 0, 1)); - assertEquals("11223344.5567", format(value, 0, 2, 2)); - assertEquals("11223344.556678", format(value, 0, 2, 2, 2)); - assertEquals("11223344.55667788", format(value, 0, 2, 2, 2, 2)); - assertEquals("11223344.557", format(value, 0, 3)); - assertEquals("11223344.5567", format(value, 0, 4)); - } - - @Test - public void mBtcRounding() throws Exception { - assertEquals("0", format(ZERO, 3, 0)); - assertEquals("0.00", format(ZERO, 3, 2)); - - assertEquals("1000", format(COIN, 3, 0)); - assertEquals("1000.0", format(COIN, 3, 1)); - assertEquals("1000.00", format(COIN, 3, 2)); - assertEquals("1000.00", format(COIN, 3, 2, 2)); - assertEquals("1000.000", format(COIN, 3, 3)); - assertEquals("1000.0000", format(COIN, 3, 4)); - - final Coin justNot = COIN.subtract(SATOSHI.multiply(10)); - assertEquals("1000", format(justNot, 3, 0)); - assertEquals("1000.0", format(justNot, 3, 1)); - assertEquals("1000.00", format(justNot, 3, 2)); - assertEquals("999.9999", format(justNot, 3, 2, 2)); - assertEquals("1000.000", format(justNot, 3, 3)); - assertEquals("999.9999", format(justNot, 3, 4)); - - final Coin slightlyMore = COIN.add(SATOSHI.multiply(10)); - assertEquals("1000", format(slightlyMore, 3, 0)); - assertEquals("1000.0", format(slightlyMore, 3, 1)); - assertEquals("1000.00", format(slightlyMore, 3, 2)); - assertEquals("1000.000", format(slightlyMore, 3, 3)); - assertEquals("1000.0001", format(slightlyMore, 3, 2, 2)); - assertEquals("1000.0001", format(slightlyMore, 3, 4)); - - final Coin pivot = COIN.add(SATOSHI.multiply(50)); - assertEquals("1000.0005", format(pivot, 3, 4)); - assertEquals("1000.0005", format(pivot, 3, 3, 1)); - assertEquals("1000.001", format(pivot, 3, 3)); - - final Coin value = Coin.valueOf(1122334455667788l); - assertEquals("11223344557", format(value, 3, 0)); - assertEquals("11223344556.7", format(value, 3, 1)); - assertEquals("11223344556.68", format(value, 3, 2)); - assertEquals("11223344556.6779", format(value, 3, 2, 2)); - assertEquals("11223344556.678", format(value, 3, 3)); - assertEquals("11223344556.6779", format(value, 3, 4)); - } - - @Test - public void uBtcRounding() throws Exception { - assertEquals("0", format(ZERO, 6, 0)); - assertEquals("0.00", format(ZERO, 6, 2)); - - assertEquals("1000000", format(COIN, 6, 0)); - assertEquals("1000000", format(COIN, 6, 0, 2)); - assertEquals("1000000.0", format(COIN, 6, 1)); - assertEquals("1000000.00", format(COIN, 6, 2)); - - final Coin justNot = COIN.subtract(SATOSHI); - assertEquals("1000000", format(justNot, 6, 0)); - assertEquals("999999.99", format(justNot, 6, 0, 2)); - assertEquals("1000000.0", format(justNot, 6, 1)); - assertEquals("999999.99", format(justNot, 6, 2)); - - final Coin slightlyMore = COIN.add(SATOSHI); - assertEquals("1000000", format(slightlyMore, 6, 0)); - assertEquals("1000000.01", format(slightlyMore, 6, 0, 2)); - assertEquals("1000000.0", format(slightlyMore, 6, 1)); - assertEquals("1000000.01", format(slightlyMore, 6, 2)); - - final Coin pivot = COIN.add(SATOSHI.multiply(5)); - assertEquals("1000000.05", format(pivot, 6, 2)); - assertEquals("1000000.05", format(pivot, 6, 0, 2)); - assertEquals("1000000.1", format(pivot, 6, 1)); - assertEquals("1000000.1", format(pivot, 6, 0, 1)); - - final Coin value = Coin.valueOf(1122334455667788l); - assertEquals("11223344556678", format(value, 6, 0)); - assertEquals("11223344556677.88", format(value, 6, 2)); - assertEquals("11223344556677.9", format(value, 6, 1)); - assertEquals("11223344556677.88", format(value, 6, 2)); - } - - private String format(Coin coin, int shift, int minDecimals, int... decimalGroups) { - return NO_CODE.shift(shift).minDecimals(minDecimals).optionalDecimals(decimalGroups).format(coin).toString(); - } - - @Test - public void repeatOptionalDecimals() { - assertEquals("0.00000001", formatRepeat(SATOSHI, 2, 4)); - assertEquals("0.00000010", formatRepeat(SATOSHI.multiply(10), 2, 4)); - assertEquals("0.01", formatRepeat(CENT, 2, 4)); - assertEquals("0.10", formatRepeat(CENT.multiply(10), 2, 4)); - - assertEquals("0", formatRepeat(SATOSHI, 2, 2)); - assertEquals("0", formatRepeat(SATOSHI.multiply(10), 2, 2)); - assertEquals("0.01", formatRepeat(CENT, 2, 2)); - assertEquals("0.10", formatRepeat(CENT.multiply(10), 2, 2)); - - assertEquals("0", formatRepeat(CENT, 2, 0)); - assertEquals("0", formatRepeat(CENT.multiply(10), 2, 0)); - } - - private String formatRepeat(Coin coin, int decimals, int repetitions) { - return NO_CODE.minDecimals(0).repeatOptionalDecimals(decimals, repetitions).format(coin).toString(); - } - - @Test - public void standardCodes() throws Exception { - assertEquals("DOGE 0.00", MonetaryFormat.BTC.format(Coin.ZERO).toString()); - assertEquals("mDOGE 0.00", MonetaryFormat.MBTC.format(Coin.ZERO).toString()); - assertEquals("µDOGE 0", MonetaryFormat.UBTC.format(Coin.ZERO).toString()); - } - - @Test - public void customCode() throws Exception { - assertEquals("dDOGE 0", MonetaryFormat.UBTC.code(1, "dDOGE").shift(1).format(Coin.ZERO).toString()); - } - - @Test - public void codeOrientation() throws Exception { - assertEquals("DOGE 0.00", MonetaryFormat.BTC.prefixCode().format(Coin.ZERO).toString()); - assertEquals("0.00 DOGE", MonetaryFormat.BTC.postfixCode().format(Coin.ZERO).toString()); - } - - @Test - public void codeSeparator() throws Exception { - assertEquals("DOGE@0.00", MonetaryFormat.BTC.codeSeparator('@').format(Coin.ZERO).toString()); - } - - @Test(expected = NumberFormatException.class) - public void missingCode() throws Exception { - MonetaryFormat.UBTC.shift(1).format(Coin.ZERO); - } - - @Test - public void withLocale() throws Exception { - final Coin value = Coin.valueOf(-1234567890l); - assertEquals("-12.34567890", NO_CODE.withLocale(Locale.US).format(value).toString()); - assertEquals("-12,34567890", NO_CODE.withLocale(Locale.GERMANY).format(value).toString()); - assertEquals("-१२.३४५६७८९०", NO_CODE.withLocale(new Locale("hi", "IN")).format(value).toString()); // Devanagari - } - - @Test - public void parse() throws Exception { - assertEquals(Coin.COIN, NO_CODE.parse("1")); - assertEquals(Coin.COIN, NO_CODE.parse("1.")); - assertEquals(Coin.COIN, NO_CODE.parse("1.0")); - assertEquals(Coin.COIN, NO_CODE.decimalMark(',').parse("1,0")); - assertEquals(Coin.COIN, NO_CODE.parse("01.0000000000")); - assertEquals(Coin.COIN, NO_CODE.positiveSign('+').parse("+1.0")); - assertEquals(Coin.COIN.negate(), NO_CODE.parse("-1")); - assertEquals(Coin.COIN.negate(), NO_CODE.parse("-1.0")); - - assertEquals(Coin.CENT, NO_CODE.parse(".01")); - - assertEquals(Coin.MILLICOIN, MonetaryFormat.MBTC.parse("1")); - assertEquals(Coin.MILLICOIN, MonetaryFormat.MBTC.parse("1.0")); - assertEquals(Coin.MILLICOIN, MonetaryFormat.MBTC.parse("01.0000000000")); - assertEquals(Coin.MILLICOIN, MonetaryFormat.MBTC.positiveSign('+').parse("+1.0")); - assertEquals(Coin.MILLICOIN.negate(), MonetaryFormat.MBTC.parse("-1")); - assertEquals(Coin.MILLICOIN.negate(), MonetaryFormat.MBTC.parse("-1.0")); - - assertEquals(Coin.MICROCOIN, MonetaryFormat.UBTC.parse("1")); - assertEquals(Coin.MICROCOIN, MonetaryFormat.UBTC.parse("1.0")); - assertEquals(Coin.MICROCOIN, MonetaryFormat.UBTC.parse("01.0000000000")); - assertEquals(Coin.MICROCOIN, MonetaryFormat.UBTC.positiveSign('+').parse("+1.0")); - assertEquals(Coin.MICROCOIN.negate(), MonetaryFormat.UBTC.parse("-1")); - assertEquals(Coin.MICROCOIN.negate(), MonetaryFormat.UBTC.parse("-1.0")); - - assertEquals(Coin.CENT, NO_CODE.withLocale(new Locale("hi", "IN")).parse(".०१")); // Devanagari - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidEmpty() throws Exception { - NO_CODE.parse(""); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidWhitespaceBefore() throws Exception { - NO_CODE.parse(" 1"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidWhitespaceSign() throws Exception { - NO_CODE.parse("- 1"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidWhitespaceAfter() throws Exception { - NO_CODE.parse("1 "); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidMultipleDecimalMarks() throws Exception { - NO_CODE.parse("1.0.0"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidDecimalMark() throws Exception { - NO_CODE.decimalMark(',').parse("1.0"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidPositiveSign() throws Exception { - NO_CODE.positiveSign('@').parse("+1.0"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidNegativeSign() throws Exception { - NO_CODE.negativeSign('@').parse("-1.0"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidHugeNumber() throws Exception { - NO_CODE.parse("99999999999999999999"); - } - - @Test(expected = NumberFormatException.class) - public void parseInvalidHugeNegativeNumber() throws Exception { - NO_CODE.parse("-99999999999999999999"); - } - - private static final Fiat ONE_EURO = Fiat.parseFiat("EUR", "1"); - - @Test - public void fiat() throws Exception { - assertEquals(ONE_EURO, NO_CODE.parseFiat("EUR", "1")); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/wallet/BasicKeyChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/wallet/BasicKeyChainTest.java deleted file mode 100644 index fe5ffb41..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/wallet/BasicKeyChainTest.java +++ /dev/null @@ -1,274 +0,0 @@ -/** - * Copyright 2013 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.dogecoin.dogecoinj.wallet; - -import com.dogecoin.dogecoinj.core.BloomFilter; -import com.dogecoin.dogecoinj.core.ECKey; -import com.dogecoin.dogecoinj.core.Utils; -import com.dogecoin.dogecoinj.crypto.KeyCrypter; -import com.dogecoin.dogecoinj.crypto.KeyCrypterException; -import com.dogecoin.dogecoinj.crypto.KeyCrypterScrypt; -import com.dogecoin.dogecoinj.store.UnreadableWalletException; -import com.dogecoin.dogecoinj.utils.Threading; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.*; - -public class BasicKeyChainTest { - private BasicKeyChain chain; - private AtomicReference> onKeysAdded; - private AtomicBoolean onKeysAddedRan; - - @Before - public void setup() { - chain = new BasicKeyChain(); - onKeysAdded = new AtomicReference>(); - onKeysAddedRan = new AtomicBoolean(); - chain.addEventListener(new AbstractKeyChainEventListener() { - @Override - public void onKeysAdded(List keys2) { - onKeysAdded.set(keys2); - onKeysAddedRan.set(true); - } - }, Threading.SAME_THREAD); - } - - @Test - public void importKeys() { - long now = Utils.currentTimeSeconds(); - Utils.setMockClock(now); - final ECKey key1 = new ECKey(); - Utils.rollMockClock(86400); - final ECKey key2 = new ECKey(); - final ArrayList keys = Lists.newArrayList(key1, key2); - - // Import two keys, check the event is correct. - assertEquals(2, chain.importKeys(keys)); - assertEquals(2, chain.numKeys()); - assertTrue(onKeysAddedRan.getAndSet(false)); - assertArrayEquals(keys.toArray(), onKeysAdded.get().toArray()); - assertEquals(now, chain.getEarliestKeyCreationTime()); - // Check we ignore duplicates. - final ECKey newKey = new ECKey(); - keys.add(newKey); - assertEquals(1, chain.importKeys(keys)); - assertTrue(onKeysAddedRan.getAndSet(false)); - assertEquals(newKey, onKeysAdded.getAndSet(null).get(0)); - assertEquals(0, chain.importKeys(keys)); - assertFalse(onKeysAddedRan.getAndSet(false)); - assertNull(onKeysAdded.get()); - - assertTrue(chain.hasKey(key1)); - assertTrue(chain.hasKey(key2)); - assertEquals(key1, chain.findKeyFromPubHash(key1.getPubKeyHash())); - assertEquals(key2, chain.findKeyFromPubKey(key2.getPubKey())); - assertNull(chain.findKeyFromPubKey(key2.getPubKeyHash())); - } - - @Test - public void removeKey() { - ECKey key = new ECKey(); - chain.importKeys(key); - assertEquals(1, chain.numKeys()); - assertTrue(chain.removeKey(key)); - assertEquals(0, chain.numKeys()); - assertFalse(chain.removeKey(key)); - } - - @Test - public void getKey() { - ECKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(onKeysAddedRan.getAndSet(false)); - assertEquals(key1, onKeysAdded.getAndSet(null).get(0)); - ECKey key2 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - assertFalse(onKeysAddedRan.getAndSet(false)); - assertEquals(key2, key1); - } - - @Test(expected = IllegalStateException.class) - public void checkPasswordNoKeys() { - chain.checkPassword("test"); - } - - @Test(expected = IllegalStateException.class) - public void checkPasswordNotEncrypted() { - final ArrayList keys = Lists.newArrayList(new ECKey(), new ECKey()); - chain.importKeys(keys); - chain.checkPassword("test"); - } - - @Test(expected = IllegalStateException.class) - public void doubleEncryptFails() { - final ArrayList keys = Lists.newArrayList(new ECKey(), new ECKey()); - chain.importKeys(keys); - chain = chain.toEncrypted("foo"); - chain.toEncrypted("foo"); - } - - @Test - public void encryptDecrypt() { - final ECKey key1 = new ECKey(); - chain.importKeys(key1, new ECKey()); - final String PASSWORD = "foobar"; - chain = chain.toEncrypted(PASSWORD); - final KeyCrypter keyCrypter = chain.getKeyCrypter(); - assertNotNull(keyCrypter); - assertTrue(keyCrypter instanceof KeyCrypterScrypt); - - assertTrue(chain.checkPassword(PASSWORD)); - assertFalse(chain.checkPassword("wrong")); - ECKey key = chain.findKeyFromPubKey(key1.getPubKey()); - assertTrue(key.isEncrypted()); - assertNull(key.getSecretBytes()); - - try { - // Don't allow import of an unencrypted key. - chain.importKeys(new ECKey()); - fail(); - } catch (KeyCrypterException e) { - } - - try { - chain.toDecrypted(keyCrypter.deriveKey("wrong")); - fail(); - } catch (KeyCrypterException e) {} - chain = chain.toDecrypted(PASSWORD); - key = chain.findKeyFromPubKey(key1.getPubKey()); - assertFalse(key.isEncrypted()); - key.getPrivKeyBytes(); - } - - @Test(expected = KeyCrypterException.class) - public void cannotImportEncryptedKey() { - final ECKey key1 = new ECKey(); - chain.importKeys(ImmutableList.of(key1)); - chain = chain.toEncrypted("foobar"); - ECKey encryptedKey = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(encryptedKey.isEncrypted()); - - BasicKeyChain chain2 = new BasicKeyChain(); - chain2.importKeys(ImmutableList.of(encryptedKey)); - } - - @Test(expected = KeyCrypterException.class) - public void cannotMixParams() throws Exception { - chain = chain.toEncrypted("foobar"); - KeyCrypterScrypt scrypter = new KeyCrypterScrypt(2); // Some bogus params. - ECKey key1 = new ECKey().encrypt(scrypter, scrypter.deriveKey("other stuff")); - chain.importKeys(key1); - } - - @Test - public void serializationUnencrypted() throws UnreadableWalletException { - Utils.setMockClock(); - Date now = Utils.now(); - final ECKey key1 = new ECKey(); - Utils.rollMockClock(5000); - final ECKey key2 = new ECKey(); - chain.importKeys(ImmutableList.of(key1, key2)); - List keys = chain.serializeToProtobuf(); - assertEquals(2, keys.size()); - assertArrayEquals(key1.getPubKey(), keys.get(0).getPublicKey().toByteArray()); - assertArrayEquals(key2.getPubKey(), keys.get(1).getPublicKey().toByteArray()); - assertArrayEquals(key1.getPrivKeyBytes(), keys.get(0).getSecretBytes().toByteArray()); - assertArrayEquals(key2.getPrivKeyBytes(), keys.get(1).getSecretBytes().toByteArray()); - long normTime = (long) (Math.floor(now.getTime() / 1000) * 1000); - assertEquals(normTime, keys.get(0).getCreationTimestamp()); - assertEquals(normTime + 5000 * 1000, keys.get(1).getCreationTimestamp()); - - chain = BasicKeyChain.fromProtobufUnencrypted(keys); - assertEquals(2, chain.getKeys().size()); - assertEquals(key1, chain.getKeys().get(0)); - assertEquals(key2, chain.getKeys().get(1)); - } - - @Test - public void serializationEncrypted() throws UnreadableWalletException { - ECKey key1 = new ECKey(); - chain.importKeys(key1); - chain = chain.toEncrypted("foo bar"); - key1 = chain.getKeys().get(0); - List keys = chain.serializeToProtobuf(); - assertEquals(1, keys.size()); - assertArrayEquals(key1.getPubKey(), keys.get(0).getPublicKey().toByteArray()); - assertFalse(keys.get(0).hasSecretBytes()); - assertTrue(keys.get(0).hasEncryptedData()); - chain = BasicKeyChain.fromProtobufEncrypted(keys, checkNotNull(chain.getKeyCrypter())); - assertEquals(key1.getEncryptedPrivateKey(), chain.getKeys().get(0).getEncryptedPrivateKey()); - assertTrue(chain.checkPassword("foo bar")); - } - - @Test - public void watching() throws UnreadableWalletException { - ECKey key1 = new ECKey(); - ECKey pub = ECKey.fromPublicOnly(key1.getPubKeyPoint()); - chain.importKeys(pub); - assertEquals(1, chain.numKeys()); - List keys = chain.serializeToProtobuf(); - assertEquals(1, keys.size()); - assertTrue(keys.get(0).hasPublicKey()); - assertFalse(keys.get(0).hasSecretBytes()); - chain = BasicKeyChain.fromProtobufUnencrypted(keys); - assertEquals(1, chain.numKeys()); - assertFalse(chain.findKeyFromPubKey(pub.getPubKey()).hasPrivKey()); - } - - @Test - public void bloom() throws Exception { - ECKey key1 = new ECKey(); - ECKey key2 = new ECKey(); - chain.importKeys(key1, key2); - assertEquals(2, chain.numKeys()); - assertEquals(4, chain.numBloomFilterEntries()); - BloomFilter filter = chain.getFilter(4, 0.001, 100); - assertTrue(filter.contains(key1.getPubKey())); - assertTrue(filter.contains(key1.getPubKeyHash())); - assertTrue(filter.contains(key2.getPubKey())); - assertTrue(filter.contains(key2.getPubKeyHash())); - ECKey key3 = new ECKey(); - assertFalse(filter.contains(key3.getPubKey())); - } - - @Test - public void keysBeforeAndAfter() throws Exception { - Utils.setMockClock(); - long now = Utils.currentTimeSeconds(); - final ECKey key1 = new ECKey(); - Utils.rollMockClock(86400); - final ECKey key2 = new ECKey(); - final List keys = Lists.newArrayList(key1, key2); - assertEquals(2, chain.importKeys(keys)); - - assertNull(chain.findOldestKeyAfter(now + 86400 * 2)); - assertEquals(key1, chain.findOldestKeyAfter(now - 1)); - assertEquals(key2, chain.findOldestKeyAfter(now + 86400 - 1)); - - assertEquals(2, chain.findKeysBefore(now + 86400 * 2).size()); - assertEquals(1, chain.findKeysBefore(now + 1).size()); - assertEquals(0, chain.findKeysBefore(now - 1).size()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultCoinSelectorTest.java b/core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultCoinSelectorTest.java deleted file mode 100644 index 2f2c1387..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultCoinSelectorTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright 2013 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.dogecoin.dogecoinj.wallet; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.params.RegTestParams; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.testing.FakeTxBuilder; -import com.dogecoin.dogecoinj.testing.TestWithWallet; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; - -import static com.dogecoin.dogecoinj.core.Coin.*; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.*; - -public class DefaultCoinSelectorTest extends TestWithWallet { - private static final NetworkParameters params = UnitTestParams.get(); - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - Utils.setMockClock(); // Use mock clock - } - - @After - @Override - public void tearDown() throws Exception { - super.tearDown(); - } - - @Test - public void selectable() throws Exception { - Transaction t; - t = new Transaction(params); - t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING); - assertFalse(DefaultCoinSelector.isSelectable(t)); - t.getConfidence().setSource(TransactionConfidence.Source.SELF); - assertFalse(DefaultCoinSelector.isSelectable(t)); - t.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("1.2.3.4"))); - assertFalse(DefaultCoinSelector.isSelectable(t)); - t.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("5.6.7.8"))); - assertTrue(DefaultCoinSelector.isSelectable(t)); - t = new Transaction(params); - t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.BUILDING); - assertTrue(DefaultCoinSelector.isSelectable(t)); - t = new Transaction(RegTestParams.get()); - t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING); - t.getConfidence().setSource(TransactionConfidence.Source.SELF); - assertTrue(DefaultCoinSelector.isSelectable(t)); - } - - @Test - public void depthOrdering() throws Exception { - // Send two transactions in two blocks on top of each other. - Transaction t1 = checkNotNull(sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN)); - Transaction t2 = checkNotNull(sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN)); - - // Check we selected just the oldest one. - DefaultCoinSelector selector = new DefaultCoinSelector(); - CoinSelection selection = selector.select(COIN, wallet.calculateAllSpendCandidates(true)); - assertTrue(selection.gathered.contains(t1.getOutputs().get(0))); - assertEquals(COIN, selection.valueGathered); - - // Check we ordered them correctly (by depth). - ArrayList candidates = new ArrayList(); - candidates.add(t2.getOutput(0)); - candidates.add(t1.getOutput(0)); - DefaultCoinSelector.sortOutputs(candidates); - assertEquals(t1.getOutput(0), candidates.get(0)); - assertEquals(t2.getOutput(0), candidates.get(1)); - } - - @Test - public void coinAgeOrdering() throws Exception { - // Send three transactions in four blocks on top of each other. Coin age of t1 is 1*4=4, coin age of t2 = 2*2=4 - // and t3=0.01. - Transaction t1 = checkNotNull(sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN)); - // Padding block. - wallet.notifyNewBestBlock(FakeTxBuilder.createFakeBlock(blockStore).storedBlock); - final Coin TWO_COINS = COIN.multiply(2); - Transaction t2 = checkNotNull(sendMoneyToWallet(TWO_COINS, AbstractBlockChain.NewBlockType.BEST_CHAIN)); - Transaction t3 = checkNotNull(sendMoneyToWallet(CENT, AbstractBlockChain.NewBlockType.BEST_CHAIN)); - - // Should be ordered t2, t1, t3. - ArrayList candidates = new ArrayList(); - candidates.add(t3.getOutput(0)); - candidates.add(t2.getOutput(0)); - candidates.add(t1.getOutput(0)); - DefaultCoinSelector.sortOutputs(candidates); - assertEquals(t2.getOutput(0), candidates.get(0)); - assertEquals(t1.getOutput(0), candidates.get(1)); - assertEquals(t3.getOutput(0), candidates.get(2)); - } - - @Test - public void identicalInputs() throws Exception { - // Add four outputs to a transaction with same value and destination. Select them all. - Transaction t = new Transaction(params); - java.util.List outputs = Arrays.asList( - new TransactionOutput(params, t, Coin.valueOf(30302787), myAddress), - new TransactionOutput(params, t, Coin.valueOf(30302787), myAddress), - new TransactionOutput(params, t, Coin.valueOf(30302787), myAddress), - new TransactionOutput(params, t, Coin.valueOf(30302787), myAddress) - ); - t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.BUILDING); - - DefaultCoinSelector selector = new DefaultCoinSelector(); - CoinSelection selection = selector.select(COIN.multiply(2), outputs); - - assertTrue(selection.gathered.size() == 4); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultRiskAnalysisTest.java b/core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultRiskAnalysisTest.java deleted file mode 100644 index d850b5cf..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultRiskAnalysisTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Copyright 2014 Andreas Schildbach - * - * 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.dogecoin.dogecoinj.wallet; - -import java.util.Arrays; -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.crypto.TransactionSignature; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.script.Script; -import com.dogecoin.dogecoinj.script.ScriptBuilder; -import com.dogecoin.dogecoinj.script.ScriptChunk; -import com.google.common.collect.ImmutableList; -import com.dogecoin.dogecoinj.wallet.DefaultRiskAnalysis.RuleViolation; -import org.junit.Before; -import org.junit.Test; - -import static com.dogecoin.dogecoinj.core.Coin.COIN; -import static com.dogecoin.dogecoinj.script.ScriptOpCodes.OP_PUSHDATA1; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -public class DefaultRiskAnalysisTest { - // Uses mainnet because isStandard checks are disabled on testnet. - private static final NetworkParameters params = MainNetParams.get(); - private Wallet wallet; - private final int TIMESTAMP = 1384190189; - private static final ECKey key1 = new ECKey(); - private final ImmutableList NO_DEPS = ImmutableList.of(); - - @Before - public void setup() { - wallet = new Wallet(params) { - @Override - public int getLastBlockSeenHeight() { - return 1000; - } - - @Override - public long getLastBlockSeenTimeSecs() { - return TIMESTAMP; - } - }; - } - - @Test - public void nonFinal() throws Exception { - // Verify that just having a lock time in the future is not enough to be considered risky (it's still final). - Transaction tx = new Transaction(params); - TransactionInput input = tx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)); - tx.addOutput(COIN, key1); - tx.setLockTime(TIMESTAMP + 86400); - - { - DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); - assertEquals(RiskAnalysis.Result.OK, analysis.analyze()); - assertNull(analysis.getNonFinal()); - // Verify we can't re-use a used up risk analysis. - try { - analysis.analyze(); - fail(); - } catch (IllegalStateException e) {} - } - - // Set a sequence number on the input to make it genuinely non-final. Verify it's risky. - input.setSequenceNumber(1); - { - DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); - assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze()); - assertEquals(tx, analysis.getNonFinal()); - } - - // If the lock time is the current block, it's about to become final and we consider it non-risky. - tx.setLockTime(1000); - { - DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); - assertEquals(RiskAnalysis.Result.OK, analysis.analyze()); - } - } - - @Test - public void selfCreatedAreNotRisky() { - Transaction tx = new Transaction(params); - tx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)).setSequenceNumber(1); - tx.addOutput(COIN, key1); - tx.setLockTime(TIMESTAMP + 86400); - - { - // Is risky ... - DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); - assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze()); - } - tx.getConfidence().setSource(TransactionConfidence.Source.SELF); - { - // Is no longer risky. - DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); - assertEquals(RiskAnalysis.Result.OK, analysis.analyze()); - } - } - - @Test - public void nonFinalDependency() { - // Final tx has a dependency that is non-final. - Transaction tx1 = new Transaction(params); - tx1.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)).setSequenceNumber(1); - TransactionOutput output = tx1.addOutput(COIN, key1); - tx1.setLockTime(TIMESTAMP + 86400); - Transaction tx2 = new Transaction(params); - tx2.addInput(output); - tx2.addOutput(COIN, new ECKey()); - - DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx2, ImmutableList.of(tx1)); - assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze()); - assertEquals(tx1, analysis.getNonFinal()); - } - - @Test - public void nonStandardDust() { - Transaction standardTx = new Transaction(params); - standardTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)); - standardTx.addOutput(COIN, key1); - assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, standardTx, NO_DEPS).analyze()); - - Transaction dustTx = new Transaction(params); - dustTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)); - dustTx.addOutput(Coin.SATOSHI, key1); // 1 Satoshi - assertEquals(RiskAnalysis.Result.NON_STANDARD, DefaultRiskAnalysis.FACTORY.create(wallet, dustTx, NO_DEPS).analyze()); - - Transaction edgeCaseTx = new Transaction(params); - edgeCaseTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)); - edgeCaseTx.addOutput(DefaultRiskAnalysis.MIN_ANALYSIS_NONDUST_OUTPUT, key1); // Dust threshold - assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, edgeCaseTx, NO_DEPS).analyze()); - } - - @Test - public void nonShortestPossiblePushData() { - ScriptChunk nonStandardChunk = new ScriptChunk(OP_PUSHDATA1, new byte[75]); - byte[] nonStandardScript = new ScriptBuilder().addChunk(nonStandardChunk).build().getProgram(); - // Test non-standard script as an input. - Transaction tx = new Transaction(params); - assertEquals(DefaultRiskAnalysis.RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx)); - tx.addInput(new TransactionInput(params, null, nonStandardScript)); - assertEquals(DefaultRiskAnalysis.RuleViolation.SHORTEST_POSSIBLE_PUSHDATA, DefaultRiskAnalysis.isStandard(tx)); - // Test non-standard script as an output. - tx.clearInputs(); - assertEquals(DefaultRiskAnalysis.RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx)); - tx.addOutput(new TransactionOutput(params, null, COIN, nonStandardScript)); - assertEquals(DefaultRiskAnalysis.RuleViolation.SHORTEST_POSSIBLE_PUSHDATA, DefaultRiskAnalysis.isStandard(tx)); - } - - @Test - public void canonicalSignature() { - TransactionSignature sig = TransactionSignature.dummy(); - Script scriptOk = ScriptBuilder.createInputScript(sig); - assertEquals(RuleViolation.NONE, - DefaultRiskAnalysis.isInputStandard(new TransactionInput(params, null, scriptOk.getProgram()))); - - byte[] sigBytes = sig.encodeToBitcoin(); - // Appending a zero byte makes the signature uncanonical without violating DER encoding. - Script scriptUncanonicalEncoding = new ScriptBuilder().data(Arrays.copyOf(sigBytes, sigBytes.length + 1)) - .build(); - assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING, - DefaultRiskAnalysis.isInputStandard(new TransactionInput(params, null, scriptUncanonicalEncoding - .getProgram()))); - } - - @Test - public void standardOutputs() throws Exception { - Transaction tx = new Transaction(params); - tx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0)); - // A pay to address output - tx.addOutput(Coin.CENT, ScriptBuilder.createOutputScript(key1.toAddress(params))); - // A pay to pubkey output - tx.addOutput(Coin.CENT, ScriptBuilder.createOutputScript(key1)); - tx.addOutput(Coin.CENT, ScriptBuilder.createOutputScript(key1)); - // 1-of-2 multisig output. - ImmutableList keys = ImmutableList.of(key1, new ECKey()); - tx.addOutput(Coin.CENT, ScriptBuilder.createMultiSigOutputScript(1, keys)); - // 2-of-2 multisig output. - tx.addOutput(Coin.CENT, ScriptBuilder.createMultiSigOutputScript(2, keys)); - // P2SH - tx.addOutput(Coin.CENT, ScriptBuilder.createP2SHOutputScript(1, keys)); - // OP_RETURN - tx.addOutput(Coin.CENT, ScriptBuilder.createOpReturnScript("hi there".getBytes())); - assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS).analyze()); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/wallet/DeterministicKeyChainTest.java b/core/src/test/java/com/dogecoin/dogecoinj/wallet/DeterministicKeyChainTest.java deleted file mode 100644 index 31201b20..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/wallet/DeterministicKeyChainTest.java +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Copyright 2013 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.dogecoin.dogecoinj.wallet; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.crypto.DeterministicHierarchy; -import com.dogecoin.dogecoinj.crypto.DeterministicKey; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.params.UnitTestParams; -import com.dogecoin.dogecoinj.store.UnreadableWalletException; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.dogecoin.dogecoinj.utils.Threading; -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; -import org.spongycastle.crypto.params.KeyParameter; - -import java.io.IOException; -import java.security.SecureRandom; -import java.util.List; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.*; - -public class DeterministicKeyChainTest { - private DeterministicKeyChain chain; - private final byte[] ENTROPY = Sha256Hash.create("don't use a string seed like this in real life".getBytes()).getBytes(); - - @Before - public void setup() { - BriefLogFormatter.init(); - // You should use a random seed instead. The secs constant comes from the unit test file, so we can compare - // serialized data properly. - long secs = 1389353062L; - chain = new DeterministicKeyChain(ENTROPY, "", secs); - chain.setLookaheadSize(10); - assertEquals(secs, checkNotNull(chain.getSeed()).getCreationTimeSeconds()); - } - - @Test - public void derive() throws Exception { - ECKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - ECKey key2 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - - final Address address = new Address(UnitTestParams.get(), "n1bQNoEx8uhmCzzA5JPG6sFdtsUQhwiQJV"); - assertEquals(address, key1.toAddress(UnitTestParams.get())); - assertEquals("mnHUcqUVvrfi5kAaXJDQzBb9HsWs78b42R", key2.toAddress(UnitTestParams.get()).toString()); - assertEquals(key1, chain.findKeyFromPubHash(address.getHash160())); - assertEquals(key2, chain.findKeyFromPubKey(key2.getPubKey())); - - key1.sign(Sha256Hash.ZERO_HASH); - - ECKey key3 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - assertEquals("mqumHgVDqNzuXNrszBmi7A2UpmwaPMx4HQ", key3.toAddress(UnitTestParams.get()).toString()); - key3.sign(Sha256Hash.ZERO_HASH); - } - - @Test - public void getKeys() throws Exception { - chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - chain.getKey(KeyChain.KeyPurpose.CHANGE); - chain.maybeLookAhead(); - assertEquals(2, chain.getKeys(false).size()); - } - - @Test - public void signMessage() throws Exception { - ECKey key = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - key.verifyMessage("test", key.signMessage("test")); - } - - @Test - public void events() throws Exception { - // Check that we get the right events at the right time. - final List> listenerKeys = Lists.newArrayList(); - long secs = 1389353062L; - chain = new DeterministicKeyChain(ENTROPY, "", secs); - chain.addEventListener(new AbstractKeyChainEventListener() { - @Override - public void onKeysAdded(List keys) { - listenerKeys.add(keys); - } - }, Threading.SAME_THREAD); - assertEquals(0, listenerKeys.size()); - chain.setLookaheadSize(5); - assertEquals(0, listenerKeys.size()); - ECKey key = chain.getKey(KeyChain.KeyPurpose.CHANGE); - assertEquals(1, listenerKeys.size()); // 1 event - final List firstEvent = listenerKeys.get(0); - assertEquals(1, firstEvent.size()); - assertTrue(firstEvent.contains(key)); // order is not specified. - listenerKeys.clear(); - - chain.maybeLookAhead(); - final List secondEvent = listenerKeys.get(0); - assertEquals(12, secondEvent.size()); // (5 lookahead keys, +1 lookahead threshold) * 2 chains - listenerKeys.clear(); - - chain.getKey(KeyChain.KeyPurpose.CHANGE); - // At this point we've entered the threshold zone so more keys won't immediately trigger more generations. - assertEquals(0, listenerKeys.size()); // 1 event - final int lookaheadThreshold = chain.getLookaheadThreshold() + chain.getLookaheadSize(); - for (int i = 0; i < lookaheadThreshold; i++) - chain.getKey(KeyChain.KeyPurpose.CHANGE); - assertEquals(1, listenerKeys.size()); // 1 event - assertEquals(1, listenerKeys.get(0).size()); // 1 key. - } - - @Test - public void random() { - // Can't test much here but verify the constructor worked and the class is functional. The other tests rely on - // a fixed seed to be deterministic. - chain = new DeterministicKeyChain(new SecureRandom(), 384); - chain.setLookaheadSize(10); - chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).sign(Sha256Hash.ZERO_HASH); - chain.getKey(KeyChain.KeyPurpose.CHANGE).sign(Sha256Hash.ZERO_HASH); - } - - @Test - public void serializeUnencrypted() throws UnreadableWalletException { - chain.maybeLookAhead(); - DeterministicKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key2 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key3 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - List keys = chain.serializeToProtobuf(); - // 1 mnemonic/seed, 1 master key, 1 account key, 2 internal keys, 3 derived, 20 lookahead and 5 lookahead threshold. - int numItems = - 1 // mnemonic/seed - + 1 // master key - + 1 // account key - + 2 // ext/int parent keys - + (chain.getLookaheadSize() + chain.getLookaheadThreshold()) * 2 // lookahead zone on each chain - ; - assertEquals(numItems, keys.size()); - - // Get another key that will be lost during round-tripping, to ensure we can derive it again. - DeterministicKey key4 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - - final String EXPECTED_SERIALIZATION = checkSerialization(keys, "deterministic-wallet-serialization.txt"); - - // Round trip the data back and forth to check it is preserved. - int oldLookaheadSize = chain.getLookaheadSize(); - chain = DeterministicKeyChain.fromProtobuf(keys, null).get(0); - assertEquals(EXPECTED_SERIALIZATION, protoToString(chain.serializeToProtobuf())); - assertEquals(key1, chain.findKeyFromPubHash(key1.getPubKeyHash())); - assertEquals(key2, chain.findKeyFromPubHash(key2.getPubKeyHash())); - assertEquals(key3, chain.findKeyFromPubHash(key3.getPubKeyHash())); - assertEquals(key4, chain.getKey(KeyChain.KeyPurpose.CHANGE)); - key1.sign(Sha256Hash.ZERO_HASH); - key2.sign(Sha256Hash.ZERO_HASH); - key3.sign(Sha256Hash.ZERO_HASH); - key4.sign(Sha256Hash.ZERO_HASH); - assertEquals(oldLookaheadSize, chain.getLookaheadSize()); - } - - @Test(expected = IllegalStateException.class) - public void notEncrypted() { - chain.toDecrypted("fail"); - } - - @Test(expected = IllegalStateException.class) - public void encryptTwice() { - chain = chain.toEncrypted("once"); - chain = chain.toEncrypted("twice"); - } - - private void checkEncryptedKeyChain(DeterministicKeyChain encChain, DeterministicKey key1) { - // Check we can look keys up and extend the chain without the AES key being provided. - DeterministicKey encKey1 = encChain.findKeyFromPubKey(key1.getPubKey()); - DeterministicKey encKey2 = encChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertFalse(key1.isEncrypted()); - assertTrue(encKey1.isEncrypted()); - assertEquals(encKey1.getPubKeyPoint(), key1.getPubKeyPoint()); - final KeyParameter aesKey = checkNotNull(encChain.getKeyCrypter()).deriveKey("open secret"); - encKey1.sign(Sha256Hash.ZERO_HASH, aesKey); - encKey2.sign(Sha256Hash.ZERO_HASH, aesKey); - assertTrue(encChain.checkAESKey(aesKey)); - assertFalse(encChain.checkPassword("access denied")); - assertTrue(encChain.checkPassword("open secret")); - } - - @Test - public void encryption() throws UnreadableWalletException { - DeterministicKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKeyChain encChain = chain.toEncrypted("open secret"); - DeterministicKey encKey1 = encChain.findKeyFromPubKey(key1.getPubKey()); - checkEncryptedKeyChain(encChain, key1); - - // Round-trip to ensure de/serialization works and that we can store two chains and they both deserialize. - List serialized = encChain.serializeToProtobuf(); - List doubled = Lists.newArrayListWithExpectedSize(serialized.size() * 2); - doubled.addAll(serialized); - doubled.addAll(serialized); - final List chains = DeterministicKeyChain.fromProtobuf(doubled, encChain.getKeyCrypter()); - assertEquals(2, chains.size()); - encChain = chains.get(0); - checkEncryptedKeyChain(encChain, chain.findKeyFromPubKey(key1.getPubKey())); - encChain = chains.get(1); - checkEncryptedKeyChain(encChain, chain.findKeyFromPubKey(key1.getPubKey())); - - DeterministicKey encKey2 = encChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - // Decrypt and check the keys match. - DeterministicKeyChain decChain = encChain.toDecrypted("open secret"); - DeterministicKey decKey1 = decChain.findKeyFromPubHash(encKey1.getPubKeyHash()); - DeterministicKey decKey2 = decChain.findKeyFromPubHash(encKey2.getPubKeyHash()); - assertEquals(decKey1.getPubKeyPoint(), encKey1.getPubKeyPoint()); - assertEquals(decKey2.getPubKeyPoint(), encKey2.getPubKeyPoint()); - assertFalse(decKey1.isEncrypted()); - assertFalse(decKey2.isEncrypted()); - assertNotEquals(encKey1.getParent(), decKey1.getParent()); // parts of a different hierarchy - // Check we can once again derive keys from the decrypted chain. - decChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).sign(Sha256Hash.ZERO_HASH); - decChain.getKey(KeyChain.KeyPurpose.CHANGE).sign(Sha256Hash.ZERO_HASH); - } - - @Test - public void watchingChain() throws UnreadableWalletException { - Utils.setMockClock(); - DeterministicKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key2 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key3 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - DeterministicKey key4 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - - NetworkParameters params = MainNetParams.get(); - DeterministicKey watchingKey = chain.getWatchingKey(); - final String pub58 = watchingKey.serializePubB58(params); - assertEquals("xpub69KR9epSNBM59KLuasxMU5CyKytMJjBP5HEZ5p8YoGUCpM6cM9hqxB9DDPCpUUtqmw5duTckvPfwpoWGQUFPmRLpxs5jYiTf2u6xRMcdhDf", pub58); - watchingKey = DeterministicKey.deserializeB58(null, pub58, params); - watchingKey.setCreationTimeSeconds(100000); - chain = DeterministicKeyChain.watch(watchingKey); - assertEquals(DeterministicHierarchy.BIP32_STANDARDISATION_TIME_SECS, chain.getEarliestKeyCreationTime()); - chain.setLookaheadSize(10); - chain.maybeLookAhead(); - - assertEquals(key1.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint()); - assertEquals(key2.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint()); - final DeterministicKey key = chain.getKey(KeyChain.KeyPurpose.CHANGE); - assertEquals(key3.getPubKeyPoint(), key.getPubKeyPoint()); - try { - // Can't sign with a key from a watching chain. - key.sign(Sha256Hash.ZERO_HASH); - fail(); - } catch (ECKey.MissingPrivateKeyException e) { - // Ignored. - } - // Test we can serialize and deserialize a watching chain OK. - List serialization = chain.serializeToProtobuf(); - checkSerialization(serialization, "watching-wallet-serialization.txt"); - chain = DeterministicKeyChain.fromProtobuf(serialization, null).get(0); - final DeterministicKey rekey4 = chain.getKey(KeyChain.KeyPurpose.CHANGE); - assertEquals(key4.getPubKeyPoint(), rekey4.getPubKeyPoint()); - } - - @Test(expected = IllegalStateException.class) - public void watchingCannotEncrypt() throws Exception { - final DeterministicKey accountKey = chain.getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH); - chain = DeterministicKeyChain.watch(accountKey.getPubOnly()); - chain = chain.toEncrypted("this doesn't make any sense"); - } - - @Test - public void bloom1() { - DeterministicKey key2 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key1 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - - int numEntries = - (((chain.getLookaheadSize() + chain.getLookaheadThreshold()) * 2) // * 2 because of internal/external - + chain.numLeafKeysIssued() - + 4 // one root key + one account key + two chain keys (internal/external) - ) * 2; // because the filter contains keys and key hashes. - assertEquals(numEntries, chain.numBloomFilterEntries()); - BloomFilter filter = chain.getFilter(numEntries, 0.001, 1); - assertTrue(filter.contains(key1.getPubKey())); - assertTrue(filter.contains(key1.getPubKeyHash())); - assertTrue(filter.contains(key2.getPubKey())); - assertTrue(filter.contains(key2.getPubKeyHash())); - - // The lookahead zone is tested in bloom2 and via KeyChainGroupTest.bloom - } - - @Test - public void bloom2() throws Exception { - // Verify that if when we watch a key, the filter contains at least 100 keys. - DeterministicKey[] keys = new DeterministicKey[100]; - for (int i = 0; i < keys.length; i++) - keys[i] = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - chain = DeterministicKeyChain.watch(chain.getWatchingKey()); - int e = chain.numBloomFilterEntries(); - BloomFilter filter = chain.getFilter(e, 0.001, 1); - for (DeterministicKey key : keys) - assertTrue("key " + key, filter.contains(key.getPubKeyHash())); - } - - private String protoToString(List keys) { - StringBuilder sb = new StringBuilder(); - for (Protos.Key key : keys) { - sb.append(key.toString()); - sb.append("\n"); - } - return sb.toString().trim(); - } - - private String checkSerialization(List keys, String filename) { - try { - String sb = protoToString(keys); - String expected = Utils.getResourceAsString(getClass().getResource(filename)); - assertEquals(expected, sb); - return expected; - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/wallet/KeyChainGroupTest.java b/core/src/test/java/com/dogecoin/dogecoinj/wallet/KeyChainGroupTest.java deleted file mode 100644 index 744b5ebd..00000000 --- a/core/src/test/java/com/dogecoin/dogecoinj/wallet/KeyChainGroupTest.java +++ /dev/null @@ -1,588 +0,0 @@ -/** - * Copyright 2014 Mike Hearn - * - * 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.dogecoin.dogecoinj.wallet; - -import com.dogecoin.dogecoinj.core.*; -import com.dogecoin.dogecoinj.crypto.DeterministicKey; -import com.dogecoin.dogecoinj.crypto.KeyCrypterException; -import com.dogecoin.dogecoinj.crypto.KeyCrypterScrypt; -import com.dogecoin.dogecoinj.crypto.MnemonicCode; -import com.dogecoin.dogecoinj.params.MainNetParams; -import com.dogecoin.dogecoinj.utils.BriefLogFormatter; -import com.dogecoin.dogecoinj.utils.Threading; -import com.google.common.collect.ImmutableList; -import org.junit.Before; -import org.junit.Test; -import org.spongycastle.crypto.params.KeyParameter; -import org.spongycastle.util.Arrays; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.*; -import static org.junit.Assert.assertArrayEquals; - -public class KeyChainGroupTest { - // Number of initial keys in this tests HD wallet, including interior keys. - private static final int INITIAL_KEYS = 4; - private static final int LOOKAHEAD_SIZE = 5; - private static final NetworkParameters params = MainNetParams.get(); - private static final String XPUB = "xpub68KFnj3bqUx1s7mHejLDBPywCAKdJEu1b49uniEEn2WSbHmZ7xbLqFTjJbtx1LUcAt1DwhoqWHmo2s5WMJp6wi38CiF2hYD49qVViKVvAoi"; - private KeyChainGroup group; - private DeterministicKey watchingAccountKey; - - @Before - public void setup() { - BriefLogFormatter.init(); - Utils.setMockClock(); - group = new KeyChainGroup(params); - group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests. - group.getActiveKeyChain(); // Force create a chain. - - watchingAccountKey = DeterministicKey.deserializeB58(null, XPUB, params); - } - - private KeyChainGroup createMarriedKeyChainGroup() { - KeyChainGroup group = new KeyChainGroup(params); - DeterministicKeyChain chain = createMarriedKeyChain(); - group.addAndActivateHDChain(chain); - group.setLookaheadSize(LOOKAHEAD_SIZE); - group.getActiveKeyChain(); - return group; - } - - private MarriedKeyChain createMarriedKeyChain() { - byte[] entropy = Sha256Hash.create("don't use a seed like this in real life".getBytes()).getBytes(); - DeterministicSeed seed = new DeterministicSeed(entropy, "", MnemonicCode.BIP39_STANDARDISATION_TIME_SECS); - MarriedKeyChain chain = MarriedKeyChain.builder() - .seed(seed) - .followingKeys(watchingAccountKey) - .threshold(2).build(); - return chain; - } - - @Test - public void freshCurrentKeys() throws Exception { - int numKeys = ((group.getLookaheadSize() + group.getLookaheadThreshold()) * 2) // * 2 because of internal/external - + 1 // keys issued - + 3 /* account key + int/ext parent keys */; - assertEquals(numKeys, group.numKeys()); - assertEquals(2 * numKeys, group.getBloomFilterElementCount()); - ECKey r1 = group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(numKeys, group.numKeys()); - assertEquals(2 * numKeys, group.getBloomFilterElementCount()); - - ECKey i1 = new ECKey(); - group.importKeys(i1); - numKeys++; - assertEquals(numKeys, group.numKeys()); - assertEquals(2 * numKeys, group.getBloomFilterElementCount()); - - ECKey r2 = group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(r1, r2); - ECKey c1 = group.currentKey(KeyChain.KeyPurpose.CHANGE); - assertNotEquals(r1, c1); - ECKey r3 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertNotEquals(r1, r3); - ECKey c2 = group.freshKey(KeyChain.KeyPurpose.CHANGE); - assertNotEquals(r3, c2); - // Current key has not moved and will not under marked as used. - ECKey r4 = group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(r2, r4); - ECKey c3 = group.currentKey(KeyChain.KeyPurpose.CHANGE); - assertEquals(c1, c3); - // Mark as used. Current key is now different. - group.markPubKeyAsUsed(r4.getPubKey()); - ECKey r5 = group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertNotEquals(r4, r5); - } - - @Test - public void freshCurrentKeysForMarriedKeychain() throws Exception { - group = createMarriedKeyChainGroup(); - - try { - group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - fail(); - } catch (UnsupportedOperationException e) { - } - - try { - group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - fail(); - } catch (UnsupportedOperationException e) { - } - } - - @Test - public void imports() throws Exception { - ECKey key1 = new ECKey(); - int numKeys = group.numKeys(); - assertFalse(group.removeImportedKey(key1)); - assertEquals(1, group.importKeys(ImmutableList.of(key1))); - assertEquals(numKeys + 1, group.numKeys()); // Lookahead is triggered by requesting a key, so none yet. - group.removeImportedKey(key1); - assertEquals(numKeys, group.numKeys()); - } - - @Test - public void findKey() throws Exception { - ECKey a = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - ECKey b = group.freshKey(KeyChain.KeyPurpose.CHANGE); - ECKey c = new ECKey(); - ECKey d = new ECKey(); // Not imported. - group.importKeys(c); - assertTrue(group.hasKey(a)); - assertTrue(group.hasKey(b)); - assertTrue(group.hasKey(c)); - assertFalse(group.hasKey(d)); - ECKey result = group.findKeyFromPubKey(a.getPubKey()); - assertEquals(a, result); - result = group.findKeyFromPubKey(b.getPubKey()); - assertEquals(b, result); - result = group.findKeyFromPubHash(a.getPubKeyHash()); - assertEquals(a, result); - result = group.findKeyFromPubHash(b.getPubKeyHash()); - assertEquals(b, result); - result = group.findKeyFromPubKey(c.getPubKey()); - assertEquals(c, result); - result = group.findKeyFromPubHash(c.getPubKeyHash()); - assertEquals(c, result); - assertNull(group.findKeyFromPubKey(d.getPubKey())); - assertNull(group.findKeyFromPubHash(d.getPubKeyHash())); - } - - @Test - public void currentP2SHAddress() throws Exception { - group = createMarriedKeyChainGroup(); - Address a1 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(a1.isP2SHAddress()); - Address a2 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(a1, a2); - Address a3 = group.currentAddress(KeyChain.KeyPurpose.CHANGE); - assertNotEquals(a2, a3); - } - - @Test - public void freshAddress() throws Exception { - group = createMarriedKeyChainGroup(); - Address a1 = group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - Address a2 = group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(a1.isP2SHAddress()); - assertNotEquals(a1, a2); - group.getBloomFilterElementCount(); - assertEquals(((group.getLookaheadSize() + group.getLookaheadThreshold()) * 2) // * 2 because of internal/external - + (2 - group.getLookaheadThreshold()) // keys issued - + 4 /* master, account, int, ext */, group.numKeys()); - - Address a3 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(a2, a3); - } - - @Test - public void findRedeemData() throws Exception { - group = createMarriedKeyChainGroup(); - - // test script hash that we don't have - assertNull(group.findRedeemDataFromScriptHash(new ECKey().getPubKey())); - - // test our script hash - Address address = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - RedeemData redeemData = group.findRedeemDataFromScriptHash(address.getHash160()); - assertNotNull(redeemData); - assertNotNull(redeemData.redeemScript); - assertEquals(2, redeemData.keys.size()); - } - - // Check encryption with and without a basic keychain. - - @Test - public void encryptionWithoutImported() throws Exception { - encryption(false); - } - - @Test - public void encryptionWithImported() throws Exception { - encryption(true); - } - - public void encryption(boolean withImported) throws Exception { - Utils.rollMockClock(0); - long now = Utils.currentTimeSeconds(); - ECKey a = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(now, group.getEarliestKeyCreationTime()); - Utils.rollMockClock(-86400); - long yesterday = Utils.currentTimeSeconds(); - ECKey b = new ECKey(); - - assertFalse(group.isEncrypted()); - try { - group.checkPassword("foo"); // Cannot check password of an unencrypted group. - fail(); - } catch (IllegalStateException e) { - } - if (withImported) { - assertEquals(now, group.getEarliestKeyCreationTime()); - group.importKeys(b); - assertEquals(yesterday, group.getEarliestKeyCreationTime()); - } - KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2); - final KeyParameter aesKey = scrypt.deriveKey("password"); - group.encrypt(scrypt, aesKey); - assertTrue(group.isEncrypted()); - assertTrue(group.checkPassword("password")); - assertFalse(group.checkPassword("wrong password")); - final ECKey ea = group.findKeyFromPubKey(a.getPubKey()); - assertTrue(checkNotNull(ea).isEncrypted()); - if (withImported) { - assertTrue(checkNotNull(group.findKeyFromPubKey(b.getPubKey())).isEncrypted()); - assertEquals(yesterday, group.getEarliestKeyCreationTime()); - } else { - assertEquals(now, group.getEarliestKeyCreationTime()); - } - try { - ea.sign(Sha256Hash.ZERO_HASH); - fail(); - } catch (ECKey.KeyIsEncryptedException e) { - // Ignored. - } - if (withImported) { - ECKey c = new ECKey(); - try { - group.importKeys(c); - fail(); - } catch (KeyCrypterException e) { - } - group.importKeysAndEncrypt(ImmutableList.of(c), aesKey); - ECKey ec = group.findKeyFromPubKey(c.getPubKey()); - try { - group.importKeysAndEncrypt(ImmutableList.of(ec), aesKey); - fail(); - } catch (IllegalArgumentException e) { - } - } - - try { - group.decrypt(scrypt.deriveKey("WRONG PASSWORD")); - fail(); - } catch (KeyCrypterException e) { - } - - group.decrypt(aesKey); - assertFalse(group.isEncrypted()); - assertFalse(checkNotNull(group.findKeyFromPubKey(a.getPubKey())).isEncrypted()); - if (withImported) { - assertFalse(checkNotNull(group.findKeyFromPubKey(b.getPubKey())).isEncrypted()); - assertEquals(yesterday, group.getEarliestKeyCreationTime()); - } else { - assertEquals(now, group.getEarliestKeyCreationTime()); - } - } - - @Test - public void encryptionWhilstEmpty() throws Exception { - group = new KeyChainGroup(params); - group.setLookaheadSize(5); - KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2); - final KeyParameter aesKey = scrypt.deriveKey("password"); - group.encrypt(scrypt, aesKey); - assertTrue(group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).isEncrypted()); - final ECKey key = group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - group.decrypt(aesKey); - assertFalse(checkNotNull(group.findKeyFromPubKey(key.getPubKey())).isEncrypted()); - } - - @Test - public void bloom() throws Exception { - ECKey key1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - ECKey key2 = new ECKey(); - BloomFilter filter = group.getBloomFilter(group.getBloomFilterElementCount(), 0.001, (long)(Math.random() * Long.MAX_VALUE)); - assertTrue(filter.contains(key1.getPubKeyHash())); - assertTrue(filter.contains(key1.getPubKey())); - assertFalse(filter.contains(key2.getPubKey())); - // Check that the filter contains the lookahead buffer and threshold zone. - for (int i = 0; i < LOOKAHEAD_SIZE + group.getLookaheadThreshold(); i++) { - ECKey k = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(filter.contains(k.getPubKeyHash())); - } - // We ran ahead of the lookahead buffer. - assertFalse(filter.contains(group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKey())); - group.importKeys(key2); - filter = group.getBloomFilter(group.getBloomFilterElementCount(), 0.001, (long) (Math.random() * Long.MAX_VALUE)); - assertTrue(filter.contains(key1.getPubKeyHash())); - assertTrue(filter.contains(key1.getPubKey())); - assertTrue(filter.contains(key2.getPubKey())); - } - - @Test - public void findRedeemScriptFromPubHash() throws Exception { - group = createMarriedKeyChainGroup(); - Address address = group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(group.findRedeemDataFromScriptHash(address.getHash160()) != null); - group.getBloomFilterElementCount(); - KeyChainGroup group2 = createMarriedKeyChainGroup(); - group2.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - group2.getBloomFilterElementCount(); // Force lookahead. - // test address from lookahead zone and lookahead threshold zone - for (int i = 0; i < group.getLookaheadSize() + group.getLookaheadThreshold(); i++) { - address = group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(group2.findRedeemDataFromScriptHash(address.getHash160()) != null); - } - assertFalse(group2.findRedeemDataFromScriptHash(group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS).getHash160()) != null); - } - - @Test - public void bloomFilterForMarriedChains() throws Exception { - group = createMarriedKeyChainGroup(); - int bufferSize = group.getLookaheadSize() + group.getLookaheadThreshold(); - int expected = bufferSize * 2 /* chains */ * 2 /* elements */; - assertEquals(expected, group.getBloomFilterElementCount()); - Address address1 = group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(expected, group.getBloomFilterElementCount()); - BloomFilter filter = group.getBloomFilter(expected + 2, 0.001, (long)(Math.random() * Long.MAX_VALUE)); - assertTrue(filter.contains(address1.getHash160())); - - Address address2 = group.freshAddress(KeyChain.KeyPurpose.CHANGE); - assertTrue(filter.contains(address2.getHash160())); - - // Check that the filter contains the lookahead buffer. - for (int i = 0; i < bufferSize - 1 /* issued address */; i++) { - Address address = group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue("key " + i, filter.contains(address.getHash160())); - } - // We ran ahead of the lookahead buffer. - assertFalse(filter.contains(group.freshAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS).getHash160())); - } - - @Test - public void earliestKeyTime() throws Exception { - long now = Utils.currentTimeSeconds(); // mock - long yesterday = now - 86400; - assertEquals(now, group.getEarliestKeyCreationTime()); - Utils.rollMockClock(10000); - group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - Utils.rollMockClock(10000); - group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - // Check that all keys are assumed to be created at the same instant the seed is. - assertEquals(now, group.getEarliestKeyCreationTime()); - ECKey key = new ECKey(); - key.setCreationTimeSeconds(yesterday); - group.importKeys(key); - assertEquals(yesterday, group.getEarliestKeyCreationTime()); - } - - @Test - public void events() throws Exception { - // Check that events are registered with the right chains and that if a chain is added, it gets the event - // listeners attached properly even post-hoc. - final AtomicReference ran = new AtomicReference(null); - final KeyChainEventListener listener = new KeyChainEventListener() { - @Override - public void onKeysAdded(List keys) { - ran.set(keys.get(0)); - } - }; - group.addEventListener(listener, Threading.SAME_THREAD); - ECKey key = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(key, ran.getAndSet(null)); - ECKey key2 = new ECKey(); - group.importKeys(key2); - assertEquals(key2, ran.getAndSet(null)); - group.removeEventListener(listener); - ECKey key3 = new ECKey(); - group.importKeys(key3); - assertNull(ran.get()); - } - - @Test - public void serialization() throws Exception { - assertEquals(INITIAL_KEYS + 1 /* for the seed */, group.serializeToProtobuf().size()); - group = KeyChainGroup.fromProtobufUnencrypted(params, group.serializeToProtobuf()); - group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicKey key2 = group.freshKey(KeyChain.KeyPurpose.CHANGE); - group.getBloomFilterElementCount(); - List protoKeys1 = group.serializeToProtobuf(); - assertEquals(INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 1, protoKeys1.size()); - group.importKeys(new ECKey()); - List protoKeys2 = group.serializeToProtobuf(); - assertEquals(INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 2, protoKeys2.size()); - - group = KeyChainGroup.fromProtobufUnencrypted(params, protoKeys1); - assertEquals(INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 1, protoKeys1.size()); - assertTrue(group.hasKey(key1)); - assertTrue(group.hasKey(key2)); - assertEquals(key2, group.currentKey(KeyChain.KeyPurpose.CHANGE)); - assertEquals(key1, group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS)); - group = KeyChainGroup.fromProtobufUnencrypted(params, protoKeys2); - assertEquals(INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 2, protoKeys2.size()); - assertTrue(group.hasKey(key1)); - assertTrue(group.hasKey(key2)); - - KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2); - final KeyParameter aesKey = scrypt.deriveKey("password"); - group.encrypt(scrypt, aesKey); - List protoKeys3 = group.serializeToProtobuf(); - group = KeyChainGroup.fromProtobufEncrypted(params, protoKeys3, scrypt); - assertTrue(group.isEncrypted()); - assertTrue(group.checkPassword("password")); - group.decrypt(aesKey); - - // No need for extensive contents testing here, as that's done in the keychain class tests. - } - - @Test - public void serializeWatching() throws Exception { - group = new KeyChainGroup(params, watchingAccountKey); - group.setLookaheadSize(LOOKAHEAD_SIZE); - group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - group.freshKey(KeyChain.KeyPurpose.CHANGE); - group.getBloomFilterElementCount(); // Force lookahead. - List protoKeys1 = group.serializeToProtobuf(); - assertEquals(3 + (group.getLookaheadSize() + group.getLookaheadThreshold() + 1) * 2, protoKeys1.size()); - group = KeyChainGroup.fromProtobufUnencrypted(params, protoKeys1); - assertEquals(3 + (group.getLookaheadSize() + group.getLookaheadThreshold() + 1) * 2, group.serializeToProtobuf().size()); - } - - @Test - public void serializeMarried() throws Exception { - group = createMarriedKeyChainGroup(); - Address address1 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertTrue(group.isMarried()); - assertEquals(2, group.getActiveKeyChain().getSigsRequiredToSpend()); - - List protoKeys = group.serializeToProtobuf(); - KeyChainGroup group2 = KeyChainGroup.fromProtobufUnencrypted(params, protoKeys); - assertTrue(group2.isMarried()); - assertEquals(2, group.getActiveKeyChain().getSigsRequiredToSpend()); - Address address2 = group2.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(address1, address2); - } - - @Test - public void addFollowingAccounts() throws Exception { - assertFalse(group.isMarried()); - group.addAndActivateHDChain(createMarriedKeyChain()); - assertTrue(group.isMarried()); - } - - @Test - public void constructFromSeed() throws Exception { - ECKey key1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - final DeterministicSeed seed = checkNotNull(group.getActiveKeyChain().getSeed()); - KeyChainGroup group2 = new KeyChainGroup(params, seed); - group2.setLookaheadSize(5); - ECKey key2 = group2.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(key1, key2); - } - - @Test(expected = DeterministicUpgradeRequiredException.class) - public void deterministicUpgradeRequired() throws Exception { - // Check that if we try to use HD features in a KCG that only has random keys, we get an exception. - group = new KeyChainGroup(params); - group.importKeys(new ECKey(), new ECKey()); - assertTrue(group.isDeterministicUpgradeRequired()); - group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); // throws - } - - @Test - public void deterministicUpgradeUnencrypted() throws Exception { - // Check that a group that contains only random keys has its HD chain created using the private key bytes of - // the oldest random key, so upgrading the same wallet twice gives the same outcome. - group = new KeyChainGroup(params); - group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests. - ECKey key1 = new ECKey(); - Utils.rollMockClock(86400); - ECKey key2 = new ECKey(); - group.importKeys(key2, key1); - - List protobufs = group.serializeToProtobuf(); - group.upgradeToDeterministic(0, null); - assertFalse(group.isDeterministicUpgradeRequired()); - DeterministicKey dkey1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicSeed seed1 = group.getActiveKeyChain().getSeed(); - assertNotNull(seed1); - - group = KeyChainGroup.fromProtobufUnencrypted(params, protobufs); - group.upgradeToDeterministic(0, null); // Should give same result as last time. - DeterministicKey dkey2 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); - DeterministicSeed seed2 = group.getActiveKeyChain().getSeed(); - assertEquals(seed1, seed2); - assertEquals(dkey1, dkey2); - - // Check we used the right (oldest) key despite backwards import order. - byte[] truncatedBytes = Arrays.copyOfRange(key1.getSecretBytes(), 0, 16); - assertArrayEquals(seed1.getEntropyBytes(), truncatedBytes); - } - - @Test - public void deterministicUpgradeRotating() throws Exception { - group = new KeyChainGroup(params); - group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests. - long now = Utils.currentTimeSeconds(); - ECKey key1 = new ECKey(); - Utils.rollMockClock(86400); - ECKey key2 = new ECKey(); - Utils.rollMockClock(86400); - ECKey key3 = new ECKey(); - group.importKeys(key2, key1, key3); - group.upgradeToDeterministic(now + 10, null); - DeterministicSeed seed = group.getActiveKeyChain().getSeed(); - assertNotNull(seed); - // Check we used the right key: oldest non rotating. - byte[] truncatedBytes = Arrays.copyOfRange(key2.getSecretBytes(), 0, 16); - assertArrayEquals(seed.getEntropyBytes(), truncatedBytes); - } - - @Test - public void deterministicUpgradeEncrypted() throws Exception { - group = new KeyChainGroup(params); - final ECKey key = new ECKey(); - group.importKeys(key); - final KeyCrypterScrypt crypter = new KeyCrypterScrypt(); - final KeyParameter aesKey = crypter.deriveKey("abc"); - assertTrue(group.isDeterministicUpgradeRequired()); - group.encrypt(crypter, aesKey); - assertTrue(group.isDeterministicUpgradeRequired()); - try { - group.upgradeToDeterministic(0, null); - fail(); - } catch (DeterministicUpgradeRequiresPassword e) { - // Expected. - } - group.upgradeToDeterministic(0, aesKey); - assertFalse(group.isDeterministicUpgradeRequired()); - final DeterministicSeed deterministicSeed = group.getActiveKeyChain().getSeed(); - assertNotNull(deterministicSeed); - assertTrue(deterministicSeed.isEncrypted()); - byte[] entropy = checkNotNull(group.getActiveKeyChain().toDecrypted(aesKey).getSeed()).getEntropyBytes(); - // Check we used the right key: oldest non rotating. - byte[] truncatedBytes = Arrays.copyOfRange(key.getSecretBytes(), 0, 16); - assertArrayEquals(entropy, truncatedBytes); - } - - @Test - public void markAsUsed() throws Exception { - Address addr1 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - Address addr2 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertEquals(addr1, addr2); - group.markPubKeyHashAsUsed(addr1.getHash160()); - Address addr3 = group.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); - assertNotEquals(addr2, addr3); - } -} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/AddressTest.java b/core/src/test/java/org/altcoinj/core/AddressTest.java similarity index 100% rename from core/src/test/java/com/dogecoin/dogecoinj/core/AddressTest.java rename to core/src/test/java/org/altcoinj/core/AddressTest.java diff --git a/core/src/test/java/org/altcoinj/core/AuxPoWTest.java b/core/src/test/java/org/altcoinj/core/AuxPoWTest.java new file mode 100644 index 00000000..5fbbff3c --- /dev/null +++ b/core/src/test/java/org/altcoinj/core/AuxPoWTest.java @@ -0,0 +1,50 @@ +package org.bitcoinj.core; + +import java.io.ByteArrayOutputStream; + +import org.bitcoinj.params.TestNet3Params; +import org.junit.Before; +import org.junit.Test; + +import static org.bitcoinj.core.CoinbaseBlockTest.getBytes; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * AuxPoW header parsing/serialization and validation + */ +public class AuxPoWTest { + static final NetworkParameters params = TestNet3Params.get(); + + /** + * Parse the AuxPoW header from Dogecoin block #403,931. + */ + @Test + public void parseAuxPoWHeader() throws Exception { + byte[] auxpowAsBytes = getBytes(getClass().getResourceAsStream("auxpow_header.bin")); + AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, false, false); + MerkleBranch branch = auxpow.getCoinbaseBranch(); + Sha256Hash expected = new Sha256Hash("089b911f5e471c0e1800f3384281ebec5b372fbb6f358790a92747ade271ccdf"); + + assertEquals(expected, auxpow.getCoinbase().getHash()); + assertEquals(3, auxpow.getCoinbaseBranch().getSize()); + assertEquals(6, auxpow.getBlockchainBranch().getSize()); + + expected = new Sha256Hash("a22a9b01671d639fa6389f62ecf8ce69204c8ed41d5f1a745e0c5ba7116d5b4c"); + assertEquals(expected, auxpow.getParentBlockHeader().getHash()); + } + + /** + * Test serializing the AuxPoW header from Dogecoin block #403,931. + */ + @Test + public void serializeAuxPoWHeader() throws Exception { + byte[] auxpowAsBytes = getBytes(getClass().getResourceAsStream("auxpow_header.bin")); + AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, false, false); + byte[] expected = auxpowAsBytes; + byte[] actual = auxpow.bitcoinSerialize(); + + assertArrayEquals(expected, actual); + } +} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/DumpedPrivateKeyTest.java b/core/src/test/java/org/altcoinj/core/DumpedPrivateKeyTest.java similarity index 100% rename from core/src/test/java/com/dogecoin/dogecoinj/core/DumpedPrivateKeyTest.java rename to core/src/test/java/org/altcoinj/core/DumpedPrivateKeyTest.java diff --git a/core/src/test/java/org/altcoinj/core/MerkleBranchTest.java b/core/src/test/java/org/altcoinj/core/MerkleBranchTest.java new file mode 100644 index 00000000..8b2510d0 --- /dev/null +++ b/core/src/test/java/org/altcoinj/core/MerkleBranchTest.java @@ -0,0 +1,62 @@ +package org.bitcoinj.core; + +import java.io.ByteArrayOutputStream; + +import org.bitcoinj.params.TestNet3Params; +import org.junit.Before; +import org.junit.Test; + +import static org.bitcoinj.core.CoinbaseBlockTest.getBytes; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * Check merkle branch parsing and root calculation. + */ +public class MerkleBranchTest { + static final NetworkParameters params = TestNet3Params.get(); + + /** + * Parse the coinbase merkle branch from Dogecoin block #403,931. + */ + @Test + public void parseMerkleBranch() throws Exception { + byte[] branchAsBytes = getBytes(getClass().getResourceAsStream("auxpow_merkle_branch.bin")); + MerkleBranch branch = new MerkleBranch(params, (ChildMessage) null, branchAsBytes, 0); + Sha256Hash[] expected = new Sha256Hash[] { + new Sha256Hash("be079078869399faccaa764c10e9df6e9981701759ad18e13724d9ca58831348"), + new Sha256Hash("5f5bfb2c79541778499cab956a103887147f2ab5d4a717f32f9eeebd29e1f894"), + new Sha256Hash("d8c6fe42ca25076159cd121a5e20c48c1bc53ab90730083e44a334566ea6bbcb") + }; + + assertArrayEquals(expected, branch.getHashes().toArray(new Sha256Hash[branch.getSize()])); + } + + /** + * Parse the transaction merkle branch from Dogecoin block #403,931, then + * serialize it back again to verify serialization works. + */ + @Test + public void serializeMerkleBranch() throws Exception { + byte[] expected = getBytes(getClass().getResourceAsStream("auxpow_merkle_branch.bin")); + MerkleBranch branch = new MerkleBranch(params, (ChildMessage) null, expected, 0, + false, false); + byte[] actual = branch.bitcoinSerialize(); + + assertArrayEquals(expected, actual); + } + + /** + * Calculate the AuxPoW merkle branch root from Dogecoin block #403,931. + */ + @Test + public void calculateRootBranch() throws Exception { + byte[] branchAsBytes = getBytes(getClass().getResourceAsStream("auxpow_merkle_branch2.bin")); + MerkleBranch branch = new MerkleBranch(params, (ChildMessage) null, branchAsBytes, 0); + Sha256Hash txId = new Sha256Hash("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"); + Sha256Hash expected = new Sha256Hash("ce3040fdb7e37484f6a1ca4f8f5da81e6b7e404ec91102315a233e03a0c39c95"); + + assertEquals(expected, branch.calculateMerkleRoot(txId)); + } +} diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/PeerGroupTest.java b/core/src/test/java/org/altcoinj/core/PeerGroupTest.java similarity index 100% rename from core/src/test/java/com/dogecoin/dogecoinj/core/PeerGroupTest.java rename to core/src/test/java/org/altcoinj/core/PeerGroupTest.java diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/TransactionBroadcastTest.java b/core/src/test/java/org/altcoinj/core/TransactionBroadcastTest.java similarity index 100% rename from core/src/test/java/com/dogecoin/dogecoinj/core/TransactionBroadcastTest.java rename to core/src/test/java/org/altcoinj/core/TransactionBroadcastTest.java diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/VersionedChecksummedBytesTest.java b/core/src/test/java/org/altcoinj/core/VersionedChecksummedBytesTest.java similarity index 100% rename from core/src/test/java/com/dogecoin/dogecoinj/core/VersionedChecksummedBytesTest.java rename to core/src/test/java/org/altcoinj/core/VersionedChecksummedBytesTest.java diff --git a/core/src/test/java/com/dogecoin/dogecoinj/core/WalletTest.java b/core/src/test/java/org/altcoinj/core/WalletTest.java similarity index 100% rename from core/src/test/java/com/dogecoin/dogecoinj/core/WalletTest.java rename to core/src/test/java/org/altcoinj/core/WalletTest.java