Example #1
0
        public MemoryBlockchain(Block? genesisBlock = null)
        {
            this.shutdownToken = new CancellationToken();
            this.random = new Random();

            // create the key pair that block rewards will be sent to
            var keyPair = TransactionManager.CreateKeyPair();
            this._coinbasePrivateKey = keyPair.Item1;
            this._coinbasePublicKey = keyPair.Item2;

            // initialize unit test storage
            this._storageContext = new MemoryStorageContext();
            this._cacheContext = new CacheContext(this._storageContext);

            // initialize unit test rules
            this._rules = new UnitTestRules(this._cacheContext);

            // initialize blockchain calculator
            this._calculator = new BlockchainCalculator(this._rules, this._cacheContext, this.shutdownToken);

            // create and mine the genesis block
            this._genesisBlock = genesisBlock ?? MineEmptyBlock(0);

            // update genesis blockchain and add to storage
            this._rules.SetGenesisBlock(this._genesisBlock);
            this._currentBlockchain = this._rules.GenesisBlockchain;
            this._genesisChainedBlock = AddBlock(this._genesisBlock, null).Item2;
        }
Example #2
0
        public void SetGenesisBlock(Block genesisBlock)
        {
            this._genesisBlock = genesisBlock;

            this._genesisChainedBlock =
                new ChainedBlock
                (
                    blockHash: this._genesisBlock.Hash,
                    previousBlockHash: this._genesisBlock.Header.PreviousBlock,
                    height: 0,
                    totalWork: this._genesisBlock.Header.CalculateWork()
                );

            this._genesisBlockchain =
                new Data.Blockchain
                (
                    blockList: ImmutableList.Create(this._genesisChainedBlock),
                    blockListHashes: ImmutableHashSet.Create(this._genesisBlock.Hash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>() // genesis block coinbase is not included in utxo, it is unspendable
                );
        }
Example #3
0
        public Tuple<Block, ChainedBlock> MineAndAddEmptyBlock(ChainedBlock prevChainedBlock)
        {
            var block = MineEmptyBlock(prevChainedBlock.BlockHash);

            return AddBlock(block, prevChainedBlock);
        }
Example #4
0
        public Tuple<Block, ChainedBlock> MineAndAddBlock(Block block, ChainedBlock? prevChainedBlock)
        {
            var minedHeader = Miner.MineBlockHeader(block.Header, this._rules.HighestTarget);
            if (minedHeader == null)
                Assert.Fail("No block could be mined for test data header.");

            var minedBlock = block.With(Header: minedHeader);

            return AddBlock(minedBlock, prevChainedBlock);
        }
Example #5
0
 public Block CreateEmptyBlock(ChainedBlock prevChainedBlock)
 {
     return CreateEmptyBlock(prevChainedBlock.BlockHash);
 }
Example #6
0
        public IEnumerable<ChainedBlock> PreviousChainedBlocks(ChainedBlock firstBlock)
        {
            var prevChainedBlock = firstBlock;
            //TODO some kind of hard stop
            while (true)
            {
                yield return prevChainedBlock;

                var prevBlockHash = prevChainedBlock.PreviousBlockHash;
                if (prevBlockHash == 0)
                {
                    break;
                }

                prevChainedBlock = this.CacheContext.GetChainedBlock(prevBlockHash);
            }
        }
Example #7
0
        public Data.Blockchain CalculateBlockchainFromExisting(Data.Blockchain currentBlockchain, ChainedBlock targetChainedBlock, out List<MissingDataException> missingData, CancellationToken cancelToken, Action<Data.Blockchain> onProgress = null)
        {
            Debug.WriteLine("Winning chained block {0} at height {1}, total work: {2}".Format2(targetChainedBlock.BlockHash.ToHexNumberString(), targetChainedBlock.Height, targetChainedBlock.TotalWork.ToString("X")));

            // if the target block is at height 0 don't use currentBlockchain as-is, set it to be the genesis chain for the target block
            if (targetChainedBlock.Height == 0)
            {
                currentBlockchain = new Data.Blockchain
                (
                    blockList: ImmutableList.Create(targetChainedBlock),
                    blockListHashes: ImmutableHashSet.Create(targetChainedBlock.BlockHash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>()
                );
            }
            // if currentBlockchain is not present find the genesis block for the target block and use it as the current chain
            else if (currentBlockchain.IsDefault)
            {
                // find the genesis block for the target block
                var genesisBlock = targetChainedBlock;
                foreach (var prevBlock in PreviousChainedBlocks(targetChainedBlock))
                {
                    // cooperative loop
                    this.shutdownToken.ThrowIfCancellationRequested();
                    cancelToken.ThrowIfCancellationRequested();

                    genesisBlock = prevBlock;
                }

                currentBlockchain = new Data.Blockchain
                (
                    blockList: ImmutableList.Create(genesisBlock),
                    blockListHashes: ImmutableHashSet.Create(genesisBlock.BlockHash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>()
                );
            }

            missingData = new List<MissingDataException>();

            Debug.WriteLine("Searching for last common ancestor between current chainblock and winning chainblock");

            List<UInt256> newChainBlockList;
            var lastCommonAncestorChain = RollbackToLastCommonAncestor(currentBlockchain, targetChainedBlock, cancelToken, out newChainBlockList);

            Debug.WriteLine("Last common ancestor found at block {0}, height {1:#,##0}, begin processing winning blockchain".Format2(currentBlockchain.RootBlockHash.ToHexNumberString(), currentBlockchain.Height));

            // setup statistics
            var totalTxCount = 0L;
            var totalInputCount = 0L;
            var totalStopwatch = new Stopwatch();

            var currentBlockCount = 0L;
            var currentTxCount = 0L;
            var currentInputCount = 0L;
            var currentRateStopwatch = new Stopwatch();

            totalStopwatch.Start();
            currentRateStopwatch.Start();

            // with last common ancestor found and utxo rolled back to that point, calculate the new blockchain
            // use ImmutableList for BlockList during modification
            var newBlockchain = new Data.Blockchain
            (
                blockList: lastCommonAncestorChain.BlockList,
                blockListHashes: lastCommonAncestorChain.BlockListHashes,
                utxo: lastCommonAncestorChain.Utxo
            );

            // start calculating new utxo
            foreach (var tuple in BlockAndTxLookAhead(newChainBlockList))
            {
                // cooperative loop
                this.shutdownToken.ThrowIfCancellationRequested();
                cancelToken.ThrowIfCancellationRequested();

                try
                {
                    // get block and metadata for next link in blockchain
                    var nextBlock = tuple.Item1;
                    var nextChainedBlock = tuple.Item2;

                    // calculate the new block utxo, double spends will be checked for
                    ImmutableDictionary<UInt256, ImmutableHashSet<int>> newTransactions = ImmutableDictionary.Create<UInt256, ImmutableHashSet<int>>();
                    long txCount = 0, inputCount = 0;
                    var newUtxo = new MethodTimer(false).Time("CalculateUtxo", () =>
                        CalculateUtxo(nextChainedBlock.Height, nextBlock, newBlockchain.Utxo, out newTransactions, out txCount, out inputCount));

                    var nextBlockchain =
                        new MethodTimer(false).Time("nextBlockchain", () =>
                            new Data.Blockchain
                            (
                                blockList: newBlockchain.BlockList.Add(nextChainedBlock),
                                blockListHashes: newBlockchain.BlockListHashes.Add(nextChainedBlock.BlockHash),
                                utxo: newUtxo
                            ));

                    // validate the block
                    // validation utxo includes all transactions added in the same block, any double spends will have failed the block above
                    validateStopwatch.Start();
                    new MethodTimer(false).Time("ValidateBlock", () =>
                        this.Rules.ValidateBlock(nextBlock, nextBlockchain, newBlockchain.Utxo, newTransactions));
                    validateStopwatch.Stop();

                    // create the next link in the new blockchain
                    newBlockchain = nextBlockchain;
                    if (onProgress != null)
                        onProgress(newBlockchain);

                    // blockchain processing statistics
                    currentBlockCount++;
                    currentTxCount += txCount;
                    currentInputCount += inputCount;
                    totalTxCount += txCount;
                    totalInputCount += inputCount;

                    var txInterval = 100.THOUSAND();
                    if (
                        newBlockchain.Height % 10.THOUSAND() == 0
                        || (totalTxCount % txInterval < (totalTxCount - txCount) % txInterval || txCount >= txInterval))
                    {
                        LogBlockchainProgress(newBlockchain, totalStopwatch, totalTxCount, totalInputCount, currentRateStopwatch, currentBlockCount, currentTxCount, currentInputCount);

                        currentBlockCount = 0;
                        currentTxCount = 0;
                        currentInputCount = 0;
                        currentRateStopwatch.Reset();
                        currentRateStopwatch.Start();
                    }
                }
                catch (MissingDataException e)
                {
                    // if there is missing data once blockchain processing has started, return the current progress
                    missingData.Add(e);
                    break;
                }
                catch (AggregateException e)
                {
                    if (e.InnerExceptions.Any(x => !(x is MissingDataException)))
                    {
                        throw;
                    }
                    else
                    {
                        missingData.AddRange(e.InnerExceptions.OfType<MissingDataException>());
                        break;
                    }
                }
            }

            if (onProgress != null)
                onProgress(newBlockchain);

            LogBlockchainProgress(newBlockchain, totalStopwatch, totalTxCount, totalInputCount, currentRateStopwatch, currentBlockCount, currentTxCount, currentInputCount);

            return newBlockchain;
        }
Example #8
0
        public List<UInt256> FindBlocksPastLastCommonAncestor(Data.Blockchain currentBlockchain, ChainedBlock targetChainedBlock, CancellationToken cancelToken)
        {
            return new MethodTimer().Time(() =>
            {
                // take snapshots
                var newChainedBlock = targetChainedBlock;
                var newChainBlockList = new List<UInt256>();

                // check height difference between chains, they will be roll backed before checking for the last common ancestor
                var heightDelta = targetChainedBlock.Height - currentBlockchain.Height;

                // if current chain is shorter, roll new chain back to current chain's height
                ImmutableList<ChainedBlock> currentChainedBlocks;
                if (heightDelta > 0)
                {
                    currentChainedBlocks = currentBlockchain.BlockList;

                    List<ChainedBlock> rolledBackChainedBlocks;
                    newChainedBlock = RollbackChainedBlockToHeight(targetChainedBlock, currentBlockchain.Height, out rolledBackChainedBlocks, this.shutdownToken);
                    newChainBlockList.AddRange(rolledBackChainedBlocks.Select(x => x.BlockHash));
                }
                // if current chain is longer, roll it back to new chain's height
                else if (heightDelta < 0)
                {
                    currentChainedBlocks = currentBlockchain.BlockList.GetRange(0, targetChainedBlock.Height + 1);
                }
                else
                {
                    currentChainedBlocks = currentBlockchain.BlockList;
                }

                if (newChainedBlock.Height != currentChainedBlocks.Last().Height)
                    throw new Exception();

                // with both chains at the same height, roll back to last common ancestor
                if (newChainedBlock.BlockHash != currentChainedBlocks.Last().BlockHash)
                {
                    foreach (var tuple in
                        PreviousChainedBlocks(newChainedBlock).Zip(currentChainedBlocks.Reverse<ChainedBlock>(),
                            (prevBlock, currentBlock) => Tuple.Create(prevBlock, currentBlock)))
                    {
                        // cooperative loop
                        this.shutdownToken.ThrowIfCancellationRequested();
                        cancelToken.ThrowIfCancellationRequested();

                        newChainedBlock = tuple.Item1;
                        var currentBlock = tuple.Item2;

                        // ensure that height is as expected while looking up previous blocks
                        if (newChainedBlock.Height != currentBlock.Height)
                        {
                            throw new ValidationException();
                        }

                        if (newChainedBlock.BlockHash == currentBlock.BlockHash)
                        {
                            break;
                        }

                        // keep track of rolled back data on the new blockchain
                        newChainBlockList.Add(newChainedBlock.BlockHash);
                    }
                }

                // work list will have last items added first, reverse
                newChainBlockList.Reverse();

                return newChainBlockList;
            });
        }
Example #9
0
 private void OnChainedBlockModification(UInt256 blockHash, ChainedBlock chainedBlock)
 {
     OnChainedBlockAddition(blockHash);
 }
Example #10
0
        public BlockchainDaemon(IBlockchainRules rules, CacheContext cacheContext)
        {
            this.shutdownToken = new CancellationTokenSource();

            this._rules = rules;
            this._cacheContext = cacheContext;
            this._calculator = new BlockchainCalculator(this._rules, this._cacheContext, this.shutdownToken.Token);

            this._winningBlock = this._rules.GenesisChainedBlock;
            this._winningBlockchain = ImmutableArray.Create(this._rules.GenesisChainedBlock);
            this.winningBlockchainLock = new ReaderWriterLockSlim();

            this._currentBlockchain = this._rules.GenesisBlockchain;
            this.currentBlockchainLock = new ReaderWriterLockSlim();
            //TODO
            this.lastCurrentBlockchainWrite = Guid.NewGuid();

            this.missingBlocks = new ConcurrentSetBuilder<UInt256>();
            this.unchainedBlocks = new ConcurrentSetBuilder<UInt256>();
            this.missingChainedBlocks = new ConcurrentSet<UInt256>();
            this.missingTransactions = new ConcurrentSet<UInt256>();

            // write genesis block out to storage
            this._cacheContext.BlockCache.UpdateValue(this._rules.GenesisBlock.Hash, this._rules.GenesisBlock);
            this._cacheContext.ChainedBlockCache.UpdateValue(this._rules.GenesisChainedBlock.BlockHash, this._rules.GenesisChainedBlock);

            // wait for genesis block to be flushed
            this._cacheContext.BlockCache.WaitForStorageFlush();
            this._cacheContext.ChainedBlockCache.WaitForStorageFlush();

            // pre-fill the chained block and header caches
            //this._cacheContext.BlockHeaderCache.FillCache();
            this._cacheContext.ChainedBlockCache.FillCache();

            // wire up cache events
            this._cacheContext.BlockHeaderCache.OnAddition += OnBlockHeaderAddition;
            this._cacheContext.BlockHeaderCache.OnModification += OnBlockHeaderModification;
            this._cacheContext.BlockCache.OnAddition += OnBlockAddition;
            this._cacheContext.BlockCache.OnModification += OnBlockModification;
            this._cacheContext.ChainedBlockCache.OnAddition += OnChainedBlockAddition;
            this._cacheContext.ChainedBlockCache.OnModification += OnChainedBlockModification;

            this.unchainedBlocks.UnionWith(this.CacheContext.BlockHeaderCache.GetAllKeys());
            this.unchainedBlocks.ExceptWith(this.CacheContext.ChainedBlockCache.GetAllKeys());

            // create workers
            this.chainingWorker = new Worker("BlockchainDaemon.ChainingWorker", ChainingWorker,
                runOnStart: true, waitTime: TimeSpan.FromSeconds(1), maxIdleTime: TimeSpan.FromSeconds(30));

            this.winnerWorker = new Worker("BlockchainDaemon.WinnerWorker", WinnerWorker,
                runOnStart: true, waitTime: TimeSpan.FromSeconds(1), maxIdleTime: TimeSpan.FromSeconds(30));

            this.validationWorker = new Worker("BlockchainDaemon.ValidationWorker", ValidationWorker,
                runOnStart: true, waitTime: TimeSpan.FromSeconds(10), maxIdleTime: TimeSpan.FromMinutes(5));

            this.blockchainWorker = new Worker("BlockchainDaemon.BlockchainWorker", BlockchainWorker,
                runOnStart: true, waitTime: TimeSpan.FromSeconds(5), maxIdleTime: TimeSpan.FromMinutes(5));

            this.validateCurrentChainWorker = new Worker("BlockchainDaemon.ValidateCurrentChainWorker", ValidateCurrentChainWorker,
                runOnStart: true, waitTime: TimeSpan.FromMinutes(30), maxIdleTime: TimeSpan.FromMinutes(30));

            this.writeBlockchainWorker = new Worker("BlockchainDaemon.WriteBlockchainWorker", WriteBlockchainWorker,
                runOnStart: true, waitTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(30));
        }
Example #11
0
        private void ChainingWorker()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            var chainCount = 0;
            ChainedBlock? lastChainedBlock = null;

            var chainedBlocks = new List<ChainedBlock>();
            var chainedBlocksSet = new HashSet<UInt256>();

            var unchainedBlocksLocal = this.unchainedBlocks.ToImmutable();

            var unchainedByPrevious = new Dictionary<UInt256, List<BlockHeader>>();
            foreach (var unchainedBlock in unchainedBlocksLocal)
            {
                // cooperative loop
                this.shutdownToken.Token.ThrowIfCancellationRequested();

                // check that chained block is actually missing
                if (this.CacheContext.ChainedBlockCache.ContainsKey(unchainedBlock))
                {
                    this.unchainedBlocks.Remove(unchainedBlock);
                    continue;
                }

                BlockHeader unchainedBlockHeader;
                if (this.CacheContext.BlockHeaderCache.TryGetValue(unchainedBlock, out unchainedBlockHeader))
                {
                    if (!chainedBlocksSet.Contains(unchainedBlockHeader.PreviousBlock))
                    {
                        ChainedBlock chainedBlock;
                        if (this.CacheContext.ChainedBlockCache.ContainsKey(unchainedBlockHeader.PreviousBlock)
                            && this.CacheContext.ChainedBlockCache.TryGetValue(unchainedBlockHeader.PreviousBlock, out chainedBlock))
                        {
                            chainedBlocks.Add(chainedBlock);
                            chainedBlocksSet.Add(chainedBlock.BlockHash);
                        }
                    }

                    List<BlockHeader> unchainedGroup;
                    if (!unchainedByPrevious.TryGetValue(unchainedBlockHeader.PreviousBlock, out unchainedGroup))
                    {
                        unchainedGroup = new List<BlockHeader>();
                        unchainedByPrevious.Add(unchainedBlockHeader.PreviousBlock, unchainedGroup);
                    }
                    unchainedGroup.Add(unchainedBlockHeader);
                }
                else
                {
                    this.missingBlocks.Add(unchainedBlock);
                }
            }

            // start with chained blocks...
            for (var i = 0; i < chainedBlocks.Count; i++)
            {
                // cooperative loop
                this.shutdownToken.Token.ThrowIfCancellationRequested();

                var chainedBlock = chainedBlocks[i];

                // find any unchained blocks whose previous block is the current chained block...
                IList<BlockHeader> unchainedGroup;
                if (unchainedByPrevious.ContainsKey(chainedBlock.BlockHash))
                {
                    unchainedGroup = unchainedByPrevious[chainedBlock.BlockHash];
                    unchainedByPrevious.Remove(chainedBlock.BlockHash);
                }
                else
                {
                    unchainedGroup = new List<BlockHeader>();
                    foreach (var blockHash in this.CacheContext.ChainedBlockCache.FindByPreviousBlockHash(chainedBlock.BlockHash))
                    {
                        // cooperative loop
                        this.shutdownToken.Token.ThrowIfCancellationRequested();

                        BlockHeader blockHeader;
                        if (this.CacheContext.BlockHeaderCache.TryGetValue(blockHash, out blockHeader))
                            unchainedGroup.Add(blockHeader);
                    }
                }

                foreach (var unchainedBlock in unchainedGroup)
                {
                    // cooperative loop
                    this.shutdownToken.Token.ThrowIfCancellationRequested();

                    // check that block hasn't become chained
                    if (this.CacheContext.ChainedBlockCache.ContainsKey(unchainedBlock.Hash))
                        break;

                    // update the unchained block to chain off of the current chained block...
                    var newChainedBlock = new ChainedBlock
                    (
                        unchainedBlock.Hash,
                        unchainedBlock.PreviousBlock,
                        chainedBlock.Height + 1,
                        chainedBlock.TotalWork + unchainedBlock.CalculateWork()
                    );
                    this.CacheContext.ChainedBlockCache.CreateValue(newChainedBlock.BlockHash, newChainedBlock);

                    // and finally add the newly chained block to the list of chained blocks so that an attempt will be made to chain off of it
                    chainedBlocks.Add(newChainedBlock);

                    // statistics
                    chainCount++;
                    lastChainedBlock = newChainedBlock;
                    Debug.WriteLineIf(chainCount % 1.THOUSAND() == 0, "Chained block {0} at height {1}, total work: {2}".Format2(newChainedBlock.BlockHash.ToHexNumberString(), newChainedBlock.Height, newChainedBlock.TotalWork.ToString("X")));

                    if (chainCount % 1.THOUSAND() == 0)
                    {
                        // notify winner worker after chaining blocks
                        this.winnerWorker.NotifyWork();

                        // notify the blockchain worker after chaining blocks
                        this.blockchainWorker.NotifyWork();
                    }
                }
            }

            if (lastChainedBlock != null)
                Debug.WriteLine("Chained block {0} at height {1}, total work: {2}".Format2(lastChainedBlock.Value.BlockHash.ToHexNumberString(), lastChainedBlock.Value.Height, lastChainedBlock.Value.TotalWork.ToString("X")));

            if (chainCount > 0)
            {
                // keep looking for more broken links after each pass
                this.chainingWorker.NotifyWork();
            }

            // notify winner worker after chaining blocks
            this.winnerWorker.NotifyWork();

            // notify the blockchain worker after chaining blocks
            this.blockchainWorker.NotifyWork();

            stopwatch.Stop();
            //Debug.WriteLine("ChainingWorker: Chained {0:#,##0} items in {1:#,##0.000}s".Format2(chainCount, stopwatch.ElapsedSecondsFloat()));
        }
Example #12
0
        public bool TryGetChainedBlock(UInt256 blockHash, out ChainedBlock chainedBlock, bool saveInCache = true)
        {
            if (this.CacheContext.ChainedBlockCache.TryGetValue(blockHash, out chainedBlock, saveInCache))
            {
                this.missingChainedBlocks.TryRemove(blockHash);
                return true;
            }
            else
            {
                this.missingChainedBlocks.TryAdd(blockHash);
                if (!this.CacheContext.BlockCache.ContainsKey(blockHash))
                    this.missingBlocks.Add(blockHash);

                chainedBlock = default(ChainedBlock);
                return false;
            }
        }
Example #13
0
        public Testnet2Rules(CacheContext cacheContext)
            : base(cacheContext)
        {
            this._genesisBlock =
                new Block
                (
                    header: new BlockHeader
                    (
                        version: 1,
                        previousBlock: 0,
                        merkleRoot: UInt256.Parse("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", NumberStyles.HexNumber),
                        time: 1296688602,
                        bits: 0x207FFFFF,
                        nonce: 2
                    ),
                    transactions: ImmutableArray.Create
                    (
                        new Transaction
                        (
                            version: 1,
                            inputs: ImmutableArray.Create
                            (
                                new TxInput
                                (
                                    previousTxOutputKey: new TxOutputKey
                                    (
                                        txHash: 0,
                                        txOutputIndex: 0xFFFFFFFF
                                    ),
                                    scriptSignature: ImmutableArray.Create<byte>
                                    (
                                        0x04, 0xFF, 0xFF, 0x00, 0x1D, 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6D, 0x65,
                                        0x73, 0x20, 0x30, 0x33, 0x2F, 0x4A, 0x61, 0x6E, 0x2F, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68,
                                        0x61, 0x6E, 0x63, 0x65, 0x6C, 0x6C, 0x6F, 0x72, 0x20, 0x6F, 0x6E, 0x20, 0x62, 0x72, 0x69, 0x6E,
                                        0x6B, 0x20, 0x6F, 0x66, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6C,
                                        0x6F, 0x75, 0x74, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x62, 0x61, 0x6E, 0x6B, 0x73
                                    ),
                                    sequence: 0xFFFFFFFF
                                )
                            ),
                            outputs: ImmutableArray.Create
                            (
                                new TxOutput
                                (
                                    value: (UInt64)(50L * 100.MILLION()),
                                    scriptPublicKey: ImmutableArray.Create<byte>
                                    (
                                        0x41, 0x04, 0x67, 0x8A, 0xFD, 0xB0, 0xFE, 0x55, 0x48, 0x27, 0x19, 0x67, 0xF1, 0xA6, 0x71, 0x30,
                                        0xB7, 0x10, 0x5C, 0xD6, 0xA8, 0x28, 0xE0, 0x39, 0x09, 0xA6, 0x79, 0x62, 0xE0, 0xEA, 0x1F, 0x61,
                                        0xDE, 0xB6, 0x49, 0xF6, 0xBC, 0x3F, 0x4C, 0xEF, 0x38, 0xC4, 0xF3, 0x55, 0x04, 0xE5, 0x1E, 0xC1,
                                        0x12, 0xDE, 0x5C, 0x38, 0x4D, 0xF7, 0xBA, 0x0B, 0x8D, 0x57, 0x8A, 0x4C, 0x70, 0x2B, 0x6B, 0xF1,
                                        0x1D, 0x5F, 0xAC
                                    )
                                )
                            ),
                            lockTime: 0
                        )
                    )
                );

            Debug.Assert(_genesisBlock.Hash == UInt256.Parse("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", NumberStyles.HexNumber));

            this._genesisChainedBlock =
                new ChainedBlock
                (
                    blockHash: this._genesisBlock.Hash,
                    previousBlockHash: this._genesisBlock.Header.PreviousBlock,
                    height: 0,
                    totalWork: this._genesisBlock.Header.CalculateWork()
                );

            this._genesisBlockchain =
                new Data.Blockchain
                (
                    blockList: ImmutableList.Create(this._genesisChainedBlock),
                    blockListHashes: ImmutableHashSet.Create(this._genesisBlock.Hash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>() // genesis block coinbase is not included in utxo, it is unspendable
                );
        }
Example #14
0
 public static long SizeEstimator(ChainedBlock chainedBlock)
 {
     return(100);
 }
Example #15
0
        private void UpdateWinningBlock(ChainedBlock winningBlock)
        {
            this.winningBlockchainLock.EnterWriteLock();
            try
            {
                this._winningBlockchain = default(ImmutableArray<ChainedBlock>);
            }
            finally
            {
                this.winningBlockchainLock.ExitWriteLock();
            }

            this._winningBlock = winningBlock;

            // notify the blockchain worker after updating winning block
            this.blockchainWorker.NotifyWork();

            var handler = this.OnWinningBlockChanged;
            if (handler != null)
                handler(this, winningBlock);
        }
Example #16
0
        public Data.Blockchain RollbackToLastCommonAncestor(Data.Blockchain currentBlockchain, ChainedBlock targetChainedBlock, CancellationToken cancelToken, out List<UInt256> newChainBlockList)
        {
            // take snapshots
            var newChainedBlock = targetChainedBlock;
            newChainBlockList = new List<UInt256>();

            // check height difference between chains, they will be roll backed before checking for the last common ancestor
            var heightDelta = targetChainedBlock.Height - currentBlockchain.Height;

            // if current chain is shorter, roll new chain back to current chain's height
            if (heightDelta > 0)
            {
                List<ChainedBlock> rolledBackChainedBlocks;
                newChainedBlock = RollbackChainedBlockToHeight(targetChainedBlock, currentBlockchain.Height, out rolledBackChainedBlocks, this.shutdownToken);
                newChainBlockList.AddRange(rolledBackChainedBlocks.Select(x => x.BlockHash));
            }
            // if current chain is longer, roll it back to new chain's height
            else if (heightDelta < 0)
            {
                List<Data.Blockchain> rolledBackBlockchains;
                currentBlockchain = RollbackBlockchainToHeight(currentBlockchain, newChainedBlock.Height, out rolledBackBlockchains, this.shutdownToken);
            }

            if (newChainedBlock.Height != currentBlockchain.Height)
                throw new Exception();

            //TODO continue looking backwards while processing moves forward to double check
            //TODO the blockchain history back to genesis? only look at height, work, valid bits in
            //TODO the metadata, sync and check this task at the end before updating current blockchain,
            //TODO if any error is ever found, mark everything after it as invalid or unprocessed, the
            //TODO processor could get stuck otherwise trying what it thinks is the winning chain over and over

            // with both chains at the same height, roll back to last common ancestor
            if (newChainedBlock.BlockHash != currentBlockchain.RootBlockHash)
            {
                var rollbackList = new List<UInt256>();
                var currentBlockchainIndex = currentBlockchain.BlockList.Count - 1;
                foreach (var prevBlock in PreviousChainedBlocks(newChainedBlock))
                {
                    // cooperative loop
                    this.shutdownToken.ThrowIfCancellationRequested();
                    cancelToken.ThrowIfCancellationRequested();

                    newChainedBlock = prevBlock;
                    if (newChainedBlock.BlockHash == currentBlockchain.BlockList[currentBlockchainIndex].BlockHash)
                    {
                        break;
                    }

                    // ensure that height is as expected while looking up previous blocks
                    if (newChainedBlock.Height != currentBlockchain.BlockList[currentBlockchainIndex].Height)
                    {
                        throw new ValidationException();
                    }

                    // keep track of rolled back data on the new blockchain
                    newChainBlockList.Add(newChainedBlock.BlockHash);

                    // queue up current blockchain rollback
                    rollbackList.Add(currentBlockchain.BlockList[currentBlockchainIndex].BlockHash);

                    currentBlockchainIndex--;
                }

                // roll back current block chain
                foreach (var tuple in BlockLookAhead(rollbackList))
                {
                    var block = tuple.Item1;
                    currentBlockchain = RollbackBlockchain(currentBlockchain, block);
                }
            }

            // work list will have last items added first, reverse
            newChainBlockList.Reverse();

            return currentBlockchain;
        }
Example #17
0
        public MainnetRules(CacheContext cacheContext)
        {
            this._cacheContext = cacheContext;

            this._highestTarget = UInt256.Parse("00000000FFFF0000000000000000000000000000000000000000000000000000", NumberStyles.HexNumber);

            this._genesisBlock =
                new Block
                (
                    header: new BlockHeader
                    (
                        version: 1,
                        previousBlock: 0,
                        merkleRoot: UInt256.Parse("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", NumberStyles.HexNumber),
                        time: 1231006505,
                        bits: 486604799,
                        nonce: 2083236893
                    ),
                    transactions: ImmutableArray.Create
                    (
                        new Transaction
                        (
                            version: 1,
                            inputs: ImmutableArray.Create
                            (
                                new TxInput
                                (
                                    previousTxOutputKey: new TxOutputKey
                                    (
                                        txHash: 0,
                                        txOutputIndex: 0xFFFFFFFF
                                    ),
                                    scriptSignature: ImmutableArray.Create<byte>
                                    (
                                        0x04, 0xFF, 0xFF, 0x00, 0x1D, 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6D, 0x65,
                                        0x73, 0x20, 0x30, 0x33, 0x2F, 0x4A, 0x61, 0x6E, 0x2F, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68,
                                        0x61, 0x6E, 0x63, 0x65, 0x6C, 0x6C, 0x6F, 0x72, 0x20, 0x6F, 0x6E, 0x20, 0x62, 0x72, 0x69, 0x6E,
                                        0x6B, 0x20, 0x6F, 0x66, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6C,
                                        0x6F, 0x75, 0x74, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x62, 0x61, 0x6E, 0x6B, 0x73
                                    ),
                                    sequence: 0xFFFFFFFF
                                )
                            ),
                            outputs: ImmutableArray.Create
                            (
                                new TxOutput
                                (
                                    value: 50 * SATOSHI_PER_BTC,
                                    scriptPublicKey: ImmutableArray.Create<byte>
                                    (
                                        0x41, 0x04, 0x67, 0x8A, 0xFD, 0xB0, 0xFE, 0x55, 0x48, 0x27, 0x19, 0x67, 0xF1, 0xA6, 0x71, 0x30,
                                        0xB7, 0x10, 0x5C, 0xD6, 0xA8, 0x28, 0xE0, 0x39, 0x09, 0xA6, 0x79, 0x62, 0xE0, 0xEA, 0x1F, 0x61,
                                        0xDE, 0xB6, 0x49, 0xF6, 0xBC, 0x3F, 0x4C, 0xEF, 0x38, 0xC4, 0xF3, 0x55, 0x04, 0xE5, 0x1E, 0xC1,
                                        0x12, 0xDE, 0x5C, 0x38, 0x4D, 0xF7, 0xBA, 0x0B, 0x8D, 0x57, 0x8A, 0x4C, 0x70, 0x2B, 0x6B, 0xF1,
                                        0x1D, 0x5F, 0xAC
                                    )
                                )
                            ),
                            lockTime: 0
                        )
                    )
                );

            this._genesisChainedBlock =
                new ChainedBlock
                (
                    blockHash: this._genesisBlock.Hash,
                    previousBlockHash: this._genesisBlock.Header.PreviousBlock,
                    height: 0,
                    totalWork: this._genesisBlock.Header.CalculateWork()
                );

            this._genesisBlockchain =
                new Data.Blockchain
                (
                    blockList: ImmutableList.Create(this._genesisChainedBlock),
                    blockListHashes: ImmutableHashSet.Create(this._genesisBlock.Hash),
                    utxo: ImmutableDictionary.Create<UInt256, UnspentTx>() // genesis block coinbase is not included in utxo, it is unspendable
                );
        }
Example #18
0
        public ChainedBlock RollbackChainedBlockToHeight(ChainedBlock chainedBlock, int targetHeight, out List<ChainedBlock> rolledBackChainedBlocks, CancellationToken cancelToken)
        {
            if (targetHeight > chainedBlock.Height || targetHeight < 0)
                throw new ArgumentOutOfRangeException("targetHeight");

            rolledBackChainedBlocks = new List<ChainedBlock>();

            var targetChainedBlock = chainedBlock;
            var expectedHeight = targetChainedBlock.Height;
            while (targetChainedBlock.Height > targetHeight)
            {
                // cooperative loop
                cancelToken.ThrowIfCancellationRequested();

                // keep track of rolled back data on the new blockchain
                rolledBackChainedBlocks.Add(targetChainedBlock);

                // roll back
                targetChainedBlock = this.CacheContext.GetChainedBlock(targetChainedBlock.PreviousBlockHash);

                // ensure that height is as expected while looking up previous blocks
                expectedHeight--;
                if (targetChainedBlock.Height != expectedHeight)
                    throw new ValidationException();
            }

            return targetChainedBlock;
        }
Example #19
0
 public static long SizeEstimator(ChainedBlock chainedBlock)
 {
     return 100;
 }
Example #20
0
 public IEnumerable<Tuple<ChainedBlock, Block>> PreviousBlocksLookAhead(ChainedBlock firstBlock)
 {
     using (var cancelToken = new CancellationTokenSource())
     {
         foreach (var tuple in LookAheadMethods.LookAhead(() => PreviousBlocks(firstBlock), cancelToken.Token))
         {
             yield return tuple;
         }
     }
 }
Example #21
0
        public Tuple<Block, ChainedBlock> AddBlock(Block block, ChainedBlock? prevChainedBlock)
        {
            if (prevChainedBlock != null)
                Assert.AreEqual(block.Header.PreviousBlock, prevChainedBlock.Value.BlockHash);

            var chainedBlock = new ChainedBlock
            (
                block.Hash,
                block.Header.PreviousBlock,
                prevChainedBlock != null ? prevChainedBlock.Value.Height + 1 : 0,
                prevChainedBlock != null ? prevChainedBlock.Value.TotalWork + block.Header.CalculateWork() : block.Header.CalculateWork()
            );

            this.CacheContext.BlockCache.CreateValue(block.Hash, block);

            this.CacheContext.ChainedBlockCache.CreateValue(block.Hash, chainedBlock);

            ChooseNewWinner();

            return Tuple.Create(block, chainedBlock);
        }
Example #22
0
 private void OnWinningBlockChanged(object sender, ChainedBlock winningChainedBlock)
 {
     this.requestHeadersWorker.NotifyWork();
 }
Example #23
0
        public bool TryReadValue(UInt256 blockHash, out ChainedBlock chainedBlock)
        {
            using (var conn = this.OpenConnection())
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = @"
                    SELECT PreviousBlockHash, Height, TotalWork
                    FROM ChainedBlocks
                    WHERE BlockHash = @blockHash";

                cmd.Parameters.SetValue("@blockHash", FbDbType.Char, FbCharset.Octets, 32).Value = blockHash.ToDbByteArray();

                using (var reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        var previousBlockHash = reader.GetUInt256(0);
                        var height = reader.GetInt32(1);
                        var totalWork = reader.GetBigInteger(2);

                        chainedBlock = new ChainedBlock
                        (
                            blockHash,
                            previousBlockHash,
                            height,
                            totalWork
                        );
                        return true;
                    }
                    else
                    {
                        chainedBlock = default(ChainedBlock);
                        return false;
                    }
                }
            }
        }