Stores the block chain to disk.
This implementation is designed to have constant memory usage, regardless of the size of the block chain being stored. It exploits operating system level buffering and the fact that get() requests are, in normal usage, localized in chain space.

Blocks are stored sequentially. Most blocks are fetched out of a small in-memory cache. The slowest part is traversing difficulty transition points, which requires seeking backwards over around 2000 blocks. On a Google Nexus S phone this takes a couple of seconds. On a MacBook Pro it takes around 50msec.

The store has much room for optimization. Expanding the size of the cache will likely allow us to traverse difficulty transitions without using too much memory and without hitting the disk at all, for the case of initial block chain download. Storing the hashes on disk would allow us to avoid deserialization and hashing which is expensive on Android.

Inheritance: IBlockStore
        public void TestStorage()
        {
            var temp = new FileInfo(Path.GetTempFileName());
            try
            {
                Console.WriteLine(temp.FullName);
                var @params = NetworkParameters.UnitTests();
                var to = new EcKey().ToAddress(@params);
                StoredBlock b1;
                using (var store = new BoundedOverheadBlockStore(@params, temp))
                {
                    // Check the first block in a new store is the genesis block.
                    var genesis = store.GetChainHead();
                    Assert.AreEqual(@params.GenesisBlock, genesis.Header);

                    // Build a new block.
                    b1 = genesis.Build(genesis.Header.CreateNextBlock(to).CloneAsHeader());
                    store.Put(b1);
                    store.SetChainHead(b1);
                }
                // Check we can get it back out again if we rebuild the store object.
                using (var store = new BoundedOverheadBlockStore(@params, temp))
                {
                    var b2 = store.Get(b1.Header.Hash);
                    Assert.AreEqual(b1, b2);
                    // Check the chain head was stored correctly also.
                    Assert.AreEqual(b1, store.GetChainHead());
                }
            }
            finally
            {
                temp.Delete();
            }
        }
        public static void Run(string[] args)
        {
            var testNet = args.Length > 0 && string.Equals(args[0], "testnet", StringComparison.InvariantCultureIgnoreCase);
            var @params = testNet ? NetworkParameters.TestNet() : NetworkParameters.ProdNet();
            var filePrefix = testNet ? "pingservice-testnet" : "pingservice-prodnet";

            // Try to read the wallet from storage, create a new one if not possible.
            Wallet wallet;
            var walletFile = new FileInfo(filePrefix + ".wallet");
            try
            {
                wallet = Wallet.LoadFromFile(walletFile);
            }
            catch (IOException)
            {
                wallet = new Wallet(@params);
                wallet.Keychain.Add(new EcKey());
                wallet.SaveToFile(walletFile);
            }
            // Fetch the first key in the wallet (should be the only key).
            var key = wallet.Keychain[0];

            Console.WriteLine(wallet);

            // Load the block chain, if there is one stored locally.
            Console.WriteLine("Reading block store from disk");
            using (var blockStore = new BoundedOverheadBlockStore(@params, new FileInfo(filePrefix + ".blockchain")))
            {
                // Connect to the localhost node. One minute timeout since we won't try any other peers
                Console.WriteLine("Connecting ...");
                var chain = new BlockChain(@params, wallet, blockStore);

                var peerGroup = new PeerGroup(blockStore, @params, chain);
                peerGroup.AddAddress(new PeerAddress(IPAddress.Loopback));
                peerGroup.Start();

                // We want to know when the balance changes.
                wallet.CoinsReceived +=
                    (sender, e) =>
                    {
                        // Running on a peer thread.
                        Debug.Assert(!e.NewBalance.Equals(0));
                        // It's impossible to pick one specific identity that you receive coins from in BitCoin as there
                        // could be inputs from many addresses. So instead we just pick the first and assume they were all
                        // owned by the same person.
                        var input = e.Tx.Inputs[0];
                        var from = input.FromAddress;
                        var value = e.Tx.GetValueSentToMe(wallet);
                        Console.WriteLine("Received " + Utils.BitcoinValueToFriendlyString(value) + " from " + from);
                        // Now send the coins back!
                        var sendTx = wallet.SendCoins(peerGroup, from, value);
                        Debug.Assert(sendTx != null); // We should never try to send more coins than we have!
                        Console.WriteLine("Sent coins back! Transaction hash is " + sendTx.HashAsString);
                        wallet.SaveToFile(walletFile);
                    };

                peerGroup.DownloadBlockChain();
                Console.WriteLine("Send coins to: " + key.ToAddress(@params));
                Console.WriteLine("Waiting for coins to arrive. Press Ctrl-C to quit.");
                // The PeerGroup thread keeps us alive until something kills the process.
            }
        }