org.bitcoinj.store.BlockStore

Here are the examples of the java api org.bitcoinj.store.BlockStore taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

59 Examples 7

19 Source : VersionTallyTest.java
with Apache License 2.0
from Samourai-Wallet

@Test
public void testInitialize() throws BlockStoreException {
    final BlockStore blockStore = new MemoryBlockStore(PARAMS);
    final BlockChain chain = new BlockChain(PARAMS, blockStore);
    // Build a historical chain of version 2 blocks
    long timeSeconds = 1231006505;
    StoredBlock chainHead = null;
    for (int height = 0; height < PARAMS.getMajorityWindow(); height++) {
        chainHead = FakeTxBuilder.createFakeBlock(blockStore, 2, timeSeconds, height).storedBlock;
        replacedertEquals(2, chainHead.getHeader().getVersion());
        timeSeconds += 60;
    }
    VersionTally instance = new VersionTally(PARAMS);
    instance.initialize(blockStore, chainHead);
    replacedertEquals(PARAMS.getMajorityWindow(), instance.getCountAtOrAbove(2).intValue());
}

19 Source : TestWithWallet.java
with Apache License 2.0
from Samourai-Wallet

// TODO: This needs to be somewhat rewritten - the "sendMoneyToWallet" methods aren't sending via the block chain object
/**
 * A utility clreplaced that you can derive from in your unit tests. TestWithWallet sets up an empty wallet,
 * an in-memory block store and a block chain object. It also provides helper methods for filling the wallet
 * with money in whatever ways you wish. Note that for simplicity with amounts, this clreplaced sets the default
 * fee per kilobyte to zero in setUp.
 */
public clreplaced TestWithWallet {

    protected static final NetworkParameters PARAMS = UnitTestParams.get();

    protected ECKey myKey;

    protected Address myAddress;

    protected Wallet wallet;

    protected BlockChain chain;

    protected BlockStore blockStore;

    public void setUp() throws Exception {
        BriefLogFormatter.init();
        Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
        wallet = new Wallet(PARAMS);
        myKey = wallet.currentReceiveKey();
        myAddress = myKey.toAddress(PARAMS);
        blockStore = new MemoryBlockStore(PARAMS);
        chain = new BlockChain(PARAMS, wallet, blockStore);
    }

    public void tearDown() throws Exception {
    }

    @Nullable
    protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
        if (type == null) {
            // Pending transaction
            for (Transaction tx : transactions) if (wallet.isPendingTransactionRelevant(tx))
                wallet.receivePending(tx, null);
        } else {
            FakeTxBuilder.BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, transactions);
            for (Transaction tx : transactions) wallet.receiveFromBlock(tx, bp.storedBlock, type, 0);
            if (type == AbstractBlockChain.NewBlockType.BEST_CHAIN)
                wallet.notifyNewBestBlock(bp.storedBlock);
        }
        if (transactions.length == 1)
            // Can be null if tx is a double spend that's otherwise irrelevant.
            return wallet.getTransaction(transactions[0].getHash());
        else
            return null;
    }

    @Nullable
    protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
        return sendMoneyToWallet(wallet, type, createFakeTx(PARAMS, value, toAddress));
    }

    @Nullable
    protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
        return sendMoneyToWallet(wallet, type, createFakeTx(PARAMS, value, toPubKey));
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, transactions);
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, value, myAddress);
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, value, toAddress);
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, value, toPubKey);
    }
}

19 Source : TestWithNetworkConnections.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Utility clreplaced that makes it easy to work with mock NetworkConnections.
 */
public clreplaced TestWithNetworkConnections {

    public static final int PEER_SERVERS = 5;

    protected static final NetworkParameters PARAMS = UnitTestParams.get();

    protected Context context;

    protected BlockStore blockStore;

    protected BlockChain blockChain;

    protected Wallet wallet;

    protected ECKey key;

    protected Address address;

    protected SocketAddress socketAddress;

    private NioServer[] peerServers = new NioServer[PEER_SERVERS];

    private final ClientConnectionManager channels;

    protected final BlockingQueue<InboundMessageQueuer> newPeerWriteTargetQueue = new LinkedBlockingQueue<>();

    public enum ClientType {

        NIO_CLIENT_MANAGER, BLOCKING_CLIENT_MANAGER, NIO_CLIENT, BLOCKING_CLIENT
    }

    private final ClientType clientType;

    public TestWithNetworkConnections(ClientType clientType) {
        this.clientType = clientType;
        if (clientType == ClientType.NIO_CLIENT_MANAGER)
            channels = new NioClientManager();
        else if (clientType == ClientType.BLOCKING_CLIENT_MANAGER)
            channels = new BlockingClientManager();
        else
            channels = null;
    }

    public void setUp() throws Exception {
        setUp(new MemoryBlockStore(UnitTestParams.get()));
    }

    public void setUp(BlockStore blockStore) throws Exception {
        BriefLogFormatter.init();
        Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
        this.blockStore = blockStore;
        // Allow subclreplacedes to override the wallet object with their own.
        if (wallet == null) {
            wallet = new Wallet(PARAMS);
            key = wallet.freshReceiveKey();
            address = key.toAddress(PARAMS);
        }
        blockChain = new BlockChain(PARAMS, wallet, blockStore);
        startPeerServers();
        if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
            channels.startAsync();
            channels.awaitRunning();
        }
        socketAddress = new InetSocketAddress("127.0.0.1", 1111);
    }

    protected void startPeerServers() throws IOException {
        for (int i = 0; i < PEER_SERVERS; i++) {
            startPeerServer(i);
        }
    }

    protected void startPeerServer(int i) throws IOException {
        peerServers[i] = new NioServer(new StreamConnectionFactory() {

            @Nullable
            @Override
            public StreamConnection getNewConnection(InetAddress inetAddress, int port) {
                return new InboundMessageQueuer(PARAMS) {

                    @Override
                    public void connectionClosed() {
                    }

                    @Override
                    public void connectionOpened() {
                        newPeerWriteTargetQueue.offer(this);
                    }
                };
            }
        }, new InetSocketAddress("127.0.0.1", 2000 + i));
        peerServers[i].startAsync();
        peerServers[i].awaitRunning();
    }

    public void tearDown() throws Exception {
        stopPeerServers();
    }

    protected void stopPeerServers() {
        for (int i = 0; i < PEER_SERVERS; i++) stopPeerServer(i);
    }

    protected void stopPeerServer(int i) {
        peerServers[i].stopAsync();
        peerServers[i].awaitTerminated();
    }

    protected InboundMessageQueuer connect(Peer peer, VersionMessage versionMessage) throws Exception {
        checkArgument(versionMessage.hasBlockChain());
        final AtomicBoolean doneConnecting = new AtomicBoolean(false);
        final Thread thisThread = Thread.currentThread();
        peer.addDisconnectedEventListener(new PeerDisconnectedEventListener() {

            @Override
            public void onPeerDisconnected(Peer p, int peerCount) {
                synchronized (doneConnecting) {
                    if (!doneConnecting.get())
                        thisThread.interrupt();
                }
            }
        });
        if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER)
            channels.openConnection(new InetSocketAddress("127.0.0.1", 2000), peer);
        else if (clientType == ClientType.NIO_CLIENT)
            new NioClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100);
        else if (clientType == ClientType.BLOCKING_CLIENT)
            new BlockingClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100, SocketFactory.getDefault(), null);
        else
            throw new RuntimeException();
        // Claim we are connected to a different IP that what we really are, so tx confidence broadcastBy sets work
        InboundMessageQueuer writeTarget = newPeerWriteTargetQueue.take();
        writeTarget.peer = peer;
        // Complete handshake with the peer - send/receive version(ack)s, receive bloom filter
        checkState(!peer.getVersionHandshakeFuture().isDone());
        writeTarget.sendMessage(versionMessage);
        writeTarget.sendMessage(new VersionAck());
        try {
            checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
            checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
            peer.getVersionHandshakeFuture().get();
            synchronized (doneConnecting) {
                doneConnecting.set(true);
            }
            // Clear interrupted bit in case it was set before we got into the CS
            Thread.interrupted();
        } catch (InterruptedException e) {
        // We were disconnected before we got back version/verack
        }
        return writeTarget;
    }

    protected void closePeer(Peer peer) throws Exception {
        peer.close();
    }

    protected void inbound(InboundMessageQueuer peerChannel, Message message) {
        peerChannel.sendMessage(message);
    }

    private void outboundPingAndWait(final InboundMessageQueuer p, long nonce) throws Exception {
        // Send a ping and wait for it to get to the other side
        SettableFuture<Void> pingReceivedFuture = SettableFuture.create();
        p.mapPingFutures.put(nonce, pingReceivedFuture);
        p.peer.sendMessage(new Ping(nonce));
        pingReceivedFuture.get();
        p.mapPingFutures.remove(nonce);
    }

    private void inboundPongAndWait(final InboundMessageQueuer p, final long nonce) throws Exception {
        // Receive a ping (that the Peer doesn't see) and wait for it to get through the socket
        final SettableFuture<Void> pongReceivedFuture = SettableFuture.create();
        PreMessageReceivedEventListener listener = new PreMessageReceivedEventListener() {

            @Override
            public Message onPreMessageReceived(Peer p, Message m) {
                if (m instanceof Pong && ((Pong) m).getNonce() == nonce) {
                    pongReceivedFuture.set(null);
                    return null;
                }
                return m;
            }
        };
        p.peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD, listener);
        inbound(p, new Pong(nonce));
        pongReceivedFuture.get();
        p.peer.removePreMessageReceivedEventListener(listener);
    }

    protected void pingAndWait(final InboundMessageQueuer p) throws Exception {
        final long nonce = (long) (Math.random() * Long.MAX_VALUE);
        // Start with an inbound Pong as pingAndWait often happens immediately after an inbound() call, and then wants
        // to wait on an outbound message, so we do it in the same order or we see race conditions
        inboundPongAndWait(p, nonce);
        outboundPingAndWait(p, nonce);
    }

    protected Message outbound(InboundMessageQueuer p1) throws Exception {
        pingAndWait(p1);
        return p1.nextMessage();
    }

    protected Message waitForOutbound(InboundMessageQueuer ch) throws InterruptedException {
        return ch.nextMessageBlocking();
    }

    protected Peer peerOf(InboundMessageQueuer ch) {
        return ch.peer;
    }
}

19 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Emulates receiving a valid block that builds on top of the chain.
 */
public static BlockPair createFakeBlock(BlockStore blockStore, long version, long timeSeconds, int height, Transaction... transactions) {
    try {
        return createFakeBlock(blockStore, blockStore.getChainHead(), version, timeSeconds, height, transactions);
    } catch (BlockStoreException e) {
        // Cannot happen.
        throw new RuntimeException(e);
    }
}

19 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

public static BlockPair createFakeBlock(BlockStore blockStore, StoredBlock previousStoredBlock, int height, Transaction... transactions) {
    return createFakeBlock(blockStore, previousStoredBlock, Block.BLOCK_VERSION_BIP66, Utils.currentTimeSeconds(), height, transactions);
}

19 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Emulates receiving a valid block that builds on top of the chain.
 */
public static BlockPair createFakeBlock(BlockStore blockStore, int height, Transaction... transactions) {
    return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), height, transactions);
}

19 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

public static Block makeSolvedTestBlock(BlockStore blockStore, Address coinsTo) throws BlockStoreException {
    Block b = blockStore.getChainHead().getHeader().createNextBlock(coinsTo);
    b.solve();
    return b;
}

19 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Emulates receiving a valid block that builds on top of the chain.
 */
public static BlockPair createFakeBlock(BlockStore blockStore, long version, long timeSeconds, Transaction... transactions) {
    return createFakeBlock(blockStore, version, timeSeconds, 0, transactions);
}

19 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Emulates receiving a valid block that builds on top of the chain.
 */
public static BlockPair createFakeBlock(BlockStore blockStore, Transaction... transactions) {
    return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), 0, transactions);
}

19 Source : ParseByteCacheTest.java
with Apache License 2.0
from Samourai-Wallet

public clreplaced ParseByteCacheTest {

    private static final int BLOCK_HEIGHT_GENESIS = 0;

    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 BlockStore blockStore;

    private static final NetworkParameters PARAMS = UnitTestParams.get();

    private byte[] b1Bytes;

    private byte[] b1BytesWithHeader;

    private byte[] tx1Bytes;

    private byte[] tx1BytesWithHeader;

    private byte[] tx2Bytes;

    private byte[] tx2BytesWithHeader;

    private void resetBlockStore() {
        blockStore = new MemoryBlockStore(PARAMS);
    }

    @Before
    public void setUp() throws Exception {
        Context context = new Context(PARAMS);
        Wallet wallet = new Wallet(context);
        wallet.freshReceiveKey();
        resetBlockStore();
        Transaction tx1 = createFakeTx(PARAMS, valueOf(2, 0), wallet.currentReceiveKey().toAddress(PARAMS));
        // add a second input so can test granularity of byte cache.
        Transaction prevTx = new Transaction(PARAMS);
        TransactionOutput prevOut = new TransactionOutput(PARAMS, prevTx, COIN, wallet.currentReceiveKey().toAddress(PARAMS));
        prevTx.addOutput(prevOut);
        // Connect it.
        tx1.addInput(prevOut);
        Transaction tx2 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
        Block b1 = createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS, tx1, tx2).block;
        MessageSerializer bs = PARAMS.getDefaultSerializer();
        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 = { 1, 1, 1, 2, 3, 4, 5, 6, 7 };
        byte[] b2 = { 1, 2, 3 };
        replacedertTrue(arrayContains(b1, b2));
        replacedertTrue(arrayContains(txMessage, txMessagePart));
        replacedertTrue(arrayContains(tx1BytesWithHeader, tx1Bytes));
        replacedertTrue(arrayContains(tx2BytesWithHeader, tx2Bytes));
        replacedertTrue(arrayContains(b1BytesWithHeader, b1Bytes));
        replacedertTrue(arrayContains(b1BytesWithHeader, tx1Bytes));
        replacedertTrue(arrayContains(b1BytesWithHeader, tx2Bytes));
        replacedertFalse(arrayContains(tx1BytesWithHeader, b1Bytes));
    }

    @Test
    public void testTransactionsRetain() throws Exception {
        testTransaction(MainNetParams.get(), txMessage, false, true);
        testTransaction(PARAMS, tx1BytesWithHeader, false, true);
        testTransaction(PARAMS, tx2BytesWithHeader, false, true);
    }

    @Test
    public void testTransactionsNoRetain() throws Exception {
        testTransaction(MainNetParams.get(), txMessage, false, false);
        testTransaction(PARAMS, tx1BytesWithHeader, false, false);
        testTransaction(PARAMS, tx2BytesWithHeader, false, false);
    }

    @Test
    public void testBlockAll() throws Exception {
        testBlock(b1BytesWithHeader, false, false);
        testBlock(b1BytesWithHeader, false, true);
    }

    public void testBlock(byte[] blockBytes, boolean isChild, boolean retain) throws Exception {
        // reference serializer to produce comparison serialization output after changes to
        // message structure.
        MessageSerializer bsRef = PARAMS.getSerializer(false);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        BitcoinSerializer bs = PARAMS.getSerializer(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);
        replacedertTrue(Arrays.equals(bos.toByteArray(), blockBytes));
        // check retain status survive both before and after a serialization
        replacedertEquals(retain, b1.isHeaderBytesValid());
        replacedertEquals(retain, b1.isTransactionBytesValid());
        serDeser(bs, b1, blockBytes, null, null);
        replacedertEquals(retain, b1.isHeaderBytesValid());
        replacedertEquals(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();
        if (b1.getTransactions().size() > 0) {
            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.
            replacedertEquals(retain, 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();
        // 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();
        b1.getTransactions();
        if (b1.getTransactions().size() > 0) {
            Transaction tx1 = b1.getTransactions().get(0);
            replacedertEquals(retain, 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);
        replacedertFalse(b1.isHeaderBytesValid());
        replacedertEquals(retain, 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);
            replacedertEquals(retain, 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));
                replacedertFalse(tx1.isCached());
                replacedertFalse(b1.isTransactionBytesValid());
                // confirm sibling cache status was unaffected
                if (tx1.getInputs().size() > 1) {
                    replacedertEquals(retain, tx1.getInputs().get(1).isCached());
                }
                // 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.
                replacedertFalse(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.
                replacedertFalse(b1.isTransactionBytesValid());
                // b2 should have it's cache invalidated because it has changed.
                replacedertFalse(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 retain) throws Exception {
        // reference serializer to produce comparison serialization output after changes to
        // message structure.
        MessageSerializer bsRef = params.getSerializer(false);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        BitcoinSerializer bs = params.getSerializer(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);
        replacedertTrue(Arrays.equals(bos.toByteArray(), txBytes));
        // check and retain status survive both before and after a serialization
        replacedertEquals(retain, t1.isCached());
        serDeser(bs, t1, txBytes, null, null);
        replacedertEquals(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();
        if (t1.getInputs().size() > 0) {
            TransactionInput tin = t1.getInputs().get(0);
            replacedertEquals(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));
            replacedertFalse(t1.isCached());
            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(MessageSerializer 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));
        replacedertEquals(message, m2);
        bos.reset();
        bs.serialize(m2, bos);
        byte[] b2 = bos.toByteArray();
        replacedertTrue(Arrays.equals(b1, b2));
        if (sourceBytes != null) {
            replacedertTrue(arrayContains(sourceBytes, b1));
            replacedertTrue(arrayContains(sourceBytes, b2));
        }
        if (containedBytes != null) {
            replacedertTrue(arrayContains(b1, containedBytes));
        }
        if (containingBytes != null) {
            replacedertTrue(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;
    }
}

19 Source : BlockChainTest.java
with Apache License 2.0
from Samourai-Wallet

// Handling of chain splits/reorgs are in ChainSplitTests.
public clreplaced BlockChainTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    private BlockChain testNetChain;

    private Wallet wallet;

    private BlockChain chain;

    private BlockStore blockStore;

    private Address coinbaseTo;

    private static final NetworkParameters PARAMS = UnitTestParams.get();

    private final StoredBlock[] block = new StoredBlock[1];

    private Transaction coinbaseTransaction;

    private static clreplaced TweakableTestNet2Params extends TestNet2Params {

        public void setMaxTarget(BigInteger limit) {
            maxTarget = limit;
        }
    }

    private static final TweakableTestNet2Params testNet = new TweakableTestNet2Params();

    private void resetBlockStore() {
        blockStore = new MemoryBlockStore(PARAMS);
    }

    @Before
    public void setUp() throws Exception {
        BriefLogFormatter.initVerbose();
        Context.propagate(new Context(testNet, 100, Coin.ZERO, false));
        testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
        Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
        wallet = new Wallet(PARAMS) {

            @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 (isTransactionRelevant(tx) && tx.isCoinBase()) {
                    BlockChainTest.this.coinbaseTransaction = tx;
                }
            }
        };
        wallet.freshReceiveKey();
        resetBlockStore();
        chain = new BlockChain(PARAMS, wallet, blockStore);
        coinbaseTo = wallet.currentReceiveKey().toAddress(PARAMS);
    }

    @Test
    public void testBasicChaining() throws Exception {
        // Check that we can plug a few blocks together and the futures work.
        ListenableFuture<StoredBlock> future = testNetChain.getHeightFuture(2);
        // Block 1 from the testnet.
        Block b1 = getBlock1();
        replacedertTrue(testNetChain.add(b1));
        replacedertFalse(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.
        replacedertTrue(testNetChain.add(b2));
        replacedertTrue(future.isDone());
        replacedertEquals(2, future.get().getHeight());
    }

    @Test
    public void receiveCoins() throws Exception {
        int height = 1;
        // Quick check that we can actually receive coins.
        Transaction tx1 = createFakeTx(PARAMS, COIN, wallet.currentReceiveKey().toAddress(PARAMS));
        Block b1 = createFakeBlock(blockStore, height, tx1).block;
        chain.add(b1);
        replacedertTrue(wallet.getBalance().signum() > 0);
    }

    @Test
    public void unconnectedBlocks() throws Exception {
        Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        Block b3 = b2.createNextBlock(coinbaseTo);
        // Connected.
        replacedertTrue(chain.add(b1));
        // Unconnected but stored. The head of the chain is still b1.
        replacedertFalse(chain.add(b3));
        replacedertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
        // Add in the middle block.
        replacedertTrue(chain.add(b2));
        replacedertEquals(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 = PARAMS.getGenesisBlock();
        Utils.setMockClock(System.currentTimeMillis() / 1000);
        for (int height = 0; height < PARAMS.getInterval() - 1; height++) {
            Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
            replacedertTrue(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, 1, Utils.currentTimeSeconds(), PARAMS.getInterval()));
            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, 1, Utils.currentTimeSeconds(), PARAMS.getInterval() + 1);
        b.setDifficultyTarget(0x201fFFFFL);
        b.solve();
        replacedertTrue(chain.add(b));
    // Successfully traversed a difficulty transition period.
    }

    @Test
    public void badDifficulty() throws Exception {
        replacedertTrue(testNetChain.add(getBlock1()));
        Block b2 = getBlock2();
        replacedertTrue(testNetChain.add(b2));
        Block bad = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
        // Merkle root can be anything here, doesn't matter.
        bad.setMerkleRoot(Sha256Hash.wrap("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 preplaced, 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) {
            replacedertTrue(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) {
            replacedertTrue(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 that version 2 blocks are rejected once version 3 blocks are a super
     * majority.
     */
    @Test
    public void badBip66Version() throws Exception {
        testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP34, Block.BLOCK_VERSION_BIP66);
    }

    /**
     * Test that version 3 blocks are rejected once version 4 blocks are a super
     * majority.
     */
    @Test
    public void badBip65Version() throws Exception {
        testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP66, Block.BLOCK_VERSION_BIP65);
    }

    private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
        final BlockStore versionBlockStore = new MemoryBlockStore(PARAMS);
        final BlockChain versionChain = new BlockChain(PARAMS, versionBlockStore);
        // Build a historical chain of version 3 blocks
        long timeSeconds = 1231006505;
        int height = 0;
        FakeTxBuilder.BlockPair chainHead = null;
        // Put in just enough v2 blocks to be a minority
        for (height = 0; height < (PARAMS.getMajorityWindow() - PARAMS.getMajorityRejectBlockOutdated()); height++) {
            chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
            versionChain.add(chainHead.block);
            timeSeconds += 60;
        }
        // Fill the rest of the window with v3 blocks
        for (; height < PARAMS.getMajorityWindow(); height++) {
            chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
            versionChain.add(chainHead.block);
            timeSeconds += 60;
        }
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
        // Trying to add a new v2 block should result in rejection
        thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
        try {
            versionChain.add(chainHead.block);
        } catch (final VerificationException ex) {
            throw (Exception) ex.getCause();
        }
    }

    @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 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        Block b3 = b2.createNextBlock(coinbaseTo);
        replacedertTrue(chain.add(b1));
        replacedertEquals(b1, block[0].getHeader());
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2, block[0].getHeader());
        replacedertTrue(chain.add(b3));
        replacedertEquals(b3, block[0].getHeader());
        replacedertEquals(b3, chain.getChainHead().getHeader());
        replacedertTrue(chain.add(b2));
        replacedertEquals(b3, chain.getChainHead().getHeader());
        // Wallet was NOT called with the new block because the duplicate add was spotted.
        replacedertEquals(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(PARAMS);
        Block b1 = PARAMS.getGenesisBlock().createNextBlock(somebodyElse);
        ECKey key = wallet.freshReceiveKey();
        Address addr = key.toAddress(PARAMS);
        // Create a tx that gives us some coins, and another that spends it to someone else in the same block.
        Transaction t1 = FakeTxBuilder.createFakeTx(PARAMS, COIN, addr);
        Transaction t2 = new Transaction(PARAMS);
        t2.addInput(t1.getOutputs().get(0));
        t2.addOutput(valueOf(2, 0), somebodyElse);
        b1.addTransaction(t1);
        b1.addTransaction(t2);
        b1.solve();
        chain.add(b1);
        replacedertEquals(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(PARAMS);
        ECKey receiveKey = wallet2.freshReceiveKey();
        int height = 1;
        chain.addWallet(wallet2);
        Address addressToSendTo = receiveKey.toAddress(PARAMS);
        // Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
        Block b1 = PARAMS.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
        chain.add(b1);
        // Check a transaction has been received.
        replacedertNotNull(coinbaseTransaction);
        // The coinbase tx is not yet available to spend.
        replacedertEquals(Coin.ZERO, wallet.getBalance());
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
        replacedertTrue(!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 < PARAMS.getSpendableCoinbaseDepth() - 2; i++) {
            // Non relevant tx - just for fake block creation.
            Transaction tx2 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
            Block b2 = createFakeBlock(blockStore, height++, tx2).block;
            chain.add(b2);
            // Wallet still does not have the coinbase transaction available for spend.
            replacedertEquals(Coin.ZERO, wallet.getBalance());
            replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
            // The coinbase transaction is still not mature.
            replacedertTrue(!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(PARAMS, COIN, new ECKey().toAddress(PARAMS));
        Block b3 = createFakeBlock(blockStore, height++, tx3).block;
        chain.add(b3);
        // Wallet now has the coinbase transaction available for spend.
        replacedertEquals(wallet.getBalance(), FIFTY_COINS);
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
        replacedertTrue(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));
        replacedertNotNull(coinbaseSend2);
        // Commit the coinbaseSpend to the first wallet and check the balances decrement.
        wallet.commitTx(coinbaseSend2);
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN);
        // Available balance is zero as change has not been received from a block yet.
        replacedertEquals(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, height++, coinbaseSend2).block;
        chain.add(b4);
        replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);
        // Check the balances in the second wallet.
        replacedertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0));
        replacedertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0));
    }

    // Some blocks from the test net.
    private static Block getBlock2() throws Exception {
        Block b2 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
        b2.setMerkleRoot(Sha256Hash.wrap("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
        b2.setNonce(2642058077L);
        b2.setTime(1296734343L);
        b2.setPrevBlockHash(Sha256Hash.wrap("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
        replacedertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashreplacedtring());
        b2.verifyHeader();
        return b2;
    }

    private static Block getBlock1() throws Exception {
        Block b1 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
        b1.setMerkleRoot(Sha256Hash.wrap("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
        b1.setNonce(236038445);
        b1.setTime(1296734340);
        b1.setPrevBlockHash(Sha256Hash.wrap("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
        replacedertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashreplacedtring());
        b1.verifyHeader();
        return b1;
    }

    @Test
    public void estimatedBlockTime() throws Exception {
        NetworkParameters params = MainNetParams.get();
        BlockChain prod = new BlockChain(new Context(params), new MemoryBlockStore(params));
        Date d = prod.estimateBlockTime(200000);
        // The actual date of block 200,000 was 2012-09-22 10:47:00
        replacedertEquals(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;
        // Exactly
        replacedertTrue(0 == chain.getFalsePositiveRate());
        chain.trackFalsePositives(55);
        replacedertEquals(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%
        replacedertEquals(0.1, chain.getFalsePositiveRate(), 0.01);
        // Check that we get repeatable results after a reset
        chain.resetFalsePositiveEstimate();
        // Exactly
        replacedertTrue(0 == chain.getFalsePositiveRate());
        chain.trackFalsePositives(55);
        replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
        chain.trackFilteredTransactions(550);
        replacedertEquals(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 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        // Add block 1, no frills.
        replacedertTrue(chain.add(b1));
        replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(1, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Add block 2 while wallet is disconnected, to simulate crash.
        chain.removeWallet(wallet);
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(2, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Add wallet back. This will detect the height mismatch and repair the damage done.
        chain.addWallet(wallet);
        replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(1, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Now add block 2 correctly.
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(2, chain.getBestChainHeight());
        replacedertEquals(2, wallet.getLastBlockSeenHeight());
    }
}

19 Source : VersionTally.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Initialize the version tally from the block store. Note this does not
 * search backwards past the start of the block store, so if starting from
 * a checkpoint this may not fill the window.
 *
 * @param blockStore block store to load blocks from.
 * @param chainHead current chain tip.
 */
public void initialize(final BlockStore blockStore, final StoredBlock chainHead) throws BlockStoreException {
    StoredBlock versionBlock = chainHead;
    final Stack<Long> versions = new Stack<>();
    // We don't know how many blocks back we can go, so load what we can first
    versions.push(versionBlock.getHeader().getVersion());
    for (int headOffset = 0; headOffset < versionWindow.length; headOffset++) {
        versionBlock = versionBlock.getPrev(blockStore);
        if (null == versionBlock) {
            break;
        }
        versions.push(versionBlock.getHeader().getVersion());
    }
    // Replay the versions into the tally
    while (!versions.isEmpty()) {
        add(versions.pop());
    }
}

19 Source : TestNet3Params.java
with Apache License 2.0
from Samourai-Wallet

@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore) throws VerificationException, BlockStoreException {
    if (!isDifficultyTransitionPoint(storedPrev.getHeight()) && nextBlock.getTime().after(testnetDiffDate)) {
        Block prev = storedPrev.getHeader();
        // After 15th February 2012 the rules on the testnet change to avoid people running up the difficulty
        // and then leaving, making it too hard to mine a block. On non-difficulty transition points, easy
        // blocks are allowed if there has been a span of 20 minutes without one.
        final long timeDelta = nextBlock.getTimeSeconds() - prev.getTimeSeconds();
        // There is an integer underflow bug in bitcoin-qt that means mindiff blocks are accepted when time
        // goes backwards.
        if (timeDelta >= 0 && timeDelta <= NetworkParameters.TARGET_SPACING * 2) {
            // Walk backwards until we find a block that doesn't have the easiest proof of work, then check
            // that difficulty is equal to that one.
            StoredBlock cursor = storedPrev;
            while (!cursor.getHeader().equals(getGenesisBlock()) && cursor.getHeight() % getInterval() != 0 && cursor.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) cursor = cursor.getPrev(blockStore);
            BigInteger cursorTarget = cursor.getHeader().getDifficultyTargetAsInteger();
            BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger();
            if (!cursorTarget.equals(newTarget))
                throw new VerificationException("Testnet block transition that is not allowed: " + Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " + Long.toHexString(nextBlock.getDifficultyTarget()));
        }
    } else {
        super.checkDifficultyTransitions(storedPrev, nextBlock, blockStore);
    }
}

19 Source : StoredBlock.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Given a block store, looks up the previous block in this chain. Convenience method for doing
 * <tt>store.get(this.getHeader().getPrevBlockHash())</tt>.
 *
 * @return the previous block in the chain or null if it was not found in the store.
 */
public StoredBlock getPrev(BlockStore store) throws BlockStoreException {
    return store.get(getHeader().getPrevBlockHash());
}

19 Source : BlockChain.java
with Apache License 2.0
from Samourai-Wallet

// TODO: Rename this clreplaced to SPVBlockChain at some point.
/**
 * A BlockChain implements the <i>simplified payment verification</i> mode of the Bitcoin protocol. It is the right
 * choice to use for programs that have limited resources as it won't verify transactions signatures or attempt to store
 * all of the block chain. Really, this clreplaced should be called SPVBlockChain but for backwards compatibility it is not.
 */
public clreplaced BlockChain extends AbstractBlockChain {

    /**
     * Keeps a map of block hashes to StoredBlocks.
     */
    protected final BlockStore blockStore;

    /**
     * <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
     * one from scratch, or you can deserialize a saved wallet from disk using
     * {@link Wallet#loadFromFile(java.io.File, WalletExtension...)}</p>
     *
     * <p>For the store, you should use {@link org.bitcoinj.store.SPVBlockStore} or you could also try a
     * {@link org.bitcoinj.store.MemoryBlockStore} if you want to hold all headers in RAM and don't care about
     * disk serialization (this is rare).</p>
     */
    public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
        addWallet(wallet);
    }

    /**
     * See {@link #BlockChain(Context, Wallet, BlockStore)}}
     */
    public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallet, blockStore);
    }

    /**
     * Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
     * and receiving coins but rather, just want to explore the network data structures.
     */
    public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * See {@link #BlockChain(Context, BlockStore)}
     */
    public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
        this(params, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * Constructs a BlockChain connected to the given list of listeners and a store.
     */
    public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        super(params, wallets, blockStore);
        this.blockStore = blockStore;
    }

    /**
     * See {@link #BlockChain(Context, List, BlockStore)}
     */
    public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallets, blockStore);
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected void rollbackBlockStore(int height) throws BlockStoreException {
        lock.lock();
        try {
            int currentHeight = getBestChainHeight();
            checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
            if (height == currentHeight)
                // nothing to do
                return;
            // Look for the block we want to be the new chain head
            StoredBlock newChainHead = blockStore.getChainHead();
            while (newChainHead.getHeight() > height) {
                newChainHead = newChainHead.getPrev(blockStore);
                if (newChainHead == null)
                    throw new BlockStoreException("Unreachable height");
            }
            // Modify store directly
            blockStore.put(newChainHead);
            this.setChainHead(newChainHead);
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean shouldVerifyTransactions() {
        return false;
    }

    @Override
    protected TransactionOutputChanges connectTransactions(int height, Block block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void disconnectTransactions(StoredBlock block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
        blockStore.setChainHead(chainHead);
    }

    @Override
    protected void notSettingChainHead() throws BlockStoreException {
    // We don't use DB transactions here, so we don't need to do anything
    }

    @Override
    protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
        return blockStore.get(hash);
    }

    @Override
    public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
        boolean success = super.add(block);
        if (success) {
            trackFilteredTransactions(block.getTransactionCount());
        }
        return success;
    }
}

19 Source : Blocks.java
with MIT License
from ppkpub

public clreplaced Blocks implements Runnable {

    public static NetworkParameters params;

    public Logger logger = LoggerFactory.getLogger(Blocks.clreplaced);

    public static boolean bRemoteWalletMode = false;

    public Wallet wallet = null;

    public PeerGroup peerGroup;

    public BlockChain blockChain;

    public BlockStore blockStore;

    public Boolean working = false;

    public Boolean parsing = false;

    public Boolean initializing = false;

    public Boolean initialized = false;

    public Integer parsingBlock = 0;

    public Integer versionCheck = 0;

    public Integer bitcoinBlock = 0;

    public Integer ppkBlock = 0;

    public String statusMessage = "";

    private static Blocks instance = null;

    private static HashMap<String, List<UnspentOutput>> cachedLastUnspentList = new HashMap<String, List<UnspentOutput>>();

    public static Blocks getInstanceSkipVersionCheck() {
        System.out.println("aaaaa");
        if (instance == null) {
            System.out.println("bbbbb");
            instance = new Blocks();
        }
        System.out.println("ccccc");
        return instance;
    }

    public static Blocks getInstanceFresh() {
        if (instance == null) {
            instance = new Blocks();
            instance.versionCheck();
        }
        return instance;
    }

    public static Blocks getInstanceAndWait() {
        if (instance == null) {
            instance = new Blocks();
            instance.versionCheck();
            new Thread() {

                public void run() {
                    instance.init();
                }
            }.start();
        }
        instance.follow();
        return instance;
    }

    public static Blocks getInstance() {
        if (instance == null) {
            instance = new Blocks();
            instance.versionCheck();
            new Thread() {

                public void run() {
                    instance.init();
                }
            }.start();
        }
        if (!instance.working && instance.initialized) {
            new Thread() {

                public void run() {
                    instance.follow();
                }
            }.start();
        }
        return instance;
    }

    public void versionCheck() {
        versionCheck(false);
    }

    public void versionCheck(Boolean autoUpdate) {
        Integer minMajorVersion = Util.getMinMajorVersion();
        Integer minMinorVersion = Util.getMinMinorVersion();
        if (Config.majorVersion < minMajorVersion || (Config.majorVersion.equals(minMajorVersion) && Config.minorVersion < minMinorVersion)) {
            if (autoUpdate) {
                statusMessage = "Version is out of date, updating now";
                logger.info(statusMessage);
                try {
                    Runtime.getRuntime().exec("java -jar update/update.jar");
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } else {
                logger.info("Version is out of date. Please upgrade to version " + Util.getMinVersion() + ".");
            }
            System.exit(0);
        }
    }

    @Override
    public void run() {
        while (true) {
            logger.info("Looping blocks");
            Blocks.getInstance();
            try {
                // once a minute, we run blocks.follow()
                Thread.sleep(1000 * 60);
            } catch (InterruptedException e) {
                logger.error("Error during loop: " + e.toString());
            }
        }
    }

    public void init() {
        if (!initializing) {
            initializing = true;
            Locale.setDefault(new Locale("en", "US"));
            params = MainNetParams.get();
            try {
                if (!bRemoteWalletMode) {
                    // 本地钱包模式
                    File localWalletFile = new File(Config.walletFile);
                    if (localWalletFile.exists()) {
                        statusMessage = Language.getLangLabel("Found wallet file");
                        logger.info(statusMessage);
                        wallet = Wallet.loadFromFile(localWalletFile);
                    } else {
                        statusMessage = Language.getLangLabel("Creating new wallet file");
                        logger.info(statusMessage);
                        wallet = new Wallet(params);
                        // ForTest,20190616
                        String testBatchImportPrvFileName = "resources/db/batch_prv_list.txt";
                        File testBatchImportPrvFile = new File(testBatchImportPrvFileName);
                        if (testBatchImportPrvFile.exists()) {
                            // 存在指定的批量私钥文件,则批量导入初始化钱包
                            try {
                                InputStreamReader read = new InputStreamReader(new FileInputStream(testBatchImportPrvFileName), "ISO-8859-1");
                                BufferedReader reader = new BufferedReader(read);
                                String line;
                                int imported_counter = 0;
                                while ((line = reader.readLine()) != null) {
                                    if (line.startsWith("L") || line.startsWith("K") || line.startsWith("5")) {
                                        try {
                                            importPrivateKey(line);
                                            imported_counter++;
                                        } catch (Exception e) {
                                            logger.error("Blocks.init() testBatchImportPrvFile meet invalid prvkey:" + line);
                                        }
                                    }
                                }
                                reader.close();
                                read.close();
                                logger.info("Batch imported " + imported_counter + " addresses. ");
                            } catch (Exception e) {
                                logger.error("Blocks.init() testBatchImportPrvFile failed:" + e.toString());
                            }
                        } else {
                            // 创建默认的单个地址初始钱包
                            ECKey newKey = new ECKey();
                            importPrivateKey(newKey);
                        }
                    // newKey.setCreationTimeSeconds(Config.ppkToolCreationTime);
                    // wallet.addKey(newKey);
                    // wallet.saveToFile(localWalletFile);
                    }
                }
                String fileBTCdb = Config.dbDirPrefix + Config.appName.toLowerCase() + ".h2.db";
                String fileODINdb = Config.defaultSqliteFile;
                if (!new File(fileODINdb).exists()) {
                    statusMessage = "Downloading ODIN database";
                    logger.info(statusMessage);
                    Util.downloadToFile(Config.downloadURL + Config.appName.toLowerCase() + "-" + Config.majorVersionDB.toString() + ".db", fileODINdb);
                }
                statusMessage = Language.getLangLabel("Downloading Bitcoin blocks");
                blockStore = new H2FullPrunedBlockStore(params, Config.dbDirPrefix + Config.appName.toLowerCase(), 2000);
                if (wallet == null) {
                    blockChain = new BlockChain(params, blockStore);
                    peerGroup = new PeerGroup(params, blockChain);
                    peerGroup.setFastCatchupTimeSecs(Config.ppkToolCreationTime);
                } else {
                    blockChain = new BlockChain(params, wallet, blockStore);
                    peerGroup = new PeerGroup(params, blockChain);
                    peerGroup.setFastCatchupTimeSecs(Config.ppkToolCreationTime);
                // peerGroup.addWallet(wallet); //不需要同步保存历史交易到wallet文件中,减少损坏可能
                // wallet.autosaveToFile(new File(Config.walletFile), 1, TimeUnit.MINUTES, null);
                }
                peerGroup.addPeerDiscovery(new DnsDiscovery(params));
                // peerGroup.startAndWait(); //for bitcoinj0.14
                peerGroup.start();
                peerGroup.addEventListener(new PPkPeerEventListener());
                peerGroup.downloadBlockChain();
                while (!hasChainHead()) {
                    try {
                        logger.info("Blockstore doesn't yet have a chain head, so we are sleeping.");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
                Database db = Database.getInstance();
                try {
                    Integer lastParsedBlock = Util.getLastParsedBlock();
                    if (lastParsedBlock.equals(0)) {
                        db.executeUpdate("CREATE TABLE IF NOT EXISTS sys_parameters (para_name VARCHAR(32) PRIMARY KEY, para_value TEXT )");
                        lastParsedBlock = Util.getLastBlock();
                        Util.updateLastParsedBlock(lastParsedBlock);
                    }
                } catch (Exception e) {
                    logger.error(e.toString());
                }
                ODIN.init();
            } catch (Exception e) {
                logger.error("Error during init: " + e.toString());
                // e.printStackTrace();
                System.exit(-1);
            }
            initialized = true;
            initializing = false;
        }
    }

    public void deleteDatabases() {
        logger.info("Deleting Bitcoin and ODIN databases");
        String fileBTCdb = Config.dbDirPrefix + Config.appName.toLowerCase() + ".h2.db";
        new File(fileBTCdb).delete();
        String fileODINdb = Config.defaultSqliteFile;
        new File(fileODINdb).delete();
    }

    public Boolean hasChainHead() {
        try {
            Integer blockHeight = blockStore.getChainHead().getHeight();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public void follow() {
        follow(false);
    }

    public void follow(Boolean force) {
        logger.info("Working status: " + working);
        logger.info("initialized: " + initialized);
        if ((!working && initialized) || force) {
            statusMessage = "Checking block height";
            logger.info(statusMessage);
            if (!force) {
                working = true;
            }
            try {
                // catch ODIN up to Bitcoin
                Integer blockHeight = blockStore.getChainHead().getHeight();
                Integer lastBlock = Util.getLastBlock();
                Integer lastBlockTime = Util.getLastBlockTimestamp();
                bitcoinBlock = blockHeight;
                ppkBlock = lastBlock;
                if (lastBlock == 0) {
                    lastBlock = Config.firstBlock - 1;
                }
                Integer nextBlock = lastBlock + 1;
                logger.info("Bitcoin block height: " + blockHeight);
                logger.info("PPk block height: " + lastBlock);
                if (lastBlock < blockHeight) {
                    // traverse new blocks
                    parsing = true;
                    Integer blocksToScan = blockHeight - lastBlock;
                    List<Sha256Hash> blockHashes = new ArrayList<Sha256Hash>();
                    Block block = peerGroup.getDownloadPeer().getBlock(blockStore.getChainHead().getHeader().getHash()).get(59, TimeUnit.SECONDS);
                    while (blockStore.get(block.getHash()).getHeight() > lastBlock) {
                        blockHashes.add(block.getHash());
                        block = blockStore.get(block.getPrevBlockHash()).getHeader();
                    }
                    for (int i = blockHashes.size() - 1; i >= 0; i--) {
                        // traverse blocks in reverse order
                        block = peerGroup.getDownloadPeer().getBlock(blockHashes.get(i)).get(59, TimeUnit.SECONDS);
                        blockHeight = blockStore.get(block.getHash()).getHeight();
                        ppkBlock = blockHeight;
                        statusMessage = "Catching ODIN up to Bitcoin " + Util.format((blockHashes.size() - i) / ((double) blockHashes.size()) * 100.0) + "%";
                        logger.info("Catching ODIN up to Bitcoin (block " + blockHeight.toString() + "): " + Util.format((blockHashes.size() - i) / ((double) blockHashes.size()) * 100.0) + "%");
                        importBlock(block, blockHeight);
                    }
                    parsing = false;
                }
            } catch (Exception e) {
                logger.error("Error during follow: " + e.toString());
                e.printStackTrace();
            }
            // Ensure to parse new imported blocks while follow finished or failed
            try {
                Integer lastImportedBlock = Util.getLastBlock();
                Integer lastImportedBlockTime = Util.getLastBlockTimestamp();
                Integer lastParsedBlock = Util.getLastParsedBlock();
                if (lastParsedBlock < lastImportedBlock) {
                    parsing = true;
                    parseFrom(lastParsedBlock + 1, true);
                    parsing = false;
                }
            } catch (Exception e) {
                logger.error("Error during parse: " + e.toString());
                e.printStackTrace();
            }
            if (!force) {
                working = false;
            }
        }
    }

    public void reDownloadBlockTransactions(Integer blockHeight) {
        Database db = Database.getInstance();
        ResultSet rs = db.executeQuery("select * from blocks where block_index='" + blockHeight.toString() + "';");
        try {
            if (rs.next()) {
                Block block = peerGroup.getDownloadPeer().getBlock(new Sha256Hash(rs.getString("block_hash"))).get();
                db.executeUpdate("delete from transactions where block_index='" + blockHeight.toString() + "';");
                Integer txSnInBlock = 0;
                for (Transaction tx : block.getTransactions()) {
                    importPPkTransaction(tx, txSnInBlock, block, blockHeight);
                    txSnInBlock++;
                }
            }
        } catch (Exception e) {
        }
    }

    public void importBlock(Block block, Integer blockHeight) {
        statusMessage = "Importing block " + blockHeight;
        logger.info(statusMessage);
        Database db = Database.getInstance();
        ResultSet rs = db.executeQuery("select * from blocks where block_hash='" + block.getHashreplacedtring() + "';");
        try {
            if (!rs.next()) {
                db.executeUpdate("INSERT INTO blocks(block_index,block_hash,block_time,block_nonce) VALUES('" + blockHeight.toString() + "','" + block.getHashreplacedtring() + "','" + block.getTimeSeconds() + "','" + block.getNonce() + "')");
            }
            Integer txSnInBlock = 0;
            for (Transaction tx : block.getTransactions()) {
                importPPkTransaction(tx, txSnInBlock, block, blockHeight);
                txSnInBlock++;
            }
        } catch (SQLException e) {
        }
    }

    public void reparse() {
        reparse(false);
    }

    public void reparse(final Boolean force) {
        Database db = Database.getInstance();
        db.executeUpdate("delete from odins;");
        db.executeUpdate("delete from odin_update_logs;");
        db.executeUpdate("delete from balances;");
        db.executeUpdate("delete from sends;");
        db.executeUpdate("delete from messages;");
        db.executeUpdate("delete from sys_parameters;");
        new Thread() {

            public void run() {
                parseFrom(Config.firstBlock, force);
            }
        }.start();
    }

    public void parseFrom(Integer blockNumber) {
        parseFrom(blockNumber, false);
    }

    public void parseFrom(Integer blockNumber, Boolean force) {
        if (!working || force) {
            parsing = true;
            if (!force) {
                working = true;
            }
            Database db = Database.getInstance();
            ResultSet rs = db.executeQuery("select * from blocks where block_index>=" + blockNumber.toString() + " order by block_index asc;");
            try {
                while (rs.next()) {
                    Integer blockIndex = rs.getInt("block_index");
                    Integer blockTime = rs.getInt("block_time");
                    parseBlock(blockIndex, blockTime);
                    Util.updateLastParsedBlock(blockIndex);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            if (!force) {
                working = false;
            }
            parsing = false;
        }
    }

    public void parseBlock(Integer blockIndex, Integer blockTime) {
        Database db = Database.getInstance();
        ResultSet rsTx = db.executeQuery("select * from transactions where block_index=" + blockIndex.toString() + " order by tx_index asc;");
        parsingBlock = blockIndex;
        statusMessage = "\n++++++++++++++++++++++++++++++++++\n Parsing block " + blockIndex.toString() + "\n++++++++++++++++++++++++++++++++++\n";
        logger.info(statusMessage);
        try {
            while (rsTx.next()) {
                Integer txIndex = rsTx.getInt("tx_index");
                String source = rsTx.getString("source");
                String destination = rsTx.getString("destination");
                BigInteger amount_satoshi = BigInteger.valueOf(rsTx.getInt("btc_amount"));
                byte[] odin_data = Util.hexStringToBytes(rsTx.getString("data"));
                Integer prefix_type = rsTx.getInt("prefix_type");
                if (1 == prefix_type) {
                    // PPk ODIN
                    Byte messageType = getPPkMessageTypeFromTransaction(odin_data);
                    List<Byte> message = getPPkMessageFromTransaction(odin_data);
                    logger.info("\n--------------------\n Parsing PPk txIndex " + txIndex.toString() + "\n------------\n");
                    if (messageType != null && message != null) {
                        logger.info("\n--------------------\n Parsing PPk messageType " + messageType.toString() + "\n------------\n");
                        if (messageType == ODIN.id) {
                            ODIN.parse(txIndex, message);
                        } else if (messageType == OdinUpdate.id) {
                            OdinUpdate.parse(txIndex, message);
                        }
                    }
                } else {
                // normal bitcoin operation
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /*
  public Integer getDBMinorVersion() {
    Database db = Database.getInstance();
    ResultSet rs = db.executeQuery("PRAGMA user_version;");
    try {
      while(rs.next()) {
        return rs.getInt("user_version");
      }
    } catch (SQLException e) {
    }  
    return 0;
  }

  public void updateMinorVersion() {
    // Update minor version
    Database db = Database.getInstance();
    db.executeUpdate("PRAGMA user_version = "+Config.minorVersionDB.toString());
  }
*/
    public Integer getHeight() {
        try {
            Integer height = blockStore.getChainHead().getHeight();
            return height;
        } catch (BlockStoreException e) {
        }
        return 0;
    }

    public String importPrivateKey(ECKey key) throws Exception {
        String address = "";
        logger.info("Importing private key");
        address = key.toAddress(params).toString();
        logger.info("Importing address " + address);
        if (wallet.getImportedKeys().contains(key)) {
            wallet.removeKey(key);
        }
        wallet.addKey(key);
        wallet.saveToFile(new File(Config.walletFile));
        /*
    try {
      importTransactionsFromAddress(address);
    } catch (Exception e) {
      throw new Exception(e.getMessage());
    }
     */
        return address;
    }

    public String importPrivateKey(String privateKey) throws Exception {
        DumpedPrivateKey dumpedPrivateKey;
        String address = "";
        ECKey key = null;
        try {
            dumpedPrivateKey = new DumpedPrivateKey(params, privateKey);
            key = dumpedPrivateKey.getKey();
            return importPrivateKey(key);
        } catch (AddressFormatException e) {
            throw new Exception("Blocks.importPrivateKey() failed:" + e.getMessage());
        }
    }

    /*
  public void importTransactionsFromAddress(String address) throws Exception {
    logger.info("Importing transactions");
    try {
      wallet.addWatchedAddress(new Address(params, address));
    } catch (AddressFormatException e) {
    }
    List<Map.Entry<String,String>> txsInfo = Util.getTransactions(address);
    BigInteger balance = BigInteger.ZERO;
    BigInteger balanceSent = BigInteger.ZERO;
    BigInteger balanceReceived = BigInteger.ZERO;
    Integer transactionCount = 0;
    for (Map.Entry<String,String> txHashBlockHash : txsInfo) {
      String txHash = txHashBlockHash.getKey();
      String blockHash = txHashBlockHash.getValue();
      try {
        Block block = peerGroup.getDownloadPeer().getBlock(new Sha256Hash(blockHash)).get();
        List<Transaction> txs = block.getTransactions();
        for (Transaction tx : txs) {
          if (tx.getHashreplacedtring().equals(txHash)){// && wallet.isPendingTransactionRelevant(tx)) {
            transactionCount ++;
            wallet.receivePending(tx, peerGroup.getDownloadPeer().downloadDependencies(tx).get());
            balanceReceived = balanceReceived.add(tx.getValueSentToMe(wallet));
            balanceSent = balanceSent.add(tx.getValueSentFromMe(wallet));
            balance = balance.add(tx.getValueSentToMe(wallet));
            balance = balance.subtract(tx.getValueSentFromMe(wallet));
          }
        }
      } catch (InterruptedException e) {
        throw new Exception(e.getMessage());
      } catch (ExecutionException e) {        
        throw new Exception(e.getMessage());
      }
    }
    logger.info("Address balance: "+balance);    
  }
   */
    public Transaction transaction(OdinTransctionData odin_tx_data) throws Exception {
        byte[] data = Util.hexStringToBytes(odin_tx_data.data_hex);
        return transaction(odin_tx_data.source, odin_tx_data.destination, odin_tx_data.amount_satoshi, odin_tx_data.fee_satoshi, odin_tx_data.mark_hex, data);
    }

    public Transaction transaction(String source, String destination, BigInteger amount_satoshi, BigInteger fee, String markPubkeyHexStr, byte[] data) throws Exception {
        Transaction tx = new Transaction(params);
        if (!destination.equals("") && amount_satoshi.compareTo(BigInteger.valueOf(Config.dustSize)) < 0) {
            tx.verify();
            return tx;
        }
        List<Byte> dataArrayList = (data == null) ? new ArrayList<Byte>() : Util.toByteArrayList(data);
        int odin_data_length = dataArrayList.size();
        boolean isOdinTransaction = odin_data_length > 0;
        BigInteger totalOutput = fee;
        BigInteger totalInput = BigInteger.ZERO;
        try {
            if (!destination.equals("") && amount_satoshi.compareTo(BigInteger.ZERO) > 0) {
                totalOutput = totalOutput.add(amount_satoshi);
                tx.addOutput(Coin.valueOf(amount_satoshi.longValue()), new Address(params, destination));
            }
        } catch (AddressFormatException e) {
            throw new Exception("Failed to generate output for " + destination);
        }
        ECKey source_key = null;
        if (bRemoteWalletMode) {
        // source_key=new ECKey(null,Util.hexStringToBytes(source_pubkey_hex));
        } else {
            for (ECKey key : wallet.getImportedKeys()) {
                try {
                    if (key.toAddress(params).equals(new Address(params, source))) {
                        source_key = key;
                        break;
                    }
                } catch (AddressFormatException e) {
                }
            }
        }
        if (null == source_key)
            return null;
        // 组织多重交易来嵌入所需存放的数据
        if (isOdinTransaction) {
            int max_tx_num = Config.MAX_MULTISIG_TX_NUM;
            int max_multisig_n = Config.MAX_N;
            int from = 0;
            for (int tt = 0; tt == 0 || (tt < max_tx_num && from < odin_data_length - Config.MAX_OP_RETURN_LENGTH); tt++) {
                List<ECKey> keys = new ArrayList<ECKey>();
                keys.add(source_key);
                if (tt == 0) {
                    // 第一条多重交易的第二个公钥固定为指定特征公钥
                    keys.add(new ECKey(null, Util.hexStringToBytes(markPubkeyHexStr)));
                }
                for (int mm = keys.size(); mm < max_multisig_n && ((tt == 0 && from < odin_data_length) || (tt > 0 && from < odin_data_length - Config.MAX_OP_RETURN_LENGTH)); mm++, from += Config.PPK_PUBKEY_EMBED_DATA_MAX_LENGTH) {
                    int embed_data_length = Math.min(Config.PPK_PUBKEY_EMBED_DATA_MAX_LENGTH, odin_data_length - from);
                    List<Byte> chunk = new ArrayList<Byte>(dataArrayList.subList(from, from + embed_data_length));
                    byte[] tmp_pub_key = Util.generateValidPubkey(Util.toByteArray(chunk));
                    if (tmp_pub_key == null) {
                        throw new Exception("Unable to generate valid pubkey for embedding data.Please change your request contents!");
                    }
                    keys.add(new ECKey(null, tmp_pub_key));
                }
                Script script = ScriptBuilder.createMultiSigOutputScript(1, keys);
                tx.addOutput(Coin.valueOf(BigInteger.valueOf(Config.dustSize).longValue()), script);
                totalOutput = totalOutput.add(BigInteger.valueOf(Config.dustSize));
            }
            // 使用op_return对应的备注脚本空间来嵌入剩余ODIN数据
            int last_data_length = odin_data_length - from;
            if (last_data_length > Config.MAX_OP_RETURN_LENGTH) {
                throw new Exception("Too big embed data.(Should be less than " + Config.MAX_ODIN_DATA_LENGTH + " bytes)");
            } else if (last_data_length > 0) {
                List<Byte> chunk = new ArrayList<Byte>(dataArrayList.subList(from, odin_data_length));
                chunk.add(0, (byte) last_data_length);
                chunk.add(0, (byte) 0x6a);
                Script script = new Script(Util.toByteArray(chunk));
                tx.addOutput(Coin.valueOf(BigInteger.valueOf(0).longValue()), script);
            }
        }
        List<UnspentOutput> unspents = Util.getUnspents(source, isOdinTransaction);
        List<Script> inputScripts = new ArrayList<Script>();
        Boolean hadOneRegularInput = false;
        Integer usedUnspents = 0;
        for (UnspentOutput unspent : unspents) {
            String txHash = unspent.txid;
            // byte[] scriptBytes = Hex.decode(unspent.scriptPubKeyHex.getBytes(Charset.forName(Config.BINARY_DATA_CHARSET)));
            byte[] scriptBytes = Util.hexStringToBytes(unspent.scriptPubKeyHex);
            Script script = new Script(scriptBytes);
            // if it's sent to an address and we don't yet have enough inputs or we don't yet have at least one regular input, or if it's sent to a multisig
            // in other words, we sweep up any unused multisig inputs with every transaction
            try {
                if (// Avoid TX data exception,20200307
                unspent.amt_satoshi.compareTo(BigInteger.valueOf(0)) > 0 && ((script.isSentToAddress() && (totalOutput.compareTo(totalInput) > 0 || !hadOneRegularInput)) || (script.isSentToMultiSig() && ((!isOdinTransaction && totalOutput.compareTo(totalInput) > 0) || (isOdinTransaction && usedUnspents < 2 && !hadOneRegularInput) || (isOdinTransaction && usedUnspents < 3 && hadOneRegularInput))))) {
                    if (script.isSentToAddress()) {
                        hadOneRegularInput = true;
                    }
                    /*
            //严格检查输入是否符合比特币协议标准要求 ,待生效
            boolean scriptIsStandard=true;
            if (script.isSentToMultiSig()) {
               System.out.println("Check UTXO script:"+script.toString());
               try{
                 List<ECKey> tmp_pubkeys=script.getPubKeys();
                 for (ECKey tmp_key : tmp_pubkeys){
                   byte[] tmp_byte_array= tmp_key.getPubKey();
                   if( tmp_byte_array[0]!=2 && tmp_byte_array[0]!=3 && tmp_byte_array[0]!=4  ){ 
                     //not standard pubkey
                     scriptIsStandard=false;
                     System.out.println("Ignore an UTXO for invalid script:"+script.toString());
                     break;
                   }
                 }
               }catch(Exception e){
                 scriptIsStandard=false;
                 System.out.println("Found invalid script :"+script.toString());
               }
            }
            
            if(!scriptIsStandard)
              continue;
            */
                    Sha256Hash sha256Hash = new Sha256Hash(txHash);
                    TransactionOutPoint txOutPt = new TransactionOutPoint(params, unspent.vout, sha256Hash);
                    System.out.println("Spending " + sha256Hash + " " + unspent.vout);
                    totalInput = totalInput.add(unspent.amt_satoshi);
                    TransactionInput input = new TransactionInput(params, tx, new byte[] {}, txOutPt);
                    tx.addInput(input);
                    inputScripts.add(script);
                    // 20200307
                    usedUnspents++;
                }
                if (isOdinTransaction && usedUnspents >= 3 && totalInput.compareTo(totalOutput) >= 0) {
                    // use max 3 unspents  to lower odin transaction size if possible
                    break;
                }
            } catch (Exception e) {
                logger.error("Error during transaction creation: " + e.toString());
                e.printStackTrace();
                throw new Exception("Error during transaction creation: " + e.toString());
            }
        }
        if (!hadOneRegularInput && odin_data_length > 0) {
            throw new Exception(Language.getLangLabel("Not enough standard unspent outputs to cover odin transaction."));
        }
        if (totalInput.compareTo(totalOutput) < 0) {
            logger.info("Not enough inputs. Output: " + totalOutput.toString() + ", input: " + totalInput.toString());
            throw new Exception("Not enough BTC to cover transaction of " + String.format("%.8f", totalOutput.doubleValue() / Config.btc_unit) + " BTC. Usable amount is  " + String.format("%.8f", totalInput.doubleValue() / Config.btc_unit));
        }
        BigInteger totalChange = totalInput.subtract(totalOutput);
        try {
            if (totalChange.compareTo(BigInteger.ZERO) > 0) {
                tx.addOutput(Coin.valueOf(totalChange.longValue()), new Address(params, source));
            }
        } catch (AddressFormatException e) {
            throw new Exception("Failed to generate charge output for " + source);
        }
        if (!bRemoteWalletMode) {
            // sign inputs while use local wallet
            for (int i = 0; i < tx.getInputs().size(); i++) {
                Script script = inputScripts.get(i);
                TransactionInput input = tx.getInput(i);
                TransactionSignature txSig = tx.calculateSignature(i, source_key, script, SigHash.ALL, false);
                if (script.isSentToAddress()) {
                    input.setScriptSig(ScriptBuilder.createInputScript(txSig, source_key));
                } else if (script.isSentToMultiSig()) {
                    // input.setScriptSig(ScriptBuilder.createMultiSigInputScript(txSig));
                    ScriptBuilder builder = new ScriptBuilder();
                    builder.smallNum(0);
                    builder.data(txSig.encodeToBitcoin());
                    input.setScriptSig(builder.build());
                }
            }
            tx.verify();
        }
        // Util.exportTextToFile(tx.toString(), "resources/db/last_transaction.log");
        // System.exit(0);
        return tx;
    }

    public Boolean sendTransaction(String source, Transaction tx) throws Exception {
        try {
            System.out.println("Try to send (" + source + ") transaction:");
            System.out.println(tx.toString());
            // for debug
            /*
      byte[] rawTxBytes = tx.bitcoinSerialize();
      System.out.println("The Raw TX:");
      for(int kk=0;kk<rawTxBytes.length;kk++){
          System.out.printf("%02x",rawTxBytes[kk]);
      }
      */
            Blocks blocks = Blocks.getInstance();
            TransactionBroadcast future = null;
            try {
                logger.info("Broadcasting transaction: " + tx.getHashreplacedtring());
                future = peerGroup.broadcastTransaction(tx);
                if (source != null)
                    cacheLastUnspentTransaction(source, tx);
            } catch (Exception e) {
                throw new Exception(Language.getLangLabel("Transaction timed out. Please try again.") + "[2]");
            }
            logger.info("Importing transaction (replacedigning block number -1)");
            blocks.importPPkTransaction(tx, null, null, null);
            return true;
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }

    // 缓存指定地址的最新未花费交易,优先作为下一次交易输入使用,20181220
    public static boolean cacheLastUnspentTransaction(String source, Transaction tx) {
        List<UnspentOutput> lastUnspents = new ArrayList<UnspentOutput>();
        try {
            int vout = 0;
            for (TransactionOutput out : tx.getOutputs()) {
                Script script = out.getScriptPubKey();
                List<ScriptChunk> asm = script.getChunks();
                int asm_num = asm.size();
                BigInteger amount_satoshi = BigInteger.valueOf(out.getValue().getValue());
                // System.out.println("vout:"+vout+"\nasm:"+asm.toString()+"\nbtcAmount:"+amount_satoshi);
                // 如果金额大于0,且是多重签名输出或者是包含指定地址的普通输出,则是有效的UTXO
                boolean isValidOut = false;
                if (amount_satoshi != BigInteger.ZERO) {
                    if (asm_num >= 5 && asm.get(0).equalsOpCode(0x51) && asm.get(asm_num - 2).isOpCode() && asm.get(asm_num - 1).equalsOpCode(0xAE)) {
                        // MULTISIG
                        isValidOut = true;
                    } else {
                        Address dest_address = script.getToAddress(params);
                        String destination = dest_address.toString();
                        if (source.equalsIgnoreCase(destination))
                            isValidOut = true;
                    }
                }
                if (isValidOut) {
                    UnspentOutput tempUnspentObj = new UnspentOutput();
                    tempUnspentObj.amt_satoshi = amount_satoshi;
                    tempUnspentObj.txid = tx.getHashreplacedtring();
                    tempUnspentObj.vout = vout;
                    tempUnspentObj.scriptPubKeyHex = Util.bytesToHexString(script.getProgram());
                    System.out.println("Cache[" + source + "]'s utxo: " + tempUnspentObj.toString());
                    if (tempUnspentObj.scriptPubKeyHex.length() > 0) {
                        lastUnspents.add(tempUnspentObj);
                    }
                }
                vout++;
            }
            cachedLastUnspentList.put(source, lastUnspents);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // 获取缓存的最近一次指定地址的未花费交易输出
    public static List<UnspentOutput> getCachedLastUnspents(String source) {
        try {
            return (List<UnspentOutput>) cachedLastUnspentList.get(source);
        } catch (Exception e) {
        }
        return null;
    }

    // 清空缓存的未花费交易输出
    public static boolean clearCachedLastUnspents() {
        try {
            cachedLastUnspentList.clear();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean importPPkTransaction(Transaction tx, Integer txSnInBlock, Block block, Integer blockHeight) {
        BigInteger fee = BigInteger.ZERO;
        String destination = "";
        BigInteger amount_satoshi = BigInteger.ZERO;
        List<Byte> dataArrayList = new ArrayList<Byte>();
        byte[] data = null;
        String source = "";
        Database db = Database.getInstance();
        boolean matched_ppk_odin_prefix = false;
        for (TransactionOutput out : tx.getOutputs()) {
            try {
                Script script = out.getScriptPubKey();
                List<ScriptChunk> asm = script.getChunks();
                int asm_num = asm.size();
                boolean isFirstMultiSigTx = false;
                if (asm_num >= 5 && asm.get(0).equalsOpCode(0x51) && asm.get(asm_num - 2).isOpCode() && asm.get(asm_num - 1).equalsOpCode(0xAE)) {
                    // MULTISIG
                    int multisig_n = asm.get(asm_num - 2).decodeOpN();
                    if (!matched_ppk_odin_prefix) {
                        if (asm.get(2).data.length == Config.PPK_ODIN_MARK_PUBKEY_HEX.length() / 2) {
                            String tmp_pubkey_hex = Util.bytesToHexString(asm.get(2).data);
                            if (Config.PPK_ODIN_MARK_PUBKEY_HEX.equals(tmp_pubkey_hex)) {
                                matched_ppk_odin_prefix = true;
                                isFirstMultiSigTx = true;
                            }
                        }
                    }
                    if (matched_ppk_odin_prefix) {
                        int from = isFirstMultiSigTx ? 3 : 2;
                        for (; from < multisig_n + 1; from++) {
                            byte[] tmp_data = asm.get(from).data;
                            byte embed_data_len = tmp_data[1];
                            if (embed_data_len > 0 && embed_data_len <= tmp_data.length - 2)
                                for (byte i = 0; i < embed_data_len; i++) dataArrayList.add(tmp_data[2 + i]);
                        }
                    }
                } else if (matched_ppk_odin_prefix && asm.get(0).equalsOpCode(0x6A)) {
                    // OP_RETURN
                    // System.out.println("asm_num="+asm_num+"  "+asm.toString());
                    for (int i = 0; i < asm.get(1).data.length; i++) dataArrayList.add(asm.get(1).data[i]);
                }
                if (destination.equals("") && amount_satoshi == BigInteger.ZERO && dataArrayList.size() == 0) {
                    Address address = script.getToAddress(params);
                    destination = address.toString();
                    amount_satoshi = BigInteger.valueOf(out.getValue().getValue());
                }
            } catch (ScriptException e) {
            }
        }
        if (dataArrayList.size() > 0) {
            // 截取特征前缀后的有效字节数据
            data = Util.toByteArray(dataArrayList);
        } else {
            return false;
        }
        for (TransactionInput in : tx.getInputs()) {
            if (in.isCoinBase())
                return false;
            try {
                Script script = in.getScriptSig();
                Address address = script.getFromAddress(params);
                if (address != null && source.equals("")) {
                    String tmp_str = address.toString();
                    if (tmp_str.startsWith("1")) {
                        source = tmp_str;
                        break;
                    }
                }
            } catch (ScriptException e) {
            }
        }
        logger.info("Incoming PPk transaction from " + source + " to " + destination + " (" + tx.getHashreplacedtring() + ")");
        if (!source.equals("") && dataArrayList.size() > 0) {
            String str_data_hex = "";
            str_data_hex = Util.bytesToHexString(data);
            // logger.info("PPk str_data_hex : ["+str_data_hex+"] length="+str_data_hex.length());
            db.executeUpdate("delete from transactions where tx_hash='" + tx.getHashreplacedtring() + "' and block_index<0");
            ResultSet rs = db.executeQuery("select * from transactions where tx_hash='" + tx.getHashreplacedtring() + "';");
            try {
                if (!rs.next()) {
                    if (block != null) {
                        Integer newTxIndex = Util.getLastTxIndex() + 1;
                        PreparedStatement ps = db.connection.prepareStatement("INSERT INTO transactions(tx_index, tx_hash, block_index, block_time, source, destination, btc_amount, fee, data,prefix_type,sn_in_block) VALUES('" + newTxIndex + "','" + tx.getHashreplacedtring() + "','" + blockHeight + "','" + block.getTimeSeconds() + "','" + source + "','" + destination + "','" + amount_satoshi.toString() + "','" + fee.toString() + "',?,1,'" + txSnInBlock.toString() + "')");
                        ps.setString(1, str_data_hex);
                        ps.execute();
                    } else {
                        PreparedStatement ps = db.connection.prepareStatement("INSERT INTO transactions(tx_index, tx_hash, block_index, block_time, source, destination, btc_amount, fee, data,prefix_type,sn_in_block) VALUES('" + (Util.getLastTxIndex() + 1) + "','" + tx.getHashreplacedtring() + "','-1','" + Util.getNowTimestamp() + "','" + source + "','" + destination + "','" + amount_satoshi.toString() + "','" + fee.toString() + "',?,1,-1)");
                        ps.setString(1, str_data_hex);
                        ps.execute();
                    }
                }
            } catch (SQLException e) {
                logger.error(e.toString());
            }
        }
        return true;
    }

    public static Byte getPPkMessageTypeFromTransaction(byte[] odin_data) {
        if (odin_data == null || odin_data.length == 0)
            return null;
        else
            return odin_data[0];
    }

    public static List<Byte> getPPkMessageFromTransaction(byte[] odin_data) {
        List<Byte> message = null;
        try {
            List<Byte> dataArrayList = Util.toByteArrayList(odin_data);
            message = dataArrayList.subList(1, dataArrayList.size());
            return message;
        } catch (Exception e) {
        }
        return message;
    }

    // 打开远程钱包模式 2019-01-15
    public static void enableRemoteWalletMode() {
        bRemoteWalletMode = true;
    }

    public static boolean isRemoteWalletMode() {
        return bRemoteWalletMode;
    }

    // 获取当前地址列表 2019-01-15
    public List<String> getAddresses() {
        List<String> addresses = new ArrayList<String>();
        if (!bRemoteWalletMode) {
            // 本地钱包模式
            List<ECKey> keys = wallet.getImportedKeys();
            for (ECKey key : keys) {
                addresses.add(key.toAddress(params).toString());
            }
        }
        return addresses;
    }
}

19 Source : BitcoinTransactionAdapter.java
with Apache License 2.0
from ICOnator

/**
 * Bitcoin-sepcific transaction clreplaced.
 */
public clreplaced BitcoinTransactionAdapter extends BaseTransactionAdapter {

    private static final String BLOCKCHAIN_INFO_LINK = "https://blockchain.info/tx/";

    private TransactionOutput bitcoinjTxOutput;

    private NetworkParameters bitcoinjNetworkParameters;

    private BlockStore bitcoinjBlockStore;

    /**
     * Remembering block height after first time getter is called. This is to save
     * computation time since without this the bitcoinj block store would be
     * searched every time the block height is requested.
     */
    private Long blockHeight;

    /**
     * Remembering block time after first time getter is called. This is to save
     * computation time since without this the bitcoinj block store would be
     * searched every time the block time is requested.
     */
    private Date blockTime;

    public BitcoinTransactionAdapter(@NotNull TransactionOutput bitcoinjTxOutput, @NotNull NetworkParameters bitcoinjNetworkParameters, @NotNull BlockStore bitcoinjBlockStore, @NotNull InvestorService investorService) {
        super(investorService);
        this.bitcoinjTxOutput = bitcoinjTxOutput;
        this.bitcoinjNetworkParameters = bitcoinjNetworkParameters;
        this.bitcoinjBlockStore = bitcoinjBlockStore;
        this.blockHeight = null;
        this.blockTime = null;
    }

    @Override
    public String getTransactionId() throws MissingTransactionInformationException {
        try {
            Transaction transaction = bitcoinjTxOutput.getParentTransaction();
            return transaction.getHashreplacedtring() + "_" + String.valueOf(bitcoinjTxOutput.getIndex());
        } catch (Exception e) {
            throw new MissingTransactionInformationException("Couldn't fetch transaction id.", e);
        }
    }

    @Override
    public BigInteger getTransactionValue() throws MissingTransactionInformationException {
        try {
            return BigInteger.valueOf(bitcoinjTxOutput.getValue().getValue());
        } catch (Exception e) {
            throw new MissingTransactionInformationException("Couldn't fetch transaction value.", e);
        }
    }

    @Override
    public String getReceivingAddress() throws MissingTransactionInformationException {
        try {
            return bitcoinjTxOutput.getAddressFromP2PKHScript(this.bitcoinjNetworkParameters).toBase58();
        } catch (Exception e) {
            throw new MissingTransactionInformationException("Couldn't fetch receiving address.", e);
        }
    }

    @Override
    public Investor getreplacedociatedInvestor() throws MissingTransactionInformationException {
        try {
            return getInvestorService().getInvestorByBitcoinAddress(getReceivingAddress());
        } catch (MissingTransactionInformationException e) {
            throw e;
        } catch (Exception e) {
            throw new MissingTransactionInformationException("Couldn't fetch replacedociated investor.", e);
        }
    }

    @Override
    public Long getBlockHeight() throws MissingTransactionInformationException {
        if (this.blockHeight == null) {
            try {
                this.blockHeight = bitcoinjTxOutput.getParentTransaction().getAppearsInHashes().keySet().stream().map((blockHash) -> {
                    try {
                        return bitcoinjBlockStore.get(blockHash);
                    } catch (BlockStoreException e) {
                        // This can happen if the transaction was seen in a side-chain
                        return null;
                    }
                }).filter(Objects::nonNull).map(StoredBlock::getHeight).min(Integer::compareTo).get().longValue();
            } catch (Exception e) {
                throw new MissingTransactionInformationException("Couldn't fetch block height.", e);
            }
        }
        return this.blockHeight;
    }

    @Override
    public CurrencyType getCurrencyType() {
        return CurrencyType.BTC;
    }

    /*
     * Retrieve the timestamp from the first block that the transaction was seen in.
     */
    @Override
    public Date getBlockTime() throws MissingTransactionInformationException {
        if (this.blockTime == null) {
            try {
                this.blockTime = bitcoinjTxOutput.getParentTransaction().getAppearsInHashes().keySet().stream().map((blockHash) -> {
                    try {
                        return bitcoinjBlockStore.get(blockHash);
                    } catch (BlockStoreException e) {
                        // This can happen if the transaction was seen in a side-chain
                        return null;
                    }
                }).filter(Objects::nonNull).map(StoredBlock::getHeader).map(Block::getTime).min(Date::compareTo).get();
            } catch (Exception e) {
                throw new MissingTransactionInformationException("Couldn't fetch block time.", e);
            }
        }
        return this.blockTime;
    }

    @Override
    public String getTransactionUrl() throws MissingTransactionInformationException {
        try {
            return BLOCKCHAIN_INFO_LINK + bitcoinjTxOutput.getParentTransaction().getHashreplacedtring();
        } catch (Exception e) {
            throw new MissingTransactionInformationException("Couldn't fetch transaction Id.", e);
        }
    }

    /**
     * @return the bitcoinj transaction object.
     */
    public Transaction getBitcoinjTransaction() {
        return bitcoinjTxOutput.getParentTransaction();
    }
}

19 Source : BlockChain.java
with Apache License 2.0
from btcontract

// TODO: Rename this clreplaced to SPVBlockChain at some point.
/**
 * A BlockChain implements the <i>simplified payment verification</i> mode of the Bitcoin protocol. It is the right
 * choice to use for programs that have limited resources as it won't verify transactions signatures or attempt to store
 * all of the block chain. Really, this clreplaced should be called SPVBlockChain but for backwards compatibility it is not.
 */
public clreplaced BlockChain extends AbstractBlockChain {

    /**
     * Keeps a map of block hashes to StoredBlocks.
     */
    protected final BlockStore blockStore;

    /**
     * <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
     * one from scratch, or you can deserialize a saved wallet from disk using
     * {@link Wallet#loadFromFile(File, WalletExtension...)}</p>
     *
     * <p>For the store, you should use {@link SPVBlockStore} or you could also try a
     * {@link MemoryBlockStore} if you want to hold all headers in RAM and don't care about
     * disk serialization (this is rare).</p>
     */
    public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
        addWallet(wallet);
    }

    /**
     * See {@link #BlockChain(Context, Wallet, BlockStore)}}
     */
    public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallet, blockStore);
    }

    /**
     * Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
     * and receiving coins but rather, just want to explore the network data structures.
     */
    public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * See {@link #BlockChain(Context, BlockStore)}
     */
    public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
        this(params, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * Constructs a BlockChain connected to the given list of listeners and a store.
     */
    public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        super(params, wallets, blockStore);
        this.blockStore = blockStore;
    }

    /**
     * See {@link #BlockChain(Context, List, BlockStore)}
     */
    public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallets, blockStore);
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected void rollbackBlockStore(int height) throws BlockStoreException {
        lock.lock();
        try {
            int currentHeight = getBestChainHeight();
            checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
            if (height == currentHeight)
                // nothing to do
                return;
            // Look for the block we want to be the new chain head
            StoredBlock newChainHead = blockStore.getChainHead();
            while (newChainHead.getHeight() > height) {
                newChainHead = newChainHead.getPrev(blockStore);
                if (newChainHead == null)
                    throw new BlockStoreException("Unreachable height");
            }
            // Modify store directly
            blockStore.put(newChainHead);
            this.setChainHead(newChainHead);
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean shouldVerifyTransactions() {
        return false;
    }

    @Override
    protected TransactionOutputChanges connectTransactions(int height, Block block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void disconnectTransactions(StoredBlock block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
        blockStore.setChainHead(chainHead);
    }

    @Override
    protected void notSettingChainHead() throws BlockStoreException {
    // We don't use DB transactions here, so we don't need to do anything
    }

    @Override
    protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
        return blockStore.get(hash);
    }

    @Override
    public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
        boolean success = super.add(block);
        if (success) {
            trackFilteredTransactions(block.getTransactionCount());
        }
        return success;
    }
}

19 Source : FetchTransactions.java
with GNU General Public License v3.0
from Blockstream

public static void main(String[] args) throws Exception {
    BriefLogFormatter.init();
    System.out.println("Connecting to node");
    final NetworkParameters params = TestNet3Params.get();
    BlockStore blockStore = new MemoryBlockStore(params);
    BlockChain chain = new BlockChain(params, blockStore);
    PeerGroup peerGroup = new PeerGroup(params, chain);
    peerGroup.start();
    peerGroup.addAddress(new PeerAddress(params, InetAddress.getLocalHost()));
    peerGroup.waitForPeers(1).get();
    Peer peer = peerGroup.getConnectedPeers().get(0);
    Sha256Hash txHash = Sha256Hash.wrap(args[0]);
    ListenableFuture<Transaction> future = peer.getPeerMempoolTransaction(txHash);
    System.out.println("Waiting for node to send us the requested transaction: " + txHash);
    Transaction tx = future.get();
    System.out.println(tx);
    System.out.println("Waiting for node to send us the dependencies ...");
    List<Transaction> deps = peer.downloadDependencies(tx).get();
    for (Transaction dep : deps) {
        System.out.println("Got dependency " + dep.getHashreplacedtring());
    }
    System.out.println("Done.");
    peerGroup.stop();
}

19 Source : FetchBlock.java
with GNU General Public License v3.0
from Blockstream

public static void main(String[] args) throws Exception {
    BriefLogFormatter.init();
    System.out.println("Connecting to node");
    final NetworkParameters params = TestNet3Params.get();
    BlockStore blockStore = new MemoryBlockStore(params);
    BlockChain chain = new BlockChain(params, blockStore);
    PeerGroup peerGroup = new PeerGroup(params, chain);
    peerGroup.start();
    PeerAddress addr = new PeerAddress(params, InetAddress.getLocalHost());
    peerGroup.addAddress(addr);
    peerGroup.waitForPeers(1).get();
    Peer peer = peerGroup.getConnectedPeers().get(0);
    Sha256Hash blockHash = Sha256Hash.wrap(args[0]);
    Future<Block> future = peer.getBlock(blockHash);
    System.out.println("Waiting for node to send us the requested block: " + blockHash);
    Block block = future.get();
    System.out.println(block);
    peerGroup.stopAsync();
}

19 Source : SPV.java
with GNU General Public License v3.0
from Blockstream

public clreplaced SPV {

    private final static String TAG = SPV.clreplaced.getSimpleName();

    private final Map<TransactionOutPoint, Coin> mCountedUtxoValues = new HashMap<>();

    static clreplaced AccountInfo extends Pair<Integer, Integer> {

        AccountInfo(final Integer subAccount, final Integer pointer) {
            super(subAccount, pointer);
        }

        Integer getSubAccount() {
            return first;
        }

        public Integer getPointer() {
            return second;
        }
    }

    public NetworkParameters getNetworkParameters(NetworkData network) {
        switch(network.getNetwork() == null ? "mainnet" : network.getNetwork()) {
            case "mainnet":
                return MainNetParams.get();
            case "testnet":
                return TestNet3Params.get();
            case "regtest":
            case "localtest":
                return RegTestParams.get();
            default:
                return null;
        }
    }

    // We use a single threaded executor to serialise config changes
    // without forcing callers to block.
    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();

    private final SparseArray<Coin> mVerifiedCoinBalances = new SparseArray<>();

    private final Map<Sha256Hash, List<Integer>> mUnspentOutpoints = new HashMap<>();

    private final Map<TransactionOutPoint, AccountInfo> mUnspentDetails = new HashMap<>();

    private GaService mService;

    private BlockStore mBlockStore;

    private BlockChain mBlockChain;

    private PeerGroup mPeerGroup;

    private final PeerFilterProvider mPeerFilter = new PeerFilterProvider(this);

    private NotificationManager mNotifyManager;

    private Builder mNotificationBuilder;

    private final static int mNotificationId = 1;

    private int mNetWorkType;

    private final Object mStateLock = new Object();

    public final SettableFuture<Void> onServiceAttached = SettableFuture.create();

    private List<SubaccountData> mSubaccounts = new ArrayList<>();

    public SPV() {
        mNetWorkType = ConnectivityManager.TYPE_DUMMY;
    }

    public void startService(final Context ctx) throws Exception {
        GaService.createNotificationChannel(ctx);
        // Provide bitcoinj with Mnemonics. These are used if we need to create a fake
        // wallet during SPV_SYNCRONIZATION syncing to prevent an exception.
        final ArrayList<String> words = new ArrayList<>(Wally.BIP39_WORDLIST_LEN);
        MnemonicHelper.initWordList(words, null);
        MnemonicCode.INSTANCE = new MnemonicCode(words, null);
        Log.d(TAG, "onCreate: binding service");
        final ServiceConnection connection = new ServiceConnection() {

            @Override
            public void onServiceConnected(final ComponentName clreplacedName, final IBinder service) {
                Log.d(TAG, "onServiceConnected: dispatching onServiceAttached callbacks");
                mService = ((GaService.GaBinder) service).getService();
                mService.onBound(ctx);
                startAsync();
                onServiceAttached.set(null);
            }

            @Override
            public void onServiceDisconnected(final ComponentName name) {
                Log.d(TAG, "onServiceDisconnected: dispatching onServiceAttached exception");
                onServiceAttached.setException(new GAException(name.toString()));
            }
        };
        final Intent intent = new Intent(ctx, GaService.clreplaced);
        ctx.bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    public GaService getService() {
        return mService;
    }

    private <T> String Var(final String name, final T value) {
        return name + " => " + value.toString() + ' ';
    }

    public boolean isEnabled() {
        final NetworkData networkData = mService.getNetwork();
        return !getSession().isWatchOnly() && cfg().getBoolean(PrefKeys.SPV_ENABLED, false) && !networkData.getLiquid();
    }

    public void setEnabledAsync(final boolean enabled) {
        mExecutor.execute(() -> setEnabled(enabled));
    }

    private void setEnabled(final boolean enabled) {
        synchronized (mStateLock) {
            final boolean current = isEnabled();
            Log.d(TAG, "setEnabled: " + Var("enabled", enabled) + Var("current", current));
            if (enabled == current)
                return;
            cfgEdit().putBoolean(PrefKeys.SPV_ENABLED, enabled).apply();
            // FIXME: Should we delete unspent here?
            reset(false, /* deleteAllData */
            false);
        }
    }

    public boolean isSyncOnMobileEnabled() {
        return cfg().getBoolean(PrefKeys.SPV_MOBILE_SYNC_ENABLED, false);
    }

    public void setSyncOnMobileEnabledAsync(final boolean enabled) {
        mExecutor.execute(() -> setSyncOnMobileEnabled(enabled));
    }

    private void setSyncOnMobileEnabled(final boolean enabled) {
        synchronized (mStateLock) {
            final boolean current = isSyncOnMobileEnabled();
            final boolean currentlyEnabled = isEnabled();
            Log.d(TAG, "setSyncOnMobileEnabled: " + Var("enabled", enabled) + Var("current", current));
            if (enabled == current)
                // Setting hasn't changed
                return;
            cfgEdit().putBoolean(PrefKeys.SPV_MOBILE_SYNC_ENABLED, enabled).apply();
            if (getNetworkType() != ConnectivityManager.TYPE_MOBILE)
                // Any change doesn't affect us since we aren't currently on mobile
                return;
            if (enabled && currentlyEnabled) {
                if (mPeerGroup == null)
                    setup();
                startSync();
            } else
                stopSync();
        }
    }

    public String getTrustedPeers() {
        return cfg().getString(PrefKeys.TRUSTED_ADDRESS, cfg().getString("trusted_peer", "")).trim();
    }

    public void setTrustedPeersAsync(final String peers) {
        mExecutor.execute(() -> setTrustedPeers(peers));
    }

    private void setTrustedPeers(final String peers) {
        synchronized (mStateLock) {
            // FIXME: We should check if the peers differ here, instead of in the caller
            final String current = getTrustedPeers();
            Log.d(TAG, "setTrustedPeers: " + Var("peers", peers) + Var("current", current));
            cfgEdit().putString(PrefKeys.TRUSTED_ADDRESS, peers).apply();
            reset(false, /* deleteAllData */
            false);
        }
    }

    public PeerGroup getPeerGroup() {
        return mPeerGroup;
    }

    public boolean isVerified(final String txHash) {
        return cfg().getBoolean(PrefKeys.VERIFIED_HASH_ + txHash, false);
    }

    public void startAsync() {
        mExecutor.execute(() -> start());
    }

    private void start() {
        synchronized (mStateLock) {
            Log.d(TAG, "start");
            reset(false, /* deleteAllData */
            true);
        }
    }

    public Coin getVerifiedBalance(final int subAccount) {
        return mVerifiedCoinBalances.get(subAccount);
    }

    private boolean isUnspentOutpoint(final Sha256Hash txHash) {
        return mUnspentOutpoints.containsKey(txHash);
    }

    private TransactionOutPoint createOutPoint(final Integer index, final Sha256Hash txHash, final NetworkParameters params) {
        return new TransactionOutPoint(params, index, txHash);
    }

    void updateUnspentOutputs() {
        final NetworkData networkData = mService.getNetwork();
        final boolean currentlyEnabled = isEnabled();
        Log.d(TAG, "updateUnspentOutputs: " + Var("currentlyEnabled", currentlyEnabled));
        final List<TransactionData> utxos = new ArrayList<>();
        for (final SubaccountData subaccountData : mSubaccounts) {
            try {
                List<TransactionData> transactionDataList = getUtxos(subaccountData.getPointer());
                utxos.addAll(transactionDataList);
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
        final Set<TransactionOutPoint> newUtxos = new HashSet<>();
        boolean recalculateBloom = false;
        Log.d(TAG, Var("number of utxos", utxos.size()));
        for (final TransactionData utxo : utxos) {
            final Integer prevIndex = utxo.getPtIdx();
            final Integer subaccount = utxo.getSubaccount();
            final Integer pointer = utxo.getPointer();
            final Sha256Hash txHash = utxo.getTxhashreplacedha256Hash();
            if (isVerified(txHash.toString())) {
                addToUtxo(txHash, prevIndex, subaccount, pointer);
                addUtxoToValues(txHash, false);
            } else {
                recalculateBloom = true;
                addToBloomFilter(utxo.getBlockHeight(), txHash, prevIndex, subaccount, pointer);
            }
            newUtxos.add(createOutPoint(prevIndex, txHash, getNetworkParameters(networkData)));
        }
        // GA inception
        mPeerGroup.setFastCatchupTimeSecs(1393545600);
        final List<Integer> changedSubaccounts = new ArrayList<>();
        for (final TransactionOutPoint oldUtxo : new HashSet<>(mCountedUtxoValues.keySet())) {
            if (!newUtxos.contains(oldUtxo)) {
                recalculateBloom = true;
                final int subAccount = mUnspentDetails.get(oldUtxo).getSubAccount();
                final Coin verifiedBalance = getVerifiedBalance(subAccount);
                mVerifiedCoinBalances.put(subAccount, verifiedBalance.subtract(mCountedUtxoValues.get(oldUtxo)));
                changedSubaccounts.add(subAccount);
                mCountedUtxoValues.remove(oldUtxo);
                mUnspentDetails.remove(oldUtxo);
                mUnspentOutpoints.get(oldUtxo.getHash()).remove(((int) oldUtxo.getIndex()));
            }
        }
        if (recalculateBloom && mPeerGroup != null)
            mPeerGroup.recalculateFastCatchupAndFilter(PeerGroup.FilterRecalculateMode.SEND_IF_CHANGED);
    }

    private void addUtxoToValues(final Sha256Hash txHash, final boolean updateVerified) {
        final String txHashHex = txHash.toString();
        if (updateVerified)
            cfgEdit().putBoolean(PrefKeys.VERIFIED_HASH_ + txHashHex, true).apply();
        for (final SubaccountData subaccountData : mSubaccounts) {
            final Integer pointer = subaccountData.getPointer();
            try {
                final List<TransactionData> txs = getTransactions(pointer);
                // TODO called too many times
                for (final TransactionData tx : txs) {
                    if (txHashHex.equals(tx.getTxhash())) {
                    // transactionDataObservable.fire();
                    }
                }
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
    }

    private List<SubaccountData> getSubAccounts() throws Exception {
        final ObjectMapper mObjectMapper = new ObjectMapper();
        final GDKTwoFactorCall call = getSession().getSubAccounts();
        final ObjectNode accounts = call.resolve(null, new HardwareCodeResolver(null, getSession().getHWWallet()));
        final List<SubaccountData> subAccounts = mObjectMapper.readValue(mObjectMapper.treeAsTokens(accounts.get("subaccounts")), new TypeReference<List<SubaccountData>>() {
        });
        return subAccounts;
    }

    private List<TransactionData> getTransactions(final int subaccount) throws Exception {
        final GDKTwoFactorCall call = getSession().getTransactionsRaw(subaccount, 0, 100);
        final ObjectNode txListObject = call.resolve(null, new HardwareCodeResolver(null, getSession().getHWWallet()));
        final List<TransactionData> transactions = getSession().parseTransactions((ArrayNode) txListObject.get("transactions"));
        return transactions;
    }

    private List<TransactionData> getUtxos(final int subaccount) throws Exception {
        final GDKTwoFactorCall call = getSession().getUTXO(subaccount, 0);
        final ObjectNode txListObject = call.resolve(null, new HardwareCodeResolver(null, getSession().getHWWallet()));
        final JsonNode unspentOutputs = txListObject.get("unspent_outputs");
        if (!unspentOutputs.has("btc"))
            return new ArrayList<>();
        final ArrayNode arrayNode = (ArrayNode) unspentOutputs.get("btc");
        for (int i = 0; i < arrayNode.size(); i++) {
            ((ObjectNode) arrayNode.get(i)).remove("satoshi");
        }
        final List<TransactionData> transactions = getSession().parseTransactions((ArrayNode) arrayNode);
        return transactions;
    }

    int getBloomFilterElementCount() {
        final int count = mUnspentOutpoints.size();
        return count == 0 ? 1 : count;
    }

    BloomFilter getBloomFilter(final int size, final double falsePositiveRate, final long nTweak) {
        final Set<Sha256Hash> keys = mUnspentOutpoints.keySet();
        Log.d(TAG, "getBloomFilter returning " + keys.size() + " items");
        final BloomFilter filter = new BloomFilter(size, falsePositiveRate, nTweak);
        for (final Sha256Hash hash : keys) filter.insert(hash.getReversedBytes());
        if (keys.isEmpty()) {
            // Add a fake entry to avoid downloading blocks when filter is empty,
            // as empty bloom filters are ignored by bitcoinj.
            // FIXME: This results in a constant filter that peers can use to identify
            // us as a Green client. That is undesirable.
            filter.insert(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef });
        }
        return filter;
    }

    private void addToBloomFilter(final Integer blockHeight, final Sha256Hash txHash, final int prevIndex, final int subAccount, final int pointer) {
        if (mBlockChain == null)
            // can happen before login (onNewBlock)
            return;
        if (txHash != null)
            addToUtxo(txHash, prevIndex, subAccount, pointer);
        if (blockHeight != null && blockHeight <= mBlockChain.getBestChainHeight() && (txHash == null || !mUnspentOutpoints.containsKey(txHash))) {
            // new tx or block notification with blockHeight <= current blockHeight means we might've [1]
            // synced the height already while we haven't seen the tx, so we need to re-sync to be able
            // to verify it.
            // [1] - "might've" in case of txHash == null (block height notification),
            // because it depends on the order of notifications
            // - "must've" in case of txHash != null, because this means the tx arrived only after
            // requesting it manually and we already had higher blockHeight
            // 
            // We do it using the special case in bitcoinj for VM crashed because of
            // a transaction received.
            try {
                Log.d(TAG, "Creating fake wallet for re-sync");
                final NetworkData networkData = mService.getNetwork();
                final Wallet fakeWallet = new Wallet(getNetworkParameters(networkData)) {

                    @Override
                    public int getLastBlockSeenHeight() {
                        return blockHeight - 1;
                    }
                };
                mBlockChain.addWallet(fakeWallet);
                // can be removed, because the call above
                mBlockChain.removeWallet(fakeWallet);
            // should rollback already
            } catch (final Exception e) {
                e.printStackTrace();
                Log.w(TAG, "fakeWallet exception: " + e.toString());
            }
        }
    }

    private void addToUtxo(final Sha256Hash txHash, final Integer prevIndex, final int subAccount, final int pointer) {
        final NetworkData networkData = mService.getNetwork();
        mUnspentDetails.put(createOutPoint(prevIndex, txHash, getNetworkParameters(networkData)), new AccountInfo(subAccount, pointer));
        if (mUnspentOutpoints.get(txHash) == null)
            mUnspentOutpoints.put(txHash, Lists.newArrayList(prevIndex));
        else
            mUnspentOutpoints.get(txHash).add(prevIndex);
    }

    public int getSPVHeight() {
        if (mBlockChain != null && isEnabled())
            return mBlockChain.getBestChainHeight();
        return 0;
    }

    @TargetApi(Build.VERSION_CODES.M)
    private PendingIntent getNotificationIntent() {
        final Context service = getService();
        final Intent intent = new Intent(service, TabbedMainActivity.clreplaced);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        return PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_IMMUTABLE);
    }

    private void startSync() {
        synchronized (mStateLock) {
            final boolean isRunning = mPeerGroup != null && mPeerGroup.isRunning();
            Log.d(TAG, "startSync: " + Var("isRunning", isRunning));
            if (isRunning)
                // Already started to sync
                return;
            if (mPeerGroup == null) {
                // FIXME: Thi should not be possible but it happens in the wild.
                Log.d(TAG, "startSync: mPeerGroup is null");
                return;
            }
            if (mNotifyManager == null) {
                mNotifyManager = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
                mNotificationBuilder = new NotificationCompat.Builder(mService, "spv_channel");
                mNotificationBuilder.setContentreplacedle("SPV Synchronization").setSmallIcon(R.drawable.ic_home);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                    mNotificationBuilder.setContentIntent(getNotificationIntent());
            }
            mNotificationBuilder.setContentText("Connecting to peer(s)...");
            updateNotification(0, 0);
            CB.after(mPeerGroup.startAsync(), new FutureCallback<Object>() {

                @Override
                public void onSuccess(final Object result) {
                    mPeerGroup.startBlockChainDownload(new DownloadProgressTracker() {

                        @Override
                        public void onChainDownloadStarted(final Peer peer, final int blocksLeft) {
                            // Note that this method may be called multiple times if syncing
                            // switches peers while downloading.
                            Log.d(TAG, "onChainDownloadStarted: " + Var("blocksLeft", blocksLeft));
                            super.onChainDownloadStarted(peer, blocksLeft);
                        }

                        @Override
                        public void onBlocksDownloaded(final Peer peer, final Block block, final FilteredBlock filteredBlock, final int blocksLeft) {
                            // Log.d(TAG, "onBlocksDownloaded: " + Var("blocksLeft", blocksLeft));
                            super.onBlocksDownloaded(peer, block, filteredBlock, blocksLeft);
                        }

                        @Override
                        protected void startDownload(final int blocks) {
                            Log.d(TAG, "startDownload");
                            updateNotification(100, 0);
                        }

                        @Override
                        protected void progress(final double percent, final int blocksSoFar, final Date date) {
                            // Log.d(TAG, "progress: " + Var("percent", percent));
                            String progressString = String.format("Block %d", getSPVHeight());
                            mNotificationBuilder.setContentText(progressString);
                            updateNotification(100, (int) percent);
                        }

                        @Override
                        protected void doneDownload() {
                            Log.d(TAG, "doneDownLoad");
                            mNotifyManager.cancel(mNotificationId);
                        }
                    });
                }

                @Override
                public void onFailure(final Throwable t) {
                    t.printStackTrace();
                    mNotifyManager.cancel(mNotificationId);
                }
            });
        }
    }

    private void updateNotification(final int total, final int soFar) {
        mNotificationBuilder.setProgress(total, soFar, false);
        mNotifyManager.notify(mNotificationId, mNotificationBuilder.build());
    }

    private PeerAddress getPeerAddress(final String address) throws URISyntaxException, UnknownHostException {
        final URI uri = new URI("btc://" + address);
        final String host = uri.getHost();
        if (host == null)
            throw new UnknownHostException(address);
        final NetworkData networkData = mService.getNetwork();
        final int port = uri.getPort() == -1 ? getNetworkParameters(networkData).getPort() : uri.getPort();
        if (!isProxyEnabled() && !getTorEnabled())
            return new PeerAddress(getNetworkParameters(networkData), InetAddress.getByName(host), port);
        return new PeerAddress(getNetworkParameters(networkData), host, port) {

            @Override
            public InetSocketAddress toSocketAddress() {
                return InetSocketAddress.createUnresolved(host, port);
            }

            @Override
            public String toString() {
                return String.format("%s:%s", host, port);
            }

            @Override
            public int hashCode() {
                return uri.hashCode();
            }
        };
    }

    private void addPeer(final String address, final NetworkParameters params) throws URISyntaxException {
        try {
            mPeerGroup.addAddress(getPeerAddress(address));
        } catch (final UnknownHostException e) {
            // FIXME: Should report this error: one the host here couldn't be resolved
            e.printStackTrace();
        }
    }

    private void setPingInterval(final long interval) {
        synchronized (mStateLock) {
            if (mPeerGroup != null)
                mPeerGroup.setPingIntervalMsec(interval);
        }
    }

    public void enablePingMonitoring() {
        setPingInterval(PeerGroup.DEFAULT_PING_INTERVAL_MSEC);
    }

    public void disablePingMonitoring() {
        setPingInterval(-1);
    }

    private void setup() {
        synchronized (mStateLock) {
            Log.d(TAG, "setup: " + Var("mPeerGroup != null", mPeerGroup != null));
            if (mPeerGroup != null) {
                // FIXME: Make sure this can never happen
                Log.e(TAG, "Must stop and tear down SPV_SYNCRONIZATION before setting up again!");
                return;
            }
            final NetworkData networkData = mService.getNetwork();
            try {
                Log.d(TAG, "Creating block store");
                mBlockStore = new SPVBlockStore(getNetworkParameters(networkData), mService.getSPVChainFile());
                // detect corruptions as early as possible
                final StoredBlock storedBlock = mBlockStore.getChainHead();
                if (storedBlock.getHeight() == 0 && networkData.isRegtest()) {
                    InputStream is = null;
                    try {
                        is = mService.getreplacedets().open(networkData.getMainnet() ? "production/checkpoints" : "btctestnet/checkpoints");
                        // mService.getLoginData().get("earliest_key_creation_time"); // TODO gdk
                        final int keyTime = 0;
                        CheckpointManager.checkpoint(getNetworkParameters(networkData), is, mBlockStore, keyTime);
                        Log.d(TAG, "checkpoints loaded");
                    } catch (final Exception e) {
                        // couldn't load checkpoints, log & skip
                        Log.d(TAG, "couldn't load checkpoints, log & skip");
                        e.printStackTrace();
                    } finally {
                        try {
                            if (is != null)
                                is.close();
                        } catch (final IOException e) {
                        // do nothing
                        }
                    }
                }
                Log.d(TAG, "Creating block chain");
                org.bitcoinj.core.Context context = new org.bitcoinj.core.Context(getNetworkParameters(networkData));
                mBlockChain = new BlockChain(context, mBlockStore);
                mBlockChain.addTransactionReceivedListener(mTxListner);
                System.setProperty("user.home", mService.getFilesDir().toString());
                Log.d(TAG, "Creating peer group");
                if (!isProxyEnabled() && !getTorEnabled()) {
                    mPeerGroup = new PeerGroup(getNetworkParameters(networkData), mBlockChain);
                } else {
                    String proxyHost, proxyPort;
                    if (!isProxyEnabled()) {
                        if (getSession() == null || getSession().getTorSocks5() == null) {
                            throw new URISyntaxException("", "null session or TorSocks5");
                        }
                        final String fullUrl = getSession().getTorSocks5().replaceFirst("socks5://", "");
                        if (fullUrl.split(":").length != 2) {
                            throw new URISyntaxException(getSession().getTorSocks5(), "Invalid Tor SOCKS5 string");
                        }
                        proxyHost = fullUrl.split(":")[0];
                        proxyPort = fullUrl.split(":")[1];
                    } else {
                        proxyHost = getProxyHost();
                        proxyPort = getProxyPort();
                    }
                    final Socks5SocketFactory sf = new Socks5SocketFactory(proxyHost, proxyPort);
                    final BlockingClientManager bcm = new BlockingClientManager(sf);
                    bcm.setConnectTimeoutMillis(60000);
                    mPeerGroup = new PeerGroup(getNetworkParameters(networkData), mBlockChain, bcm);
                    mPeerGroup.setConnectTimeoutMillis(60000);
                }
                disablePingMonitoring();
                mSubaccounts = getSubAccounts();
                updateUnspentOutputs();
                mPeerGroup.addPeerFilterProvider(mPeerFilter);
                Log.d(TAG, "Adding peers");
                final String peers = getTrustedPeers();
                final List<String> addresses;
                if (peers.isEmpty()) {
                    // DEFAULT_PEER is only set for regtest. For other networks
                    // it is empty and so will cause us to use DNS discovery.
                    addresses = mService.getNetwork().getDefaultPeers();
                } else
                    addresses = new ArrayList<>(Arrays.asList(peers.split(",")));
                for (final String address : addresses) addPeer(address, getNetworkParameters(networkData));
                if (addresses.isEmpty() && !isProxyEnabled() && !getTorEnabled() && getNetworkParameters(networkData).getDnsSeeds() != null) {
                    // Blank w/o proxy: Use the built in resolving via DNS
                    mPeerGroup.addPeerDiscovery(new DnsDiscovery(getNetworkParameters(networkData)));
                }
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
    }

    private final TransactionReceivedInBlockListener mTxListner = new TransactionReceivedInBlockListener() {

        @Override
        public void receiveFromBlock(final Transaction tx, final StoredBlock block, final BlockChain.NewBlockType blockType, final int relativityOffset) throws VerificationException {
            if (tx == null)
                throw new RuntimeException("receiveFromBlock got null tx");
            Log.d(TAG, "receiveFromBlock " + tx.getHash().toString());
            addUtxoToValues(tx.getHash(), true);
        }

        @Override
        public boolean notifyTransactionIsInBlock(final Sha256Hash txHash, final StoredBlock block, final BlockChain.NewBlockType blockType, final int relativityOffset) throws VerificationException {
            Log.d(TAG, "notifyTransactionIsInBlock " + txHash.toString());
            return isUnspentOutpoint(txHash);
        }
    };

    private void stopSync() {
        synchronized (mStateLock) {
            Log.d(TAG, "stopSync: " + Var("isEnabled", isEnabled()));
            if (mPeerGroup != null && mPeerGroup.isRunning()) {
                Log.d(TAG, "Stopping peer group");
                final Intent i = new Intent("PEERGROUP_UPDATED");
                i.putExtra("peergroup", "stopSPVSync");
                mService.sendBroadcast(i);
                mPeerGroup.stop();
            }
            if (mNotifyManager != null)
                mNotifyManager.cancel(mNotificationId);
            if (mBlockChain != null) {
                Log.d(TAG, "Disposing of block chain");
                // mBlockChain.removeTransactionReceivedListener(mTxListner);
                mBlockChain = null;
            }
            if (mPeerGroup != null) {
                Log.d(TAG, "Deleting peer group");
                mPeerGroup.removePeerFilterProvider(mPeerFilter);
                mPeerGroup = null;
            }
            if (mBlockStore != null) {
                Log.d(TAG, "Closing block store");
                try {
                    mBlockStore.close();
                    mBlockStore = null;
                } catch (final BlockStoreException x) {
                    throw new RuntimeException(x);
                }
            }
        }
    }

    // We only care about mobile vs non-mobile so treat others as ethernet
    private int getNetworkType(final NetworkInfo info) {
        if (info == null)
            return ConnectivityManager.TYPE_DUMMY;
        final int type = info.getType();
        return type == ConnectivityManager.TYPE_MOBILE ? type : ConnectivityManager.TYPE_ETHERNET;
    }

    private int getNetworkType() {
        return getNetworkType(mService.getNetworkInfo());
    }

    // Handle changes to network connectivity.
    // Note that this only handles mobile/non-mobile transitions
    public void onNetConnectivityChangedAsync(final NetworkInfo info) {
        mExecutor.execute(() -> onNetConnectivityChanged(info));
    }

    private void onNetConnectivityChanged(final NetworkInfo info) {
        synchronized (mStateLock) {
            final int oldType = mNetWorkType;
            final int newType = getNetworkType(info);
            mNetWorkType = newType;
            if (!isEnabled() || newType == oldType)
                // No change
                return;
            Log.d(TAG, "onNetConnectivityChanged: " + Var("newType", newType) + Var("oldType", oldType) + Var("isSyncOnMobileEnabled", isSyncOnMobileEnabled()));
            // FIXME: - It seems network connectivity changes can happen when
            // mPeerGroup is null (i.e. setup hasn't been called),
            // but its not clear what path leads to this happening.
            if (newType == ConnectivityManager.TYPE_MOBILE) {
                if (!isSyncOnMobileEnabled())
                    // Mobile network and we have sync mobile disabled
                    stopSync();
            } else if (oldType == ConnectivityManager.TYPE_MOBILE) {
                if (isSyncOnMobileEnabled())
                    // Non-Mobile network and we have sync mobile enabled
                    startSync();
            }
        }
    }

    public void resetAsync() {
        mExecutor.execute(() -> reset(true, /* deleteAllData */
        true));
    }

    private void reset(final boolean deleteAllData, final boolean deleteUnspent) {
        synchronized (mStateLock) {
            Log.d(TAG, "reset: " + Var("deleteAllData", deleteAllData) + Var("deleteUnspent", deleteUnspent));
            stopSync();
            if (deleteAllData) {
                Log.d(TAG, "Deleting chain file");
                mService.getSPVChainFile().delete();
                try {
                    Log.d(TAG, "Clearing verified and spendable transactions");
                // TODO not a namespace anymore, delete all preferences starting with?
                // mService.cfgInEdit(SPENDABLE).clear().commit();
                // mService.cfgInEdit(VERIFIED).clear().commit();
                } catch (final NullPointerException e) {
                // ignore
                }
            }
            if (deleteUnspent) {
                Log.d(TAG, "Resetting unspent outputs");
                mUnspentDetails.clear();
                mUnspentOutpoints.clear();
                mCountedUtxoValues.clear();
                mVerifiedCoinBalances.clear();
            }
            if (isEnabled()) {
                setup();
                // We might race with our network callbacks, so fetch the network type
                // if its unknown.
                if (mNetWorkType == ConnectivityManager.TYPE_DUMMY)
                    mNetWorkType = getNetworkType();
                if (isSyncOnMobileEnabled() || mNetWorkType != ConnectivityManager.TYPE_MOBILE)
                    startSync();
            }
            Log.d(TAG, "Finished reset");
        }
    }

    public SharedPreferences cfg() {
        final String network = PreferenceManager.getDefaultSharedPreferences(mService).getString(PrefKeys.NETWORK_ID_ACTIVE, "mainnet");
        return mService.getSharedPreferences(network, MODE_PRIVATE);
    }

    public SharedPreferences.Editor cfgEdit() {
        return cfg().edit();
    }

    public String getProxyHost() {
        return cfg().getString(PrefKeys.PROXY_HOST, "");
    }

    public String getProxyPort() {
        return cfg().getString(PrefKeys.PROXY_PORT, "");
    }

    public boolean getProxyEnabled() {
        return cfg().getBoolean(PrefKeys.PROXY_ENABLED, false);
    }

    public boolean getTorEnabled() {
        return cfg().getBoolean(PrefKeys.TOR_ENABLED, false);
    }

    public boolean isProxyEnabled() {
        return !TextUtils.isEmpty(getProxyHost()) && !TextUtils.isEmpty(getProxyPort());
    }

    public String getSPVTrustedPeers() {
        return getTrustedPeers();
    }

    public void setSPVTrustedPeersAsync(final String peers) {
        setTrustedPeersAsync(peers);
    }

    public boolean isSPVEnabled() {
        return isEnabled();
    }

    public void setSPVEnabledAsync(final boolean enabled) {
        setEnabledAsync(enabled);
    }

    public boolean isSPVSyncOnMobileEnabled() {
        return isSyncOnMobileEnabled();
    }

    public void setSPVSyncOnMobileEnabledAsync(final boolean enabled) {
        setSyncOnMobileEnabledAsync(enabled);
    }

    public void resetSPVAsync() {
        resetAsync();
    }

    public PeerGroup getSPVPeerGroup() {
        return getPeerGroup();
    }

    public boolean isSPVVerified(final String txHash) {
        return isVerified(txHash);
    }

    public void enableSPVPingMonitoring() {
        enablePingMonitoring();
    }

    public void disableSPVPingMonitoring() {
        disablePingMonitoring();
    }
}

19 Source : FetchTransactions.java
with Apache License 2.0
from bitcoincash-wallet

public static void main(String[] args) throws Exception {
    BriefLogFormatter.init();
    System.out.println("Connecting to node");
    final NetworkParameters params = TestNet3Params.get();
    BlockStore blockStore = new MemoryBlockStore(params);
    BlockChain chain = new BlockChain(params, blockStore);
    PeerGroup peerGroup = new PeerGroup(params, chain);
    peerGroup.start();
    peerGroup.addAddress(new PeerAddress(InetAddress.getLocalHost(), params.getPort()));
    peerGroup.waitForPeers(1).get();
    Peer peer = peerGroup.getConnectedPeers().get(0);
    Sha256Hash txHash = Sha256Hash.wrap(args[0]);
    ListenableFuture<Transaction> future = peer.getPeerMempoolTransaction(txHash);
    System.out.println("Waiting for node to send us the requested transaction: " + txHash);
    Transaction tx = future.get();
    System.out.println(tx);
    System.out.println("Waiting for node to send us the dependencies ...");
    List<Transaction> deps = peer.downloadDependencies(tx).get();
    for (Transaction dep : deps) {
        System.out.println("Got dependency " + dep.getHashreplacedtring());
    }
    System.out.println("Done.");
    peerGroup.stop();
}

19 Source : FetchBlock.java
with Apache License 2.0
from bitcoincash-wallet

public static void main(String[] args) throws Exception {
    BriefLogFormatter.init();
    System.out.println("Connecting to node");
    final NetworkParameters params = TestNet3Params.get();
    BlockStore blockStore = new MemoryBlockStore(params);
    BlockChain chain = new BlockChain(params, blockStore);
    PeerGroup peerGroup = new PeerGroup(params, chain);
    peerGroup.start();
    PeerAddress addr = new PeerAddress(InetAddress.getLocalHost(), params.getPort());
    peerGroup.addAddress(addr);
    peerGroup.waitForPeers(1).get();
    Peer peer = peerGroup.getConnectedPeers().get(0);
    Sha256Hash blockHash = Sha256Hash.wrap(args[0]);
    Future<Block> future = peer.getBlock(blockHash);
    System.out.println("Waiting for node to send us the requested block: " + blockHash);
    Block block = future.get();
    System.out.println(block);
    peerGroup.stopAsync();
}

19 Source : TestWithNetworkConnections.java
with Apache License 2.0
from bitcoincash-wallet

/**
 * Utility clreplaced that makes it easy to work with mock NetworkConnections.
 */
public clreplaced TestWithNetworkConnections {

    public static final int PEER_SERVERS = 5;

    protected static final NetworkParameters PARAMS = UnitTestParams.get();

    protected Context context;

    protected BlockStore blockStore;

    protected BlockChain blockChain;

    protected Wallet wallet;

    protected ECKey key;

    protected Address address;

    protected SocketAddress socketAddress;

    private NioServer[] peerServers = new NioServer[PEER_SERVERS];

    private final ClientConnectionManager channels;

    protected final BlockingQueue<InboundMessageQueuer> newPeerWriteTargetQueue = new LinkedBlockingQueue<InboundMessageQueuer>();

    public enum ClientType {

        NIO_CLIENT_MANAGER, BLOCKING_CLIENT_MANAGER, NIO_CLIENT, BLOCKING_CLIENT
    }

    private final ClientType clientType;

    public TestWithNetworkConnections(ClientType clientType) {
        this.clientType = clientType;
        if (clientType == ClientType.NIO_CLIENT_MANAGER)
            channels = new NioClientManager();
        else if (clientType == ClientType.BLOCKING_CLIENT_MANAGER)
            channels = new BlockingClientManager();
        else
            channels = null;
    }

    public void setUp() throws Exception {
        setUp(new MemoryBlockStore(UnitTestParams.get()));
    }

    public void setUp(BlockStore blockStore) throws Exception {
        BriefLogFormatter.init();
        Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
        this.blockStore = blockStore;
        // Allow subclreplacedes to override the wallet object with their own.
        if (wallet == null) {
            wallet = new Wallet(PARAMS);
            key = wallet.freshReceiveKey();
            address = key.toAddress(PARAMS);
        }
        blockChain = new BlockChain(PARAMS, wallet, blockStore);
        startPeerServers();
        if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
            channels.startAsync();
            channels.awaitRunning();
        }
        socketAddress = new InetSocketAddress("127.0.0.1", 1111);
    }

    protected void startPeerServers() throws IOException {
        for (int i = 0; i < PEER_SERVERS; i++) {
            startPeerServer(i);
        }
    }

    protected void startPeerServer(int i) throws IOException {
        peerServers[i] = new NioServer(new StreamConnectionFactory() {

            @Nullable
            @Override
            public StreamConnection getNewConnection(InetAddress inetAddress, int port) {
                return new InboundMessageQueuer(PARAMS) {

                    @Override
                    public void connectionClosed() {
                    }

                    @Override
                    public void connectionOpened() {
                        newPeerWriteTargetQueue.offer(this);
                    }
                };
            }
        }, new InetSocketAddress("127.0.0.1", 2000 + i));
        peerServers[i].startAsync();
        peerServers[i].awaitRunning();
    }

    public void tearDown() throws Exception {
        stopPeerServers();
    }

    protected void stopPeerServers() {
        for (int i = 0; i < PEER_SERVERS; i++) stopPeerServer(i);
    }

    protected void stopPeerServer(int i) {
        peerServers[i].stopAsync();
        peerServers[i].awaitTerminated();
    }

    protected InboundMessageQueuer connect(Peer peer, VersionMessage versionMessage) throws Exception {
        checkArgument(versionMessage.hasBlockChain());
        final AtomicBoolean doneConnecting = new AtomicBoolean(false);
        final Thread thisThread = Thread.currentThread();
        peer.addDisconnectedEventListener(new PeerDisconnectedEventListener() {

            @Override
            public void onPeerDisconnected(Peer p, int peerCount) {
                synchronized (doneConnecting) {
                    if (!doneConnecting.get())
                        thisThread.interrupt();
                }
            }
        });
        if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER)
            channels.openConnection(new InetSocketAddress("127.0.0.1", 2000), peer);
        else if (clientType == ClientType.NIO_CLIENT)
            new NioClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100);
        else if (clientType == ClientType.BLOCKING_CLIENT)
            new BlockingClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100, SocketFactory.getDefault(), null);
        else
            throw new RuntimeException();
        // Claim we are connected to a different IP that what we really are, so tx confidence broadcastBy sets work
        InboundMessageQueuer writeTarget = newPeerWriteTargetQueue.take();
        writeTarget.peer = peer;
        // Complete handshake with the peer - send/receive version(ack)s, receive bloom filter
        checkState(!peer.getVersionHandshakeFuture().isDone());
        writeTarget.sendMessage(versionMessage);
        writeTarget.sendMessage(new VersionAck());
        try {
            checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
            checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
            peer.getVersionHandshakeFuture().get();
            synchronized (doneConnecting) {
                doneConnecting.set(true);
            }
            // Clear interrupted bit in case it was set before we got into the CS
            Thread.interrupted();
        } catch (InterruptedException e) {
        // We were disconnected before we got back version/verack
        }
        return writeTarget;
    }

    protected void closePeer(Peer peer) throws Exception {
        peer.close();
    }

    protected void inbound(InboundMessageQueuer peerChannel, Message message) {
        peerChannel.sendMessage(message);
    }

    private void outboundPingAndWait(final InboundMessageQueuer p, long nonce) throws Exception {
        // Send a ping and wait for it to get to the other side
        SettableFuture<Void> pingReceivedFuture = SettableFuture.create();
        p.mapPingFutures.put(nonce, pingReceivedFuture);
        p.peer.sendMessage(new Ping(nonce));
        pingReceivedFuture.get();
        p.mapPingFutures.remove(nonce);
    }

    private void inboundPongAndWait(final InboundMessageQueuer p, final long nonce) throws Exception {
        // Receive a ping (that the Peer doesn't see) and wait for it to get through the socket
        final SettableFuture<Void> pongReceivedFuture = SettableFuture.create();
        PreMessageReceivedEventListener listener = new PreMessageReceivedEventListener() {

            @Override
            public Message onPreMessageReceived(Peer p, Message m) {
                if (m instanceof Pong && ((Pong) m).getNonce() == nonce) {
                    pongReceivedFuture.set(null);
                    return null;
                }
                return m;
            }
        };
        p.peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD, listener);
        inbound(p, new Pong(nonce));
        pongReceivedFuture.get();
        p.peer.removePreMessageReceivedEventListener(listener);
    }

    protected void pingAndWait(final InboundMessageQueuer p) throws Exception {
        final long nonce = (long) (Math.random() * Long.MAX_VALUE);
        // Start with an inbound Pong as pingAndWait often happens immediately after an inbound() call, and then wants
        // to wait on an outbound message, so we do it in the same order or we see race conditions
        inboundPongAndWait(p, nonce);
        outboundPingAndWait(p, nonce);
    }

    protected Message outbound(InboundMessageQueuer p1) throws Exception {
        pingAndWait(p1);
        return p1.nextMessage();
    }

    protected Message waitForOutbound(InboundMessageQueuer ch) throws InterruptedException {
        return ch.nextMessageBlocking();
    }

    protected Peer peerOf(InboundMessageQueuer ch) {
        return ch.peer;
    }
}

19 Source : BlockChainTest.java
with Apache License 2.0
from bitcoincash-wallet

// Handling of chain splits/reorgs are in ChainSplitTests.
public clreplaced BlockChainTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    private BlockChain testNetChain;

    private Wallet wallet;

    private BlockChain chain;

    private BlockStore blockStore;

    private Address coinbaseTo;

    private static final NetworkParameters PARAMS = UnitTestParams.get();

    private final StoredBlock[] block = new StoredBlock[1];

    private Transaction coinbaseTransaction;

    private static clreplaced TweakableTestNet2Params extends TestNet2Params {

        public TweakableTestNet2Params() {
            super();
            this.cashHardForkActivationTime = (System.currentTimeMillis() / 1000) + 24 * 60 * 60;
        }

        public void setMaxTarget(BigInteger limit) {
            maxTarget = limit;
        }
    }

    private static final TweakableTestNet2Params testNet = new TweakableTestNet2Params();

    private void resetBlockStore() {
        blockStore = new MemoryBlockStore(PARAMS);
    }

    @Before
    public void setUp() throws Exception {
        BriefLogFormatter.initVerbose();
        Context.propagate(new Context(testNet, 100, Coin.ZERO, false));
        testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
        Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
        wallet = new Wallet(PARAMS) {

            @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 (isTransactionRelevant(tx) && tx.isCoinBase()) {
                    BlockChainTest.this.coinbaseTransaction = tx;
                }
            }
        };
        wallet.freshReceiveKey();
        resetBlockStore();
        chain = new BlockChain(PARAMS, wallet, blockStore);
        coinbaseTo = wallet.currentReceiveKey().toAddress(PARAMS);
    }

    @Test
    public void testBasicChaining() throws Exception {
        // Check that we can plug a few blocks together and the futures work.
        ListenableFuture<StoredBlock> future = testNetChain.getHeightFuture(2);
        // Block 1 from the testnet.
        Block b1 = getBlock1();
        replacedertTrue(testNetChain.add(b1));
        replacedertFalse(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.
        replacedertTrue(testNetChain.add(b2));
        replacedertTrue(future.isDone());
        replacedertEquals(2, future.get().getHeight());
    }

    @Test
    public void receiveCoins() throws Exception {
        int height = 1;
        // Quick check that we can actually receive coins.
        Transaction tx1 = createFakeTx(PARAMS, COIN, wallet.currentReceiveKey().toAddress(PARAMS));
        Block b1 = createFakeBlock(blockStore, height, tx1).block;
        chain.add(b1);
        replacedertTrue(wallet.getBalance().signum() > 0);
    }

    @Test
    public void unconnectedBlocks() throws Exception {
        Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        Block b3 = b2.createNextBlock(coinbaseTo);
        // Connected.
        replacedertTrue(chain.add(b1));
        // Unconnected but stored. The head of the chain is still b1.
        replacedertFalse(chain.add(b3));
        replacedertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
        // Add in the middle block.
        replacedertTrue(chain.add(b2));
        replacedertEquals(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 = PARAMS.getGenesisBlock();
        Utils.setMockClock(System.currentTimeMillis() / 1000);
        for (int height = 0; height < PARAMS.getInterval() - 1; height++) {
            Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
            replacedertTrue(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, 1, Utils.currentTimeSeconds(), PARAMS.getInterval()));
            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, 1, Utils.currentTimeSeconds(), PARAMS.getInterval() + 1);
        b.setDifficultyTarget(0x201fFFFFL);
        b.solve();
        replacedertTrue(chain.add(b));
    // Successfully traversed a difficulty transition period.
    }

    @Test
    public void badDifficulty() throws Exception {
        replacedertTrue(testNetChain.add(getBlock1()));
        Block b2 = getBlock2();
        replacedertTrue(testNetChain.add(b2));
        Block bad = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
        // Merkle root can be anything here, doesn't matter.
        bad.setMerkleRoot(Sha256Hash.wrap("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 preplaced, 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) {
            replacedertTrue(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();
        // TODO: Bitcoin Cash difficulty algorithm returns successfully if there are less than 6 blocks in the blockchain.  This test only has 3.  We will let the test finish for now.
        } catch (VerificationException e) {
            replacedertTrue(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 that version 2 blocks are rejected once version 3 blocks are a super
     * majority.
     */
    @Test
    public void badBip66Version() throws Exception {
        testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP34, Block.BLOCK_VERSION_BIP66);
    }

    /**
     * Test that version 3 blocks are rejected once version 4 blocks are a super
     * majority.
     */
    @Test
    public void badBip65Version() throws Exception {
        testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP66, Block.BLOCK_VERSION_BIP65);
    }

    private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
        final BlockStore versionBlockStore = new MemoryBlockStore(PARAMS);
        final BlockChain versionChain = new BlockChain(PARAMS, versionBlockStore);
        // Build a historical chain of version 3 blocks
        long timeSeconds = 1231006505;
        int height = 0;
        FakeTxBuilder.BlockPair chainHead = null;
        // Put in just enough v2 blocks to be a minority
        for (height = 0; height < (PARAMS.getMajorityWindow() - PARAMS.getMajorityRejectBlockOutdated()); height++) {
            chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
            versionChain.add(chainHead.block);
            timeSeconds += 60;
        }
        // Fill the rest of the window with v3 blocks
        for (; height < PARAMS.getMajorityWindow(); height++) {
            chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
            versionChain.add(chainHead.block);
            timeSeconds += 60;
        }
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
        // Trying to add a new v2 block should result in rejection
        thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
        try {
            versionChain.add(chainHead.block);
        } catch (final VerificationException ex) {
            throw (Exception) ex.getCause();
        }
    }

    @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 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        Block b3 = b2.createNextBlock(coinbaseTo);
        replacedertTrue(chain.add(b1));
        replacedertEquals(b1, block[0].getHeader());
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2, block[0].getHeader());
        replacedertTrue(chain.add(b3));
        replacedertEquals(b3, block[0].getHeader());
        replacedertEquals(b3, chain.getChainHead().getHeader());
        replacedertTrue(chain.add(b2));
        replacedertEquals(b3, chain.getChainHead().getHeader());
        // Wallet was NOT called with the new block because the duplicate add was spotted.
        replacedertEquals(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(PARAMS);
        Block b1 = PARAMS.getGenesisBlock().createNextBlock(somebodyElse);
        ECKey key = wallet.freshReceiveKey();
        Address addr = key.toAddress(PARAMS);
        // Create a tx that gives us some coins, and another that spends it to someone else in the same block.
        Transaction t1 = FakeTxBuilder.createFakeTx(PARAMS, COIN, addr);
        Transaction t2 = new Transaction(PARAMS);
        t2.addInput(t1.getOutputs().get(0));
        t2.addOutput(valueOf(2, 0), somebodyElse);
        b1.addTransaction(t1);
        b1.addTransaction(t2);
        b1.solve();
        chain.add(b1);
        replacedertEquals(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(PARAMS);
        ECKey receiveKey = wallet2.freshReceiveKey();
        int height = 1;
        chain.addWallet(wallet2);
        Address addressToSendTo = receiveKey.toAddress(PARAMS);
        // Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
        Block b1 = PARAMS.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
        chain.add(b1);
        // Check a transaction has been received.
        replacedertNotNull(coinbaseTransaction);
        // The coinbase tx is not yet available to spend.
        replacedertEquals(Coin.ZERO, wallet.getBalance());
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
        replacedertTrue(!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 < PARAMS.getSpendableCoinbaseDepth() - 2; i++) {
            // Non relevant tx - just for fake block creation.
            Transaction tx2 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
            Block b2 = createFakeBlock(blockStore, height++, tx2).block;
            chain.add(b2);
            // Wallet still does not have the coinbase transaction available for spend.
            replacedertEquals(Coin.ZERO, wallet.getBalance());
            replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
            // The coinbase transaction is still not mature.
            replacedertTrue(!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(PARAMS, COIN, new ECKey().toAddress(PARAMS));
        Block b3 = createFakeBlock(blockStore, height++, tx3).block;
        chain.add(b3);
        // Wallet now has the coinbase transaction available for spend.
        replacedertEquals(wallet.getBalance(), FIFTY_COINS);
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
        replacedertTrue(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));
        replacedertNotNull(coinbaseSend2);
        // Commit the coinbaseSpend to the first wallet and check the balances decrement.
        wallet.commitTx(coinbaseSend2);
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN);
        // Available balance is zero as change has not been received from a block yet.
        replacedertEquals(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, height++, coinbaseSend2).block;
        chain.add(b4);
        replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);
        // Check the balances in the second wallet.
        replacedertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0));
        replacedertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0));
    }

    // Some blocks from the test net.
    private static Block getBlock2() throws Exception {
        Block b2 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
        b2.setMerkleRoot(Sha256Hash.wrap("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
        b2.setNonce(2642058077L);
        b2.setTime(1296734343L);
        b2.setPrevBlockHash(Sha256Hash.wrap("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
        replacedertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashreplacedtring());
        b2.verifyHeader();
        return b2;
    }

    private static Block getBlock1() throws Exception {
        Block b1 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
        b1.setMerkleRoot(Sha256Hash.wrap("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
        b1.setNonce(236038445);
        b1.setTime(1296734340);
        b1.setPrevBlockHash(Sha256Hash.wrap("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
        replacedertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashreplacedtring());
        b1.verifyHeader();
        return b1;
    }

    @Test
    public void estimatedBlockTime() throws Exception {
        NetworkParameters params = MainNetParams.get();
        BlockChain prod = new BlockChain(new Context(params), new MemoryBlockStore(params));
        Date d = prod.estimateBlockTime(200000);
        // The actual date of block 200,000 was 2012-09-22 10:47:00
        replacedertEquals(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;
        // Exactly
        replacedertTrue(0 == chain.getFalsePositiveRate());
        chain.trackFalsePositives(55);
        replacedertEquals(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%
        replacedertEquals(0.1, chain.getFalsePositiveRate(), 0.01);
        // Check that we get repeatable results after a reset
        chain.resetFalsePositiveEstimate();
        // Exactly
        replacedertTrue(0 == chain.getFalsePositiveRate());
        chain.trackFalsePositives(55);
        replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
        chain.trackFilteredTransactions(550);
        replacedertEquals(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 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        // Add block 1, no frills.
        replacedertTrue(chain.add(b1));
        replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(1, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Add block 2 while wallet is disconnected, to simulate crash.
        chain.removeWallet(wallet);
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(2, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Add wallet back. This will detect the height mismatch and repair the damage done.
        chain.addWallet(wallet);
        replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(1, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Now add block 2 correctly.
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(2, chain.getBestChainHeight());
        replacedertEquals(2, wallet.getLastBlockSeenHeight());
    }
}

19 Source : VersionTally.java
with Apache License 2.0
from bitcoincash-wallet

/**
 * Initialize the version tally from the block store. Note this does not
 * search backwards past the start of the block store, so if starting from
 * a checkpoint this may not fill the window.
 *
 * @param blockStore block store to load blocks from.
 * @param chainHead current chain tip.
 */
public void initialize(final BlockStore blockStore, final StoredBlock chainHead) throws BlockStoreException {
    StoredBlock versionBlock = chainHead;
    final Stack<Long> versions = new Stack<Long>();
    // We don't know how many blocks back we can go, so load what we can first
    versions.push(versionBlock.getHeader().getVersion());
    for (int headOffset = 0; headOffset < versionWindow.length; headOffset++) {
        versionBlock = versionBlock.getPrev(blockStore);
        if (null == versionBlock) {
            break;
        }
        versions.push(versionBlock.getHeader().getVersion());
    }
    // Replay the versions into the tally
    while (!versions.isEmpty()) {
        add(versions.pop());
    }
}

19 Source : TestNet3Params.java
with Apache License 2.0
from bitcoincash-wallet

@Override
protected void checkNextCashWorkRequired(StoredBlock storedPrev, Block nextBlock, BlockStore blockStore) {
    // This cannot handle the genesis block and early blocks in general.
    // replacedert(pindexPrev);
    // Compute the difficulty based on the full adjustement interval.
    int nHeight = storedPrev.getHeight();
    Preconditions.checkState(nHeight >= this.interval);
    // Get the last suitable block of the difficulty interval.
    try {
        // Special difficulty rule for testnet:
        // If the new block's timestamp is more than 2* 10 minutes then allow
        // mining of a min-difficulty block.
        Block prev = storedPrev.getHeader();
        final long timeDelta = nextBlock.getTimeSeconds() - prev.getTimeSeconds();
        if (timeDelta >= 0 && timeDelta > NetworkParameters.TARGET_SPACING * 2) {
            if (!maxTarget.equals(nextBlock.getDifficultyTargetAsInteger()))
                throw new VerificationException("Testnet block transition that is not allowed: " + Long.toHexString(Utils.encodeCompactBits(maxTarget)) + " (required min difficulty) vs " + Long.toHexString(nextBlock.getDifficultyTarget()));
            return;
        }
        StoredBlock pindexLast = GetSuitableBlock(storedPrev, blockStore);
        // replacedert (pindexLast);
        // Get the first suitable block of the difficulty interval.
        int nHeightFirst = nHeight - 144;
        StoredBlock pindexFirst = storedPrev;
        for (int i = 144; i > 0; --i) {
            pindexFirst = pindexFirst.getPrev(blockStore);
            if (pindexFirst == null)
                return;
        }
        pindexFirst = GetSuitableBlock(pindexFirst, blockStore);
        // replacedert (pindexFirst);
        // Compute the target based on time and work done during the interval.
        BigInteger nextTarget = ComputeTarget(pindexFirst, pindexLast);
        verifyDifficulty(nextTarget, nextBlock);
    } catch (BlockStoreException x) {
        // this means we don't have enough blocks, yet.  let it go until we do.
        return;
    }
}

19 Source : TestNet3Params.java
with Apache License 2.0
from bitcoincash-wallet

@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore, AbstractBlockChain blockChain) throws VerificationException, BlockStoreException {
    if (storedPrev.getHeight() < daaHeight && !isDifficultyTransitionPoint(storedPrev) && nextBlock.getTime().after(testnetDiffDate)) {
        Block prev = storedPrev.getHeader();
        // After 15th February 2012 the rules on the testnet change to avoid people running up the difficulty
        // and then leaving, making it too hard to mine a block. On non-difficulty transition points, easy
        // blocks are allowed if there has been a span of 20 minutes without one.
        final long timeDelta = nextBlock.getTimeSeconds() - prev.getTimeSeconds();
        // There is an integer underflow bug in bitcoin-qt that means mindiff blocks are accepted when time
        // goes backwards.
        if (timeDelta >= 0 && timeDelta <= NetworkParameters.TARGET_SPACING * 2) {
            // Walk backwards until we find a block that doesn't have the easiest proof of work, then check
            // that difficulty is equal to that one.
            StoredBlock cursor = storedPrev;
            while (!cursor.getHeader().equals(getGenesisBlock()) && cursor.getHeight() % getInterval() != 0 && cursor.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) cursor = cursor.getPrev(blockStore);
            BigInteger cursorTarget = cursor.getHeader().getDifficultyTargetAsInteger();
            BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger();
            if (!cursorTarget.equals(newTarget))
                throw new VerificationException("Testnet block transition that is not allowed: " + Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " + Long.toHexString(nextBlock.getDifficultyTarget()));
        }
    } else {
        super.checkDifficultyTransitions(storedPrev, nextBlock, blockStore, blockChain);
    }
}

19 Source : AbstractBitcoinNetParams.java
with Apache License 2.0
from bitcoincash-wallet

/**
 * To reduce the impact of timestamp manipulation, we select the block we are
 * basing our computation on via a median of 3.
 */
StoredBlock GetSuitableBlock(StoredBlock pindex, BlockStore blockStore) throws BlockStoreException {
    // replacedert(pindex->nHeight >= 3);
    /**
     * In order to avoid a block is a very skewed timestamp to have too much
     * influence, we select the median of the 3 top most blocks as a starting
     * point.
     */
    StoredBlock[] blocks = new StoredBlock[3];
    blocks[2] = pindex;
    blocks[1] = pindex.getPrev(blockStore);
    blocks[0] = blocks[1].getPrev(blockStore);
    // Sorting network.
    if (blocks[0].getHeader().getTimeSeconds() > blocks[2].getHeader().getTimeSeconds()) {
        // std::swap(blocks[0], blocks[2]);
        StoredBlock temp = blocks[0];
        blocks[0] = blocks[2];
        blocks[2] = temp;
    }
    if (blocks[0].getHeader().getTimeSeconds() > blocks[1].getHeader().getTimeSeconds()) {
        // std::swap(blocks[0], blocks[1]);
        StoredBlock temp = blocks[0];
        blocks[0] = blocks[1];
        blocks[1] = temp;
    }
    if (blocks[1].getHeader().getTimeSeconds() > blocks[2].getHeader().getTimeSeconds()) {
        // std::swap(blocks[1], blocks[2]);
        StoredBlock temp = blocks[1];
        blocks[1] = blocks[2];
        blocks[2] = temp;
    }
    // We should have our candidate in the middle now.
    return blocks[1];
}

19 Source : BlockchainExtended.java
with MIT License
from Bitcoin-com

public clreplaced BlockchainExtended extends AbstractBlockChain {

    private static final Logger logger = LoggerFactory.getLogger(BlockchainExtended.clreplaced);

    /**
     * Keeps a map of block hashes to StoredBlocks.
     */
    protected final BlockStore blockStore;

    private BitcoinJListener bitcoinJListener;

    private String lastHandledBlockHash = "";

    /**
     * <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
     * one from scratch, or you can deserialize a saved wallet from disk using
     * </p>
     *
     * <p>For the store, you should use {@link org.bitcoinj.store.SPVBlockStore} or you could also try a
     * {@link org.bitcoinj.store.MemoryBlockStore} if you want to hold all headers in RAM and don't care about
     * disk serialization (this is rare).</p>
     */
    public BlockchainExtended(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
        addWallet(wallet);
    }

    /**
     * See {@link #BlockchainExtended(Context, Wallet, BlockStore)}}
     */
    public BlockchainExtended(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallet, blockStore);
    }

    /**
     * Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
     * and receiving coins but rather, just want to explore the network data structures.
     */
    public BlockchainExtended(Context context, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * See {@link #BlockchainExtended(Context, BlockStore)}
     */
    public BlockchainExtended(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
        this(params, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * Constructs a BlockChain connected to the given list of listeners and a store.
     */
    public BlockchainExtended(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        super(params, wallets, blockStore);
        this.blockStore = blockStore;
    }

    /**
     * See {@link #BlockchainExtended(Context, List, BlockStore)}
     */
    public BlockchainExtended(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallets, blockStore);
    }

    public BlockchainExtended(Context context, BlockStore blockStore, BitcoinJListener bitcoinJListener) throws BlockStoreException {
        this(context, new ArrayList<>(), blockStore, bitcoinJListener);
    }

    public BlockchainExtended(Context context, ArrayList<Wallet> wallets, BlockStore blockStore, BitcoinJListener bitcoinJListener) throws BlockStoreException {
        super(context, wallets, blockStore);
        this.bitcoinJListener = bitcoinJListener;
        this.blockStore = blockStore;
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected void connectBlock(Block block, StoredBlock storedPrev, boolean expensiveChecks, @Nullable List<Sha256Hash> filteredTxHashList, @Nullable Map<Sha256Hash, Transaction> filteredTxn) throws BlockStoreException, VerificationException, PrunedException {
        checkState(lock.isHeldByCurrentThread());
        boolean filtered = filteredTxHashList != null && filteredTxn != null;
        // Check that we aren't connecting a block that fails a checkpoint check
        if (!params.preplacedesCheckpoint(storedPrev.getHeight() + 1, block.getHash())) {
            throw new VerificationException("Block failed checkpoint lockin at " + (storedPrev.getHeight() + 1));
        }
        if (shouldVerifyTransactions()) {
            checkNotNull(block.getTransactions());
            for (Transaction tx : block.getTransactions()) {
                if (!tx.isFinal(storedPrev.getHeight() + 1, block.getTimeSeconds())) {
                    throw new VerificationException("Block contains non-final transaction");
                }
            }
        }
        StoredBlock head = getChainHead();
        if (storedPrev.equals(head)) {
            if (filtered && filteredTxn.size() > 0) {
                logger.debug("Block {} connects to top of best chain with {} transaction(s) of which we were sent {}", block.getHashreplacedtring(), filteredTxHashList.size(), filteredTxn.size());
                for (Sha256Hash hash : filteredTxHashList) {
                    logger.debug("  matched tx {}", hash);
                }
            }
            if (expensiveChecks && block.getTimeSeconds() <= getMedianTimestampOfRecentBlocks(head, blockStore)) {
                throw new VerificationException("Block's timestamp is too early");
            }
            // BIP 66 & 65: Enforce block version 3/4 once they are a supermajority of blocks
            // NOTE: This requires 1,000 blocks since the last checkpoint (on main
            // net, less on test) in order to be applied. It is also limited to
            // stopping addition of new v2/3 blocks to the tip of the chain.
            if (block.getVersion() == Block.BLOCK_VERSION_BIP34 || block.getVersion() == Block.BLOCK_VERSION_BIP66) {
                final Integer count = versionTally.getCountAtOrAbove(block.getVersion() + 1);
                if (count != null && count >= params.getMajorityRejectBlockOutdated()) {
                    throw new VerificationException.BlockVersionOutOfDate(block.getVersion());
                }
            }
            // This block connects to the best known block, it is a normal continuation of the system.
            TransactionOutputChanges txOutChanges = null;
            if (shouldVerifyTransactions()) {
                txOutChanges = connectTransactions(storedPrev.getHeight() + 1, block);
            }
            handleGenesis(storedPrev);
            if (!lastHandledBlockHash.equals(block.getHashreplacedtring())) {
                try {
                    bitcoinJListener.onBlocksDownloaded(block, storedPrev.getHeight() + 1);
                    lastHandledBlockHash = block.getHashreplacedtring();
                } catch (Exception e) {
                    logger.error("Download block handler failed exiting before ruining the SPV blockhash={}", block.getHashreplacedtring(), e);
                    while (true) {
                        try {
                            Thread.sleep(1000);
                            logger.error("Retrying block handler for hash={}", block.getHashreplacedtring());
                            bitcoinJListener.onBlocksDownloaded(block, storedPrev.getHeight() + 1);
                            lastHandledBlockHash = block.getHashreplacedtring();
                            break;
                        } catch (Exception ex) {
                        }
                    }
                }
            }
            StoredBlock newStoredBlock = addToBlockStore(storedPrev, block.getTransactions() == null ? block : block.cloneAsHeader(), txOutChanges);
            versionTally.add(block.getVersion());
            setChainHead(newStoredBlock);
            logger.debug("Chain is now {} blocks high, running listeners", newStoredBlock.getHeight());
            informListenersForNewBlock(block, NewBlockType.BEST_CHAIN, filteredTxHashList, filteredTxn, newStoredBlock);
        } else {
            // This block connects to somewhere other than the top of the best known chain. We treat these differently.
            // 
            // Note that we send the transactions to the wallet FIRST, even if we're about to re-organize this block
            // to become the new best chain head. This simplifies handling of the re-org in the Wallet clreplaced.
            StoredBlock newBlock = storedPrev.build(block);
            boolean haveNewBestChain = newBlock.moreWorkThan(head);
            if (haveNewBestChain) {
                logger.info("Block is causing a re-organize");
            } else {
                StoredBlock splitPoint = findSplit(newBlock, head, blockStore);
                if (splitPoint != null && splitPoint.equals(newBlock)) {
                    // newStoredBlock is a part of the same chain, there's no fork. This happens when we receive a block
                    // that we already saw and linked into the chain previously, which isn't the chain head.
                    // Re-processing it is confusing for the wallet so just skip.
                    logger.warn("Saw duplicated block in main chain at height {}: {}", newBlock.getHeight(), newBlock.getHeader().getHash());
                    return;
                }
                if (splitPoint == null) {
                    // This should absolutely never happen
                    // (lets not write the full block to disk to keep any bugs which allow this to happen
                    // from writing unreasonable amounts of data to disk)
                    throw new VerificationException("Block forks the chain but splitPoint is null");
                } else {
                    // We aren't actually spending any transactions (yet) because we are on a fork
                    addToBlockStore(storedPrev, block);
                    int splitPointHeight = splitPoint.getHeight();
                    String splitPointHash = splitPoint.getHeader().getHashreplacedtring();
                    logger.info("Block forks the chain at height {}/block {}, but it did not cause a reorganize:\n{}", splitPointHeight, splitPointHash, newBlock.getHeader().getHashreplacedtring());
                }
            }
            // We may not have any transactions if we received only a header, which can happen during fast catchup.
            // If we do, send them to the wallet but state that they are on a side chain so it knows not to try and
            // spend them until they become activated.
            if (block.getTransactions() != null || filtered) {
                informListenersForNewBlock(block, NewBlockType.SIDE_CHAIN, filteredTxHashList, filteredTxn, newBlock);
            }
            if (haveNewBestChain) {
                handleNewBestChain(storedPrev, newBlock, block, expensiveChecks);
            }
        }
    }

    private void handleGenesis(StoredBlock storedPrev) {
        try {
            if (storedPrev.getHeight() + 1 == 1) {
                logger.info("Adding genesis to index");
                bitcoinJListener.onBlocksDownloaded(Genesis.GENESIS, 0);
            }
        } catch (Exception e) {
            try {
                logger.error("Error handling genesis block handler for hash={}", storedPrev.getHeader().getHashreplacedtring(), e);
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
            }
            handleGenesis(storedPrev);
        }
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected void rollbackBlockStore(int height) throws BlockStoreException {
        lock.lock();
        try {
            int currentHeight = getBestChainHeight();
            checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
            if (height == currentHeight) {
                // nothing to do
                return;
            }
            // Look for the block we want to be the new chain head
            StoredBlock newChainHead = blockStore.getChainHead();
            while (newChainHead.getHeight() > height) {
                newChainHead = newChainHead.getPrev(blockStore);
                if (newChainHead == null) {
                    throw new BlockStoreException("Unreachable height");
                }
            }
            // Modify store directly
            blockStore.put(newChainHead);
            this.setChainHead(newChainHead);
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean shouldVerifyTransactions() {
        return false;
    }

    @Override
    protected TransactionOutputChanges connectTransactions(int height, Block block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void disconnectTransactions(StoredBlock block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
        blockStore.setChainHead(chainHead);
    }

    @Override
    protected void notSettingChainHead() throws BlockStoreException {
    // We don't use DB transactions here, so we don't need to do anything
    }

    @Override
    protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
        return blockStore.get(hash);
    }

    @Override
    public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
        boolean success = super.add(block);
        if (success) {
            trackFilteredTransactions(block.getTransactionCount());
        }
        return success;
    }
}

19 Source : BitcoinJConfig.java
with MIT License
from Bitcoin-com

@Override
public void destroy() throws Exception {
    try {
        peerGroup.stopAsync().get(10, TimeUnit.SECONDS);
    } catch (RuntimeException e) {
        logger.error("Failed to stop PeerGroup", e);
    } finally {
        BlockStore blockStore = blockChain.getBlockStore();
        blockStore.close();
    }
}

19 Source : VersionTallyTest.java
with GNU General Public License v3.0
from bcmapp

@Test
public void testInitialize() throws BlockStoreException {
    final BlockStore blockStore = new MemoryBlockStore(UNITTEST);
    final BlockChain chain = new BlockChain(UNITTEST, blockStore);
    // Build a historical chain of version 2 blocks
    long timeSeconds = 1231006505;
    StoredBlock chainHead = null;
    for (int height = 0; height < UNITTEST.getMajorityWindow(); height++) {
        chainHead = FakeTxBuilder.createFakeBlock(blockStore, 2, timeSeconds, height).storedBlock;
        replacedertEquals(2, chainHead.getHeader().getVersion());
        timeSeconds += 60;
    }
    VersionTally instance = new VersionTally(UNITTEST);
    instance.initialize(blockStore, chainHead);
    replacedertEquals(UNITTEST.getMajorityWindow(), instance.getCountAtOrAbove(2).intValue());
}

19 Source : TestWithWallet.java
with GNU General Public License v3.0
from bcmapp

// TODO: This needs to be somewhat rewritten - the "sendMoneyToWallet" methods aren't sending via the block chain object
/**
 * A utility clreplaced that you can derive from in your unit tests. TestWithWallet sets up an empty wallet,
 * an in-memory block store and a block chain object. It also provides helper methods for filling the wallet
 * with money in whatever ways you wish. Note that for simplicity with amounts, this clreplaced sets the default
 * fee per kilobyte to zero in setUp.
 */
public clreplaced TestWithWallet {

    protected static final NetworkParameters UNITTEST = UnitTestParams.get();

    protected static final NetworkParameters MAINNET = MainNetParams.get();

    protected ECKey myKey;

    protected Address myAddress;

    protected Wallet wallet;

    protected BlockChain chain;

    protected BlockStore blockStore;

    public void setUp() throws Exception {
        BriefLogFormatter.init();
        Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
        wallet = new Wallet(UNITTEST);
        myKey = wallet.currentReceiveKey();
        myAddress = LegacyAddress.fromKey(UNITTEST, myKey);
        blockStore = new MemoryBlockStore(UNITTEST);
        chain = new BlockChain(UNITTEST, wallet, blockStore);
    }

    public void tearDown() throws Exception {
    }

    @Nullable
    protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
        if (type == null) {
            // Pending transaction
            for (Transaction tx : transactions) if (wallet.isPendingTransactionRelevant(tx))
                wallet.receivePending(tx, null);
        } else {
            FakeTxBuilder.BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, transactions);
            for (Transaction tx : transactions) wallet.receiveFromBlock(tx, bp.storedBlock, type, 0);
            if (type == AbstractBlockChain.NewBlockType.BEST_CHAIN)
                wallet.notifyNewBestBlock(bp.storedBlock);
        }
        if (transactions.length == 1)
            // Can be null if tx is a double spend that's otherwise irrelevant.
            return wallet.getTransaction(transactions[0].getHash());
        else
            return null;
    }

    @Nullable
    protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
        return sendMoneyToWallet(wallet, type, createFakeTx(UNITTEST, value, toAddress));
    }

    @Nullable
    protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
        return sendMoneyToWallet(wallet, type, createFakeTx(UNITTEST, value, toPubKey));
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, transactions);
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, value, myAddress);
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, value, toAddress);
    }

    @Nullable
    protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
        return sendMoneyToWallet(this.wallet, type, value, toPubKey);
    }
}

19 Source : TestWithNetworkConnections.java
with GNU General Public License v3.0
from bcmapp

/**
 * Utility clreplaced that makes it easy to work with mock NetworkConnections.
 */
public clreplaced TestWithNetworkConnections {

    public static final int PEER_SERVERS = 5;

    protected static final NetworkParameters UNITTEST = UnitTestParams.get();

    protected Context context;

    protected BlockStore blockStore;

    protected BlockChain blockChain;

    protected Wallet wallet;

    protected ECKey key;

    protected Address address;

    protected SocketAddress socketAddress;

    private NioServer[] peerServers = new NioServer[PEER_SERVERS];

    private final ClientConnectionManager channels;

    protected final BlockingQueue<InboundMessageQueuer> newPeerWriteTargetQueue = new LinkedBlockingQueue<>();

    public enum ClientType {

        NIO_CLIENT_MANAGER, BLOCKING_CLIENT_MANAGER, NIO_CLIENT, BLOCKING_CLIENT
    }

    private final ClientType clientType;

    public TestWithNetworkConnections(ClientType clientType) {
        this.clientType = clientType;
        if (clientType == ClientType.NIO_CLIENT_MANAGER)
            channels = new NioClientManager();
        else if (clientType == ClientType.BLOCKING_CLIENT_MANAGER)
            channels = new BlockingClientManager();
        else
            channels = null;
    }

    public void setUp() throws Exception {
        setUp(new MemoryBlockStore(UNITTEST));
    }

    public void setUp(BlockStore blockStore) throws Exception {
        BriefLogFormatter.init();
        Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
        this.blockStore = blockStore;
        // Allow subclreplacedes to override the wallet object with their own.
        if (wallet == null) {
            wallet = new Wallet(UNITTEST);
            key = wallet.freshReceiveKey();
            address = LegacyAddress.fromKey(UNITTEST, key);
        }
        blockChain = new BlockChain(UNITTEST, wallet, blockStore);
        startPeerServers();
        if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
            channels.startAsync();
            channels.awaitRunning();
        }
        socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 1111);
    }

    protected void startPeerServers() throws IOException {
        for (int i = 0; i < PEER_SERVERS; i++) {
            startPeerServer(i);
        }
    }

    protected void startPeerServer(int i) throws IOException {
        peerServers[i] = new NioServer(new StreamConnectionFactory() {

            @Nullable
            @Override
            public StreamConnection getNewConnection(InetAddress inetAddress, int port) {
                return new InboundMessageQueuer(UNITTEST) {

                    @Override
                    public void connectionClosed() {
                    }

                    @Override
                    public void connectionOpened() {
                        newPeerWriteTargetQueue.offer(this);
                    }
                };
            }
        }, new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000 + i));
        peerServers[i].startAsync();
        peerServers[i].awaitRunning();
    }

    public void tearDown() throws Exception {
        stopPeerServers();
    }

    protected void stopPeerServers() {
        for (int i = 0; i < PEER_SERVERS; i++) stopPeerServer(i);
    }

    protected void stopPeerServer(int i) {
        peerServers[i].stopAsync();
        peerServers[i].awaitTerminated();
    }

    protected InboundMessageQueuer connect(Peer peer, VersionMessage versionMessage) throws Exception {
        checkArgument(versionMessage.hasBlockChain());
        final AtomicBoolean doneConnecting = new AtomicBoolean(false);
        final Thread thisThread = Thread.currentThread();
        peer.addDisconnectedEventListener(new PeerDisconnectedEventListener() {

            @Override
            public void onPeerDisconnected(Peer p, int peerCount) {
                synchronized (doneConnecting) {
                    if (!doneConnecting.get())
                        thisThread.interrupt();
                }
            }
        });
        if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER)
            channels.openConnection(new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000), peer);
        else if (clientType == ClientType.NIO_CLIENT)
            new NioClient(new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000), peer, 100);
        else if (clientType == ClientType.BLOCKING_CLIENT)
            new BlockingClient(new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000), peer, 100, SocketFactory.getDefault(), null);
        else
            throw new RuntimeException();
        // Claim we are connected to a different IP that what we really are, so tx confidence broadcastBy sets work
        InboundMessageQueuer writeTarget = newPeerWriteTargetQueue.take();
        writeTarget.peer = peer;
        // Complete handshake with the peer - send/receive version(ack)s, receive bloom filter
        checkState(!peer.getVersionHandshakeFuture().isDone());
        writeTarget.sendMessage(versionMessage);
        writeTarget.sendMessage(new VersionAck());
        try {
            checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
            checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
            peer.getVersionHandshakeFuture().get();
            synchronized (doneConnecting) {
                doneConnecting.set(true);
            }
            // Clear interrupted bit in case it was set before we got into the CS
            Thread.interrupted();
        } catch (InterruptedException e) {
        // We were disconnected before we got back version/verack
        }
        return writeTarget;
    }

    protected void closePeer(Peer peer) throws Exception {
        peer.close();
    }

    protected void inbound(InboundMessageQueuer peerChannel, Message message) {
        peerChannel.sendMessage(message);
    }

    private void outboundPingAndWait(final InboundMessageQueuer p, long nonce) throws Exception {
        // Send a ping and wait for it to get to the other side
        SettableFuture<Void> pingReceivedFuture = SettableFuture.create();
        p.mapPingFutures.put(nonce, pingReceivedFuture);
        p.peer.sendMessage(new Ping(nonce));
        pingReceivedFuture.get();
        p.mapPingFutures.remove(nonce);
    }

    private void inboundPongAndWait(final InboundMessageQueuer p, final long nonce) throws Exception {
        // Receive a ping (that the Peer doesn't see) and wait for it to get through the socket
        final SettableFuture<Void> pongReceivedFuture = SettableFuture.create();
        PreMessageReceivedEventListener listener = new PreMessageReceivedEventListener() {

            @Override
            public Message onPreMessageReceived(Peer p, Message m) {
                if (m instanceof Pong && ((Pong) m).getNonce() == nonce) {
                    pongReceivedFuture.set(null);
                    return null;
                }
                return m;
            }
        };
        p.peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD, listener);
        inbound(p, new Pong(nonce));
        pongReceivedFuture.get();
        p.peer.removePreMessageReceivedEventListener(listener);
    }

    protected void pingAndWait(final InboundMessageQueuer p) throws Exception {
        final long nonce = (long) (Math.random() * Long.MAX_VALUE);
        // Start with an inbound Pong as pingAndWait often happens immediately after an inbound() call, and then wants
        // to wait on an outbound message, so we do it in the same order or we see race conditions
        inboundPongAndWait(p, nonce);
        outboundPingAndWait(p, nonce);
    }

    protected Message outbound(InboundMessageQueuer p1) throws Exception {
        pingAndWait(p1);
        return p1.nextMessage();
    }

    protected Message waitForOutbound(InboundMessageQueuer ch) throws InterruptedException {
        return ch.nextMessageBlocking();
    }

    protected Peer peerOf(InboundMessageQueuer ch) {
        return ch.peer;
    }
}

19 Source : ParseByteCacheTest.java
with GNU General Public License v3.0
from bcmapp

public clreplaced ParseByteCacheTest {

    private static final int BLOCK_HEIGHT_GENESIS = 0;

    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 BlockStore blockStore;

    private byte[] b1Bytes;

    private byte[] b1BytesWithHeader;

    private byte[] tx1Bytes;

    private byte[] tx1BytesWithHeader;

    private byte[] tx2Bytes;

    private byte[] tx2BytesWithHeader;

    private static final NetworkParameters UNITTEST = UnitTestParams.get();

    private static final NetworkParameters MAINNET = MainNetParams.get();

    private void resetBlockStore() {
        blockStore = new MemoryBlockStore(UNITTEST);
    }

    @Before
    public void setUp() throws Exception {
        Context context = new Context(UNITTEST);
        Wallet wallet = new Wallet(context);
        wallet.freshReceiveKey();
        resetBlockStore();
        Transaction tx1 = createFakeTx(UNITTEST, valueOf(2, 0), LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey()));
        // add a second input so can test granularity of byte cache.
        Transaction prevTx = new Transaction(UNITTEST);
        TransactionOutput prevOut = new TransactionOutput(UNITTEST, prevTx, COIN, LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey()));
        prevTx.addOutput(prevOut);
        // Connect it.
        tx1.addInput(prevOut);
        Transaction tx2 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, new ECKey()));
        Block b1 = createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS, tx1, tx2).block;
        MessageSerializer bs = UNITTEST.getDefaultSerializer();
        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 = { 1, 1, 1, 2, 3, 4, 5, 6, 7 };
        byte[] b2 = { 1, 2, 3 };
        replacedertTrue(arrayContains(b1, b2));
        replacedertTrue(arrayContains(txMessage, txMessagePart));
        replacedertTrue(arrayContains(tx1BytesWithHeader, tx1Bytes));
        replacedertTrue(arrayContains(tx2BytesWithHeader, tx2Bytes));
        replacedertTrue(arrayContains(b1BytesWithHeader, b1Bytes));
        replacedertTrue(arrayContains(b1BytesWithHeader, tx1Bytes));
        replacedertTrue(arrayContains(b1BytesWithHeader, tx2Bytes));
        replacedertFalse(arrayContains(tx1BytesWithHeader, b1Bytes));
    }

    @Test
    public void testTransactionsRetain() throws Exception {
        testTransaction(MAINNET, txMessage, false, true);
        testTransaction(UNITTEST, tx1BytesWithHeader, false, true);
        testTransaction(UNITTEST, tx2BytesWithHeader, false, true);
    }

    @Test
    public void testTransactionsNoRetain() throws Exception {
        testTransaction(MAINNET, txMessage, false, false);
        testTransaction(UNITTEST, tx1BytesWithHeader, false, false);
        testTransaction(UNITTEST, tx2BytesWithHeader, false, false);
    }

    @Test
    public void testBlockAll() throws Exception {
        testBlock(b1BytesWithHeader, false, false);
        testBlock(b1BytesWithHeader, false, true);
    }

    public void testBlock(byte[] blockBytes, boolean isChild, boolean retain) throws Exception {
        // reference serializer to produce comparison serialization output after changes to
        // message structure.
        MessageSerializer bsRef = UNITTEST.getSerializer(false);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        BitcoinSerializer bs = UNITTEST.getSerializer(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);
        replacedertTrue(Arrays.equals(bos.toByteArray(), blockBytes));
        // check retain status survive both before and after a serialization
        replacedertEquals(retain, b1.isHeaderBytesValid());
        replacedertEquals(retain, b1.isTransactionBytesValid());
        serDeser(bs, b1, blockBytes, null, null);
        replacedertEquals(retain, b1.isHeaderBytesValid());
        replacedertEquals(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();
        if (b1.getTransactions().size() > 0) {
            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.
            replacedertEquals(retain, 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();
        // 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();
        b1.getTransactions();
        if (b1.getTransactions().size() > 0) {
            Transaction tx1 = b1.getTransactions().get(0);
            replacedertEquals(retain, 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);
        replacedertFalse(b1.isHeaderBytesValid());
        replacedertEquals(retain, 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);
            replacedertEquals(retain, 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));
                replacedertFalse(tx1.isCached());
                replacedertFalse(b1.isTransactionBytesValid());
                // confirm sibling cache status was unaffected
                if (tx1.getInputs().size() > 1) {
                    replacedertEquals(retain, tx1.getInputs().get(1).isCached());
                }
                // 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.
                replacedertFalse(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.
                replacedertFalse(b1.isTransactionBytesValid());
                // b2 should have it's cache invalidated because it has changed.
                replacedertFalse(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 retain) throws Exception {
        // reference serializer to produce comparison serialization output after changes to
        // message structure.
        MessageSerializer bsRef = params.getSerializer(false);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        BitcoinSerializer bs = params.getSerializer(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);
        replacedertTrue(Arrays.equals(bos.toByteArray(), txBytes));
        // check and retain status survive both before and after a serialization
        replacedertEquals(retain, t1.isCached());
        serDeser(bs, t1, txBytes, null, null);
        replacedertEquals(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();
        if (t1.getInputs().size() > 0) {
            TransactionInput tin = t1.getInputs().get(0);
            replacedertEquals(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));
            replacedertFalse(t1.isCached());
            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(MessageSerializer 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));
        replacedertEquals(message, m2);
        bos.reset();
        bs.serialize(m2, bos);
        byte[] b2 = bos.toByteArray();
        replacedertTrue(Arrays.equals(b1, b2));
        if (sourceBytes != null) {
            replacedertTrue(arrayContains(sourceBytes, b1));
            replacedertTrue(arrayContains(sourceBytes, b2));
        }
        if (containedBytes != null) {
            replacedertTrue(arrayContains(b1, containedBytes));
        }
        if (containingBytes != null) {
            replacedertTrue(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;
    }
}

19 Source : BlockChainTest.java
with GNU General Public License v3.0
from bcmapp

// Handling of chain splits/reorgs are in ChainSplitTests.
public clreplaced BlockChainTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    private BlockChain testNetChain;

    private Wallet wallet;

    private BlockChain chain;

    private BlockStore blockStore;

    private Address coinbaseTo;

    private final StoredBlock[] block = new StoredBlock[1];

    private Transaction coinbaseTransaction;

    private static clreplaced TweakableTestNet3Params extends TestNet3Params {

        public void setMaxTarget(BigInteger limit) {
            maxTarget = limit;
        }
    }

    private static final TweakableTestNet3Params TESTNET = new TweakableTestNet3Params();

    private static final NetworkParameters UNITTEST = UnitTestParams.get();

    private static final NetworkParameters MAINNET = MainNetParams.get();

    private void resetBlockStore() {
        blockStore = new MemoryBlockStore(UNITTEST);
    }

    @Before
    public void setUp() throws Exception {
        BriefLogFormatter.initVerbose();
        Context.propagate(new Context(TESTNET, 100, Coin.ZERO, false));
        testNetChain = new BlockChain(TESTNET, new Wallet(TESTNET), new MemoryBlockStore(TESTNET));
        Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
        wallet = new Wallet(UNITTEST) {

            @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 (isTransactionRelevant(tx) && tx.isCoinBase()) {
                    BlockChainTest.this.coinbaseTransaction = tx;
                }
            }
        };
        wallet.freshReceiveKey();
        resetBlockStore();
        chain = new BlockChain(UNITTEST, wallet, blockStore);
        coinbaseTo = LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey());
    }

    @Test
    public void testBasicChaining() throws Exception {
        // Check that we can plug a few blocks together and the futures work.
        ListenableFuture<StoredBlock> future = testNetChain.getHeightFuture(2);
        // Block 1 from the testnet.
        Block b1 = getBlock1();
        replacedertTrue(testNetChain.add(b1));
        replacedertFalse(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.
        replacedertTrue(testNetChain.add(b2));
        replacedertTrue(future.isDone());
        replacedertEquals(2, future.get().getHeight());
    }

    @Test
    public void receiveCoins() throws Exception {
        int height = 1;
        // Quick check that we can actually receive coins.
        Transaction tx1 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey()));
        Block b1 = createFakeBlock(blockStore, height, tx1).block;
        chain.add(b1);
        replacedertTrue(wallet.getBalance().signum() > 0);
    }

    @Test
    public void unconnectedBlocks() throws Exception {
        Block b1 = UNITTEST.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        Block b3 = b2.createNextBlock(coinbaseTo);
        // Connected.
        replacedertTrue(chain.add(b1));
        // Unconnected but stored. The head of the chain is still b1.
        replacedertFalse(chain.add(b3));
        replacedertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
        // Add in the middle block.
        replacedertTrue(chain.add(b2));
        replacedertEquals(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 = UNITTEST.getGenesisBlock();
        Utils.setMockClock(System.currentTimeMillis() / 1000);
        for (int height = 0; height < UNITTEST.getInterval() - 1; height++) {
            Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
            replacedertTrue(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, 1, Utils.currentTimeSeconds(), UNITTEST.getInterval()));
            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, 1, Utils.currentTimeSeconds(), UNITTEST.getInterval() + 1);
        b.setDifficultyTarget(0x201fFFFFL);
        b.solve();
        replacedertTrue(chain.add(b));
    // Successfully traversed a difficulty transition period.
    }

    @Test
    public void badDifficulty() throws Exception {
        replacedertTrue(testNetChain.add(getBlock1()));
        Block b2 = getBlock2();
        replacedertTrue(testNetChain.add(b2));
        Block bad = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
        // Merkle root can be anything here, doesn't matter.
        bad.setMerkleRoot(Sha256Hash.wrap("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 preplaced, 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) {
            replacedertTrue(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) {
            replacedertTrue(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 that version 2 blocks are rejected once version 3 blocks are a super
     * majority.
     */
    @Test
    public void badBip66Version() throws Exception {
        testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP34, Block.BLOCK_VERSION_BIP66);
    }

    /**
     * Test that version 3 blocks are rejected once version 4 blocks are a super
     * majority.
     */
    @Test
    public void badBip65Version() throws Exception {
        testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP66, Block.BLOCK_VERSION_BIP65);
    }

    private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
        final BlockStore versionBlockStore = new MemoryBlockStore(UNITTEST);
        final BlockChain versionChain = new BlockChain(UNITTEST, versionBlockStore);
        // Build a historical chain of version 3 blocks
        long timeSeconds = 1231006505;
        int height = 0;
        FakeTxBuilder.BlockPair chainHead = null;
        // Put in just enough v2 blocks to be a minority
        for (height = 0; height < (UNITTEST.getMajorityWindow() - UNITTEST.getMajorityRejectBlockOutdated()); height++) {
            chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
            versionChain.add(chainHead.block);
            timeSeconds += 60;
        }
        // Fill the rest of the window with v3 blocks
        for (; height < UNITTEST.getMajorityWindow(); height++) {
            chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
            versionChain.add(chainHead.block);
            timeSeconds += 60;
        }
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
        // Trying to add a new v2 block should result in rejection
        thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
        try {
            versionChain.add(chainHead.block);
        } catch (final VerificationException ex) {
            throw (Exception) ex.getCause();
        }
    }

    @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 = UNITTEST.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        Block b3 = b2.createNextBlock(coinbaseTo);
        replacedertTrue(chain.add(b1));
        replacedertEquals(b1, block[0].getHeader());
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2, block[0].getHeader());
        replacedertTrue(chain.add(b3));
        replacedertEquals(b3, block[0].getHeader());
        replacedertEquals(b3, chain.getChainHead().getHeader());
        replacedertTrue(chain.add(b2));
        replacedertEquals(b3, chain.getChainHead().getHeader());
        // Wallet was NOT called with the new block because the duplicate add was spotted.
        replacedertEquals(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 = LegacyAddress.fromKey(UNITTEST, new ECKey());
        Block b1 = UNITTEST.getGenesisBlock().createNextBlock(somebodyElse);
        ECKey key = wallet.freshReceiveKey();
        Address addr = LegacyAddress.fromKey(UNITTEST, key);
        // Create a tx that gives us some coins, and another that spends it to someone else in the same block.
        Transaction t1 = FakeTxBuilder.createFakeTx(UNITTEST, COIN, addr);
        Transaction t2 = new Transaction(UNITTEST);
        t2.addInput(t1.getOutputs().get(0));
        t2.addOutput(valueOf(2, 0), somebodyElse);
        b1.addTransaction(t1);
        b1.addTransaction(t2);
        b1.solve();
        chain.add(b1);
        replacedertEquals(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(UNITTEST);
        ECKey receiveKey = wallet2.freshReceiveKey();
        int height = 1;
        chain.addWallet(wallet2);
        Address addressToSendTo = LegacyAddress.fromKey(UNITTEST, receiveKey);
        // Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
        Block b1 = UNITTEST.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
        chain.add(b1);
        // Check a transaction has been received.
        replacedertNotNull(coinbaseTransaction);
        // The coinbase tx is not yet available to spend.
        replacedertEquals(Coin.ZERO, wallet.getBalance());
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
        replacedertTrue(!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 < UNITTEST.getSpendableCoinbaseDepth() - 2; i++) {
            // Non relevant tx - just for fake block creation.
            Transaction tx2 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, new ECKey()));
            Block b2 = createFakeBlock(blockStore, height++, tx2).block;
            chain.add(b2);
            // Wallet still does not have the coinbase transaction available for spend.
            replacedertEquals(Coin.ZERO, wallet.getBalance());
            replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
            // The coinbase transaction is still not mature.
            replacedertTrue(!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(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, new ECKey()));
        Block b3 = createFakeBlock(blockStore, height++, tx3).block;
        chain.add(b3);
        // Wallet now has the coinbase transaction available for spend.
        replacedertEquals(wallet.getBalance(), FIFTY_COINS);
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
        replacedertTrue(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));
        replacedertNotNull(coinbaseSend2);
        // Commit the coinbaseSpend to the first wallet and check the balances decrement.
        wallet.commitTx(coinbaseSend2);
        replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN);
        // Available balance is zero as change has not been received from a block yet.
        replacedertEquals(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, height++, coinbaseSend2).block;
        chain.add(b4);
        replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);
        // Check the balances in the second wallet.
        replacedertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0));
        replacedertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0));
    }

    // Some blocks from the test net.
    private static Block getBlock2() throws Exception {
        Block b2 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
        b2.setMerkleRoot(Sha256Hash.wrap("20222eb90f5895556926c112bb5aa0df4ab5abc3107e21a6950aec3b2e3541e2"));
        b2.setNonce(875942400L);
        b2.setTime(1296688946L);
        b2.setDifficultyTarget(0x1d00ffff);
        b2.setPrevBlockHash(Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"));
        replacedertEquals("000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820", b2.getHashreplacedtring());
        b2.verifyHeader();
        return b2;
    }

    private static Block getBlock1() throws Exception {
        Block b1 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
        b1.setMerkleRoot(Sha256Hash.wrap("f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"));
        b1.setNonce(1924588547);
        b1.setTime(1296688928);
        b1.setDifficultyTarget(0x1d00ffff);
        b1.setPrevBlockHash(Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
        replacedertEquals("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206", b1.getHashreplacedtring());
        b1.verifyHeader();
        return b1;
    }

    @Test
    public void estimatedBlockTime() throws Exception {
        BlockChain prod = new BlockChain(new Context(MAINNET), new MemoryBlockStore(MAINNET));
        Date d = prod.estimateBlockTime(200000);
        // The actual date of block 200,000 was 2012-09-22 10:47:00
        replacedertEquals(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;
        // Exactly
        replacedertTrue(0 == chain.getFalsePositiveRate());
        chain.trackFalsePositives(55);
        replacedertEquals(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%
        replacedertEquals(0.1, chain.getFalsePositiveRate(), 0.01);
        // Check that we get repeatable results after a reset
        chain.resetFalsePositiveEstimate();
        // Exactly
        replacedertTrue(0 == chain.getFalsePositiveRate());
        chain.trackFalsePositives(55);
        replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
        chain.trackFilteredTransactions(550);
        replacedertEquals(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 = UNITTEST.getGenesisBlock().createNextBlock(coinbaseTo);
        Block b2 = b1.createNextBlock(coinbaseTo);
        // Add block 1, no frills.
        replacedertTrue(chain.add(b1));
        replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(1, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Add block 2 while wallet is disconnected, to simulate crash.
        chain.removeWallet(wallet);
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(2, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Add wallet back. This will detect the height mismatch and repair the damage done.
        chain.addWallet(wallet);
        replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(1, chain.getBestChainHeight());
        replacedertEquals(1, wallet.getLastBlockSeenHeight());
        // Now add block 2 correctly.
        replacedertTrue(chain.add(b2));
        replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
        replacedertEquals(2, chain.getBestChainHeight());
        replacedertEquals(2, wallet.getLastBlockSeenHeight());
    }
}

19 Source : VersionTally.java
with GNU General Public License v3.0
from bcmapp

/**
 * Initialize the version tally from the block store. Note this does not
 * search backwards past the start of the block store, so if starting from
 * a checkpoint this may not fill the window.
 *
 * @param blockStore block store to load blocks from.
 * @param chainHead  current chain tip.
 */
public void initialize(final BlockStore blockStore, final StoredBlock chainHead) throws BlockStoreException {
    StoredBlock versionBlock = chainHead;
    final Stack<Long> versions = new Stack<>();
    // We don't know how many blocks back we can go, so load what we can first
    versions.push(versionBlock.getHeader().getVersion());
    for (int headOffset = 0; headOffset < versionWindow.length; headOffset++) {
        versionBlock = versionBlock.getPrev(blockStore);
        if (null == versionBlock) {
            break;
        }
        versions.push(versionBlock.getHeader().getVersion());
    }
    // Replay the versions into the tally
    while (!versions.isEmpty()) {
        add(versions.pop());
    }
}

19 Source : BlockChain.java
with GNU General Public License v3.0
from bcmapp

// TODO: Rename this clreplaced to SPVBlockChain at some point.
/**
 * A BlockChain implements the <i>simplified payment verification</i> mode of the Bitcoin protocol. It is the right
 * choice to use for programs that have limited resources as it won't verify transactions signatures or attempt to store
 * all of the block chain. Really, this clreplaced should be called SPVBlockChain but for backwards compatibility it is not.
 */
public clreplaced BlockChain extends AbstractBlockChain {

    /**
     * Keeps a map of block hashes to StoredBlocks.
     */
    protected final BlockStore blockStore;

    /**
     * <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
     * one from scratch, or you can deserialize a saved wallet from disk using
     * {@link Wallet#loadFromFile(File, WalletExtension...)}</p>
     * <p>
     * <p>For the store, you should use {@link SPVBlockStore} or you could also try a
     * {@link MemoryBlockStore} if you want to hold all headers in RAM and don't care about
     * disk serialization (this is rare).</p>
     */
    public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
        addWallet(wallet);
    }

    /**
     * See {@link #BlockChain(Context, Wallet, BlockStore)}}
     */
    public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallet, blockStore);
    }

    /**
     * Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
     * and receiving coins but rather, just want to explore the network data structures.
     */
    public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
        this(context, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * See {@link #BlockChain(Context, BlockStore)}
     */
    public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
        this(params, new ArrayList<Wallet>(), blockStore);
    }

    /**
     * Constructs a BlockChain connected to the given list of listeners and a store.
     */
    public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        super(params, wallets, blockStore);
        this.blockStore = blockStore;
    }

    /**
     * See {@link #BlockChain(Context, List, BlockStore)}
     */
    public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
        this(Context.getOrCreate(params), wallets, blockStore);
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
        StoredBlock newBlock = storedPrev.build(blockHeader);
        blockStore.put(newBlock);
        return newBlock;
    }

    @Override
    protected void rollbackBlockStore(int height) throws BlockStoreException {
        lock.lock();
        try {
            int currentHeight = getBestChainHeight();
            checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
            if (height == currentHeight)
                // nothing to do
                return;
            // Look for the block we want to be the new chain head
            StoredBlock newChainHead = blockStore.getChainHead();
            while (newChainHead.getHeight() > height) {
                newChainHead = newChainHead.getPrev(blockStore);
                if (newChainHead == null)
                    throw new BlockStoreException("Unreachable height");
            }
            // Modify store directly
            blockStore.put(newChainHead);
            this.setChainHead(newChainHead);
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean shouldVerifyTransactions() {
        return false;
    }

    @Override
    protected TransactionOutputChanges connectTransactions(int height, Block block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void disconnectTransactions(StoredBlock block) {
        // Don't have to do anything as this is only called if(shouldVerifyTransactions())
        throw new UnsupportedOperationException();
    }

    @Override
    protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
        blockStore.setChainHead(chainHead);
    }

    @Override
    protected void notSettingChainHead() throws BlockStoreException {
    // We don't use DB transactions here, so we don't need to do anything
    }

    @Override
    protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
        return blockStore.get(hash);
    }

    @Override
    public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
        boolean success = super.add(block);
        if (success) {
            trackFilteredTransactions(block.getTransactionCount());
        }
        return success;
    }
}

19 Source : CoinType.java
with Apache License 2.0
from AnyBitIO

@Override
public void checkDifficultyTransitions(StoredBlock storedBlock, Block block, BlockStore blockStore) throws VerificationException, BlockStoreException {
}

18 Source : TestWithNetworkConnections.java
with Apache License 2.0
from Samourai-Wallet

public void setUp(BlockStore blockStore) throws Exception {
    BriefLogFormatter.init();
    Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
    this.blockStore = blockStore;
    // Allow subclreplacedes to override the wallet object with their own.
    if (wallet == null) {
        wallet = new Wallet(PARAMS);
        key = wallet.freshReceiveKey();
        address = key.toAddress(PARAMS);
    }
    blockChain = new BlockChain(PARAMS, wallet, blockStore);
    startPeerServers();
    if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
        channels.startAsync();
        channels.awaitRunning();
    }
    socketAddress = new InetSocketAddress("127.0.0.1", 1111);
}

18 Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet

/**
 * Emulates receiving a valid block
 */
public static BlockPair createFakeBlock(BlockStore blockStore, StoredBlock previousStoredBlock, long version, long timeSeconds, int height, Transaction... transactions) {
    try {
        Block previousBlock = previousStoredBlock.getHeader();
        Address to = new ECKey().toAddress(previousBlock.getParams());
        Block b = previousBlock.createNextBlock(to, version, timeSeconds, height);
        // Coinbase tx was already added.
        for (Transaction tx : transactions) {
            tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
            b.addTransaction(tx);
        }
        b.solve();
        BlockPair pair = new BlockPair();
        pair.block = b;
        pair.storedBlock = previousStoredBlock.build(b);
        blockStore.put(pair.storedBlock);
        blockStore.setChainHead(pair.storedBlock);
        return pair;
    } catch (VerificationException e) {
        // Cannot happen.
        throw new RuntimeException(e);
    } catch (BlockStoreException e) {
        // Cannot happen.
        throw new RuntimeException(e);
    }
}

18 Source : RefreshWallet.java
with GNU General Public License v3.0
from Blockstream

public static void main(String[] args) throws Exception {
    File file = new File(args[0]);
    Wallet wallet = Wallet.loadFromFile(file);
    System.out.println(wallet.toString());
    // Set up the components and link them together.
    final NetworkParameters params = TestNet3Params.get();
    BlockStore blockStore = new MemoryBlockStore(params);
    BlockChain chain = new BlockChain(params, wallet, blockStore);
    final PeerGroup peerGroup = new PeerGroup(params, chain);
    peerGroup.startAsync();
    wallet.addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {

        @Override
        public synchronized void onCoinsReceived(Wallet w, Transaction tx, Coin prevBalance, Coin newBalance) {
            System.out.println("\nReceived tx " + tx.getHashreplacedtring());
            System.out.println(tx.toString());
        }
    });
    // Now download and process the block chain.
    peerGroup.downloadBlockChain();
    peerGroup.stopAsync();
    wallet.saveToFile(file);
    System.out.println("\nDone!\n");
    System.out.println(wallet.toString());
}

18 Source : AbstractBitcoinNetParams.java
with Apache License 2.0
from bitcoincash-wallet

/**
 * Compute the next required proof of work using a weighted average of the
 * estimated hashrate per block.
 *
 * Using a weighted average ensure that the timestamp parameter cancels out in
 * most of the calculation - except for the timestamp of the first and last
 * block. Because timestamps are the least trustworthy information we have as
 * input, this ensures the algorithm is more resistant to malicious inputs.
 */
protected void checkNextCashWorkRequired(StoredBlock pindexPrev, Block pblock, BlockStore blockStore) {
    // This cannot handle the genesis block and early blocks in general.
    // replacedert(pindexPrev);
    // Special difficulty rule for testnet:
    // If the new block's timestamp is more than 2* 10 minutes then allow
    // mining of a min-difficulty block.
    /*if (params.fPowAllowMinDifficultyBlocks &&
                (pblock->GetBlockTime() >
                        pindexPrev->GetBlockTime() + 2 * params.nPowTargetSpacing)) {
            return UintToArith256(params.powLimit).GetCompact();
        }*/
    // Compute the difficulty based on the full adjustement interval.
    int nHeight = pindexPrev.getHeight();
    Preconditions.checkState(nHeight >= this.interval);
    // Get the last suitable block of the difficulty interval.
    try {
        StoredBlock pindexLast = GetSuitableBlock(pindexPrev, blockStore);
        // replacedert (pindexLast);
        // Get the first suitable block of the difficulty interval.
        int nHeightFirst = nHeight - 144;
        StoredBlock pindexFirst = pindexPrev;
        for (int i = 144; i > 0; --i) {
            pindexFirst = pindexFirst.getPrev(blockStore);
            if (pindexFirst == null)
                return;
        }
        pindexFirst = GetSuitableBlock(pindexFirst, blockStore);
        // replacedert (pindexFirst);
        // Compute the target based on time and work done during the interval.
        BigInteger nextTarget = ComputeTarget(pindexFirst, pindexLast);
        verifyDifficulty(nextTarget, pblock);
    } catch (BlockStoreException x) {
        // this means we don't have enough blocks, yet.  let it go until we do.
        return;
    }
}

18 Source : BitcoinJConfig.java
with MIT License
from Bitcoin-com

@Bean
public BlockchainExtended blockChain(BlockStore blockStore, Context context, BitcoinJListener bitcoinJListener) throws BlockStoreException {
    this.blockChain = new BlockchainExtended(context, blockStore, bitcoinJListener);
    return blockChain;
}

18 Source : TestWithNetworkConnections.java
with GNU General Public License v3.0
from bcmapp

public void setUp(BlockStore blockStore) throws Exception {
    BriefLogFormatter.init();
    Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
    this.blockStore = blockStore;
    // Allow subclreplacedes to override the wallet object with their own.
    if (wallet == null) {
        wallet = new Wallet(UNITTEST);
        key = wallet.freshReceiveKey();
        address = LegacyAddress.fromKey(UNITTEST, key);
    }
    blockChain = new BlockChain(UNITTEST, wallet, blockStore);
    startPeerServers();
    if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
        channels.startAsync();
        channels.awaitRunning();
    }
    socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 1111);
}

17 Source : BlockChainTest.java
with Apache License 2.0
from Samourai-Wallet

private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
    final BlockStore versionBlockStore = new MemoryBlockStore(PARAMS);
    final BlockChain versionChain = new BlockChain(PARAMS, versionBlockStore);
    // Build a historical chain of version 3 blocks
    long timeSeconds = 1231006505;
    int height = 0;
    FakeTxBuilder.BlockPair chainHead = null;
    // Put in just enough v2 blocks to be a minority
    for (height = 0; height < (PARAMS.getMajorityWindow() - PARAMS.getMajorityRejectBlockOutdated()); height++) {
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
        versionChain.add(chainHead.block);
        timeSeconds += 60;
    }
    // Fill the rest of the window with v3 blocks
    for (; height < PARAMS.getMajorityWindow(); height++) {
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
        versionChain.add(chainHead.block);
        timeSeconds += 60;
    }
    chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
    // Trying to add a new v2 block should result in rejection
    thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
    try {
        versionChain.add(chainHead.block);
    } catch (final VerificationException ex) {
        throw (Exception) ex.getCause();
    }
}

17 Source : AbstractBitcoinNetParams.java
with Apache License 2.0
from bitcoincash-wallet

@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore, AbstractBlockChain blockChain) throws VerificationException, BlockStoreException {
    Block prev = storedPrev.getHeader();
    if (storedPrev.getHeight() + 1 >= daaHeight) {
        checkNextCashWorkRequired(storedPrev, nextBlock, blockStore);
        return;
    }
    // Is this supposed to be a difficulty transition point
    if (!isDifficultyTransitionPoint(storedPrev)) {
        if (storedPrev.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) {
            // No ... so check the difficulty didn't actually change.
            if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget())
                throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Long.toHexString(prev.getDifficultyTarget()));
            return;
        }
        // If producing the last 6 block took less than 12h, we keep the same
        // difficulty.
        StoredBlock cursor = blockStore.get(prev.getHash());
        for (int i = 0; i < 6; i++) {
            if (cursor == null) {
                return;
            // This should never happen. If it does, it means we are following an incorrect or busted chain.
            // throw new VerificationException(
            // "We did not find a way back to the genesis block.");
            }
            cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
        }
        long mpt6blocks = 0;
        try {
            mpt6blocks = blockChain.getMedianTimestampOfRecentBlocks(storedPrev, blockStore) - blockChain.getMedianTimestampOfRecentBlocks(cursor, blockStore);
        } catch (NullPointerException x) {
            return;
        }
        // If producing the last 6 block took more than 12h, increase the difficulty
        // target by 1/4 (which reduces the difficulty by 20%). This ensure the
        // chain do not get stuck in case we lose hashrate abruptly.
        if (mpt6blocks >= 12 * 3600) {
            BigInteger nPow = storedPrev.getHeader().getDifficultyTargetAsInteger();
            nPow = nPow.add(nPow.shiftRight(2));
            if (nPow.compareTo(getMaxTarget()) > 0)
                nPow = getMaxTarget();
            if (nextBlock.getDifficultyTarget() != Utils.encodeCompactBits(nPow))
                throw new VerificationException("Unexpected change in difficulty [6 blocks >12 hours] at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Utils.encodeCompactBits(nPow));
            return;
        }
        // No ... so check the difficulty didn't actually change.
        if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget())
            throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Long.toHexString(prev.getDifficultyTarget()));
        return;
    }
    // We need to find a block far back in the chain. It's OK that this is expensive because it only occurs every
    // two weeks after the initial block chain download.
    final Stopwatch watch = Stopwatch.createStarted();
    StoredBlock cursor = blockStore.get(prev.getHash());
    for (int i = 0; i < this.getInterval() - 1; i++) {
        if (cursor == null) {
            // This should never happen. If it does, it means we are following an incorrect or busted chain.
            throw new VerificationException("Difficulty transition point but we did not find a way back to the genesis block.");
        }
        cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
    }
    watch.stop();
    if (watch.elapsed(TimeUnit.MILLISECONDS) > 50)
        log.info("Difficulty transition traversal took {}", watch);
    Block blockIntervalAgo = cursor.getHeader();
    int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
    // Limit the adjustment step.
    final int targetTimespan = this.getTargetTimespan();
    if (timespan < targetTimespan / 4)
        timespan = targetTimespan / 4;
    if (timespan > targetTimespan * 4)
        timespan = targetTimespan * 4;
    BigInteger newTarget = Utils.decodeCompactBits(prev.getDifficultyTarget());
    newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
    newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan));
    verifyDifficulty(newTarget, nextBlock);
/*if (newTarget.compareTo(this.getMaxTarget()) > 0) {
            log.info("Difficulty hit proof of work limit: {}", newTarget.toString(16));
            newTarget = this.getMaxTarget();
        }


        int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3;
        long receivedTargetCompact = nextBlock.getDifficultyTarget();

        // The calculated difficulty is to a higher precision than received, so reduce here.
        BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
        newTarget = newTarget.and(mask);
        long newTargetCompact = Utils.encodeCompactBits(newTarget);

        if (newTargetCompact != receivedTargetCompact)
            throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
                    Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact));
                    */
}

17 Source : BlockChainTest.java
with GNU General Public License v3.0
from bcmapp

private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
    final BlockStore versionBlockStore = new MemoryBlockStore(UNITTEST);
    final BlockChain versionChain = new BlockChain(UNITTEST, versionBlockStore);
    // Build a historical chain of version 3 blocks
    long timeSeconds = 1231006505;
    int height = 0;
    FakeTxBuilder.BlockPair chainHead = null;
    // Put in just enough v2 blocks to be a minority
    for (height = 0; height < (UNITTEST.getMajorityWindow() - UNITTEST.getMajorityRejectBlockOutdated()); height++) {
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
        versionChain.add(chainHead.block);
        timeSeconds += 60;
    }
    // Fill the rest of the window with v3 blocks
    for (; height < UNITTEST.getMajorityWindow(); height++) {
        chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
        versionChain.add(chainHead.block);
        timeSeconds += 60;
    }
    chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
    // Trying to add a new v2 block should result in rejection
    thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
    try {
        versionChain.add(chainHead.block);
    } catch (final VerificationException ex) {
        throw (Exception) ex.getCause();
    }
}

15 Source : CheckpointManager.java
with Apache License 2.0
from Samourai-Wallet

/**
 * <p>Convenience method that creates a CheckpointManager, loads the given data, gets the checkpoint for the given
 * time, then inserts it into the store and sets that to be the chain head. Useful when you have just created
 * a new store from scratch and want to use configure it all in one go.</p>
 *
 * <p>Note that time is adjusted backwards by a week to account for possible clock drift in the block headers.</p>
 */
public static void checkpoint(NetworkParameters params, InputStream checkpoints, BlockStore store, long time) throws IOException, BlockStoreException {
    checkNotNull(params);
    checkNotNull(store);
    checkArgument(!(store instanceof FullPrunedBlockStore), "You cannot use checkpointing with a full store.");
    time -= 86400 * 7;
    checkArgument(time > 0);
    log.info("Attempting to initialize a new block store with a checkpoint for time {} ({})", time, Utils.dateTimeFormat(time * 1000));
    BufferedInputStream stream = new BufferedInputStream(checkpoints);
    CheckpointManager manager = new CheckpointManager(params, stream);
    StoredBlock checkpoint = manager.getCheckpointBefore(time);
    store.put(checkpoint);
    store.setChainHead(checkpoint);
}

15 Source : CheckpointManager.java
with GNU General Public License v3.0
from bcmapp

/**
 * <p>Convenience method that creates a CheckpointManager, loads the given data, gets the checkpoint for the given
 * time, then inserts it into the store and sets that to be the chain head. Useful when you have just created
 * a new store from scratch and want to use configure it all in one go.</p>
 * <p>
 * <p>Note that time is adjusted backwards by a week to account for possible clock drift in the block headers.</p>
 */
public static void checkpoint(NetworkParameters params, InputStream checkpoints, BlockStore store, long time) throws IOException, BlockStoreException {
    checkNotNull(params);
    checkNotNull(store);
    checkArgument(!(store instanceof FullPrunedBlockStore), "You cannot use checkpointing with a full store.");
    time -= 86400 * 7;
    checkArgument(time > 0);
    log.info("Attempting to initialize a new block store with a checkpoint for time {} ({})", time, Utils.dateTimeFormat(time * 1000));
    BufferedInputStream stream = new BufferedInputStream(checkpoints);
    CheckpointManager manager = new CheckpointManager(params, stream);
    StoredBlock checkpoint = manager.getCheckpointBefore(time);
    store.put(checkpoint);
    store.setChainHead(checkpoint);
}

See More Examples