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 MainWindowViewModel(BlockchainDaemon blockchainDaemon)
        {
            this.blockchainDaemon = blockchainDaemon;

            var winningBlockLocal      = this.blockchainDaemon.WinningBlock;
            var currentBlockchainLocal = this.blockchainDaemon.CurrentBlockchain;

            this.viewBlockchain = currentBlockchainLocal;

            this.WinningBlockchainHeight = winningBlockLocal.Height;
            this.CurrentBlockchainHeight = currentBlockchainLocal.Height;
            this.DownloadedBlockCount    = this.blockchainDaemon.CacheContext.BlockCache.Count;

            this.blockchainDaemon.CacheContext.BlockCache.OnAddition +=
                blockHash =>
                DownloadedBlockCount = this.blockchainDaemon.CacheContext.BlockCache.Count;

            this.blockchainDaemon.OnWinningBlockChanged +=
                (sender, block) =>
                WinningBlockchainHeight = block.Height;

            this.blockchainDaemon.OnCurrentBlockchainChanged +=
                (sender, blockchain) =>
                CurrentBlockchainHeight = blockchain.Height;
        }
Example #3
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 #4
0
        private void LogBlockchainProgress(Data.Blockchain blockchain, Stopwatch totalStopwatch, long totalTxCount, long totalInputCount, Stopwatch currentRateStopwatch, long currentBlockCount, long currentTxCount, long currentInputCount)
        {
            var currentBlockRate = (float)currentBlockCount / currentRateStopwatch.ElapsedSecondsFloat();
            var currentTxRate    = (float)currentTxCount / currentRateStopwatch.ElapsedSecondsFloat();
            var currentInputRate = (float)currentInputCount / currentRateStopwatch.ElapsedSecondsFloat();

            Debug.WriteLine(
                string.Join("\n",
                            new string('-', 80),
                            "Height: {0,10} | Date: {1} | Duration: {7} hh:mm:ss | Validation: {8} hh:mm:ss | Blocks/s: {2,7} | Tx/s: {3,7} | Inputs/s: {4,7} | Total Tx: {5,7} | Total Inputs: {6,7} | Utxo Size: {9,7}",
                            "GC Memory:      {10,10:#,##0.00} MB",
                            "Process Memory: {11,10:#,##0.00} MB",
                            new string('-', 80)
                            )
                .Format2
                (
                    blockchain.Height.ToString("#,##0"),
                    "",
                    currentBlockRate.ToString("#,##0"),
                    currentTxRate.ToString("#,##0"),
                    currentInputRate.ToString("#,##0"),
                    totalTxCount.ToString("#,##0"),
                    totalInputCount.ToString("#,##0"),
                    totalStopwatch.Elapsed.ToString(@"hh\:mm\:ss"),
                    validateStopwatch.Elapsed.ToString(@"hh\:mm\:ss"),
                    blockchain.Utxo.Count.ToString("#,##0"),
                    (float)GC.GetTotalMemory(false) / 1.MILLION(),
                    (float)Process.GetCurrentProcess().PrivateMemorySize64 / 1.MILLION()
                ));
        }
Example #5
0
        public Data.Blockchain RollbackBlockchain(Data.Blockchain blockchain, Block block, out List <TxOutputKey> spendOutputs, out List <TxOutputKey> receiveOutputs)
        {
            if (blockchain.BlockCount == 0 || blockchain.RootBlockHash != block.Hash)
            {
                throw new ValidationException();
            }

            var newUtxo = RollbackUtxo(blockchain, block, out spendOutputs, out receiveOutputs);

            return(new Data.Blockchain
                   (
                       blockList: blockchain.BlockList.RemoveAt(blockchain.BlockCount - 1),
                       blockListHashes: blockchain.BlockListHashes.Remove(blockchain.RootBlockHash),
                       utxo: newUtxo
                   ));
        }
Example #6
0
        private void SetViewBlockchain(Data.Blockchain blockchain)
        {
            this.viewBlockchain = blockchain;

            try
            {
                if (blockchain.Height > 0)
                {
                    Block block;
                    if (this.blockchainDaemon.TryGetBlock(this.viewBlockchain.RootBlockHash, out block))
                    {
                        // TODO this is abusing rollback a bit just to get the transactions that exist in a target block that's already known
                        // TODO make a better api for get the net output of a block
                        List <TxOutputKey> spendOutputs, receiveOutputs;
                        this.blockchainDaemon.Calculator.RollbackBlockchain(this.viewBlockchain, block, out spendOutputs, out receiveOutputs);

                        ViewBlockchainSpendOutputs   = spendOutputs;
                        ViewBlockchainReceiveOutputs = receiveOutputs;
                    }
                }
                else
                {
                    ViewBlockchainSpendOutputs   = new List <TxOutputKey>();
                    ViewBlockchainReceiveOutputs = new List <TxOutputKey>();
                }
            }
            catch (MissingDataException)
            {
                // TODO
            }
            catch (AggregateException e)
            {
                if (!e.IsMissingDataOnly())
                {
                    throw;
                }
            }

            var handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs("ViewBlockchainHeight"));
                handler(this, new PropertyChangedEventArgs("ViewBlockchainSpendOutputs"));
                handler(this, new PropertyChangedEventArgs("ViewBlockchainReceiveOutputs"));
            }
        }
Example #7
0
        private Guid UpdateCurrentBlockchain(Data.Blockchain newBlockchain)
        {
            var guid = Guid.NewGuid();

            this.currentBlockchainLock.DoWrite(() =>
            {
                this.lastCurrentBlockchainWrite = guid;
                this._currentBlockchain         = newBlockchain;
            });

            var handler = this.OnCurrentBlockchainChanged;

            if (handler != null)
            {
                handler(this, newBlockchain);
            }

            return(guid);
        }
Example #8
0
        public Data.Blockchain RollbackBlockchainToHeight(Data.Blockchain blockchain, int targetHeight, out List <Data.Blockchain> rolledBackBlockchains, CancellationToken cancelToken)
        {
            if (targetHeight > blockchain.Height || targetHeight < 0)
            {
                throw new ArgumentOutOfRangeException("targetHeight");
            }

            List <ChainedBlock> rolledBackChainedBlocks;
            var targetChainedBlock = RollbackChainedBlockToHeight(blockchain.RootBlock, targetHeight, out rolledBackChainedBlocks, cancelToken);

            var rollbackCount = blockchain.Height - targetHeight;

            if (rolledBackChainedBlocks.Count != rollbackCount)
            {
                throw new Exception();
            }

            rolledBackBlockchains = new List <Data.Blockchain>();

            var targetBlockchain = blockchain;
            var rollbackIndex    = 0;

            foreach (var tuple in BlockLookAhead(rolledBackChainedBlocks.Select(x => x.BlockHash).ToList()))
            {
                // cooperative loop
                this.shutdownToken.ThrowIfCancellationRequested();
                cancelToken.ThrowIfCancellationRequested();

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

                // roll back
                var block = tuple.Item1;
                Debug.Assert(targetBlockchain.RootBlockHash == block.Hash);
                targetBlockchain = RollbackBlockchain(targetBlockchain, block);

                Debug.WriteLineIf(rollbackIndex % 100 == 0, "Rolling back {0} of {1}".Format2(rollbackIndex + 1, rollbackCount));
                rollbackIndex++;
            }

            return(targetBlockchain);
        }
Example #9
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 #10
0
        private void ChooseNewWinner()
        {
            //TODO if there is a valid blockchain with less work than invalid blockchains, it won't get picked up as this is currently implemented

            //TODO when there is a tie this method is not deterministic, causing TestSimpleBlockchainSplit to fail

            var leafChainedBlocks =
                this.CacheContext.ChainedBlockCache.FindLeafChainedBlocks()
                .ToDictionary(x => x.BlockHash, x => x);

            while (true)
            {
                var newWinner = this._rules.SelectWinningChainedBlock(leafChainedBlocks.Values.ToList());
                if (newWinner == null)
                {
                    break;
                }

                leafChainedBlocks.Remove(newWinner.BlockHash);
                try
                {
                    // try to use the blockchain
                    using (var cancelToken = new CancellationTokenSource())
                    {
                        List <MissingDataException> missingData;
                        this._currentBlockchain = this._calculator.CalculateBlockchainFromExisting(this._currentBlockchain, newWinner, out missingData, cancelToken.Token);
                    }

                    // success, exit
                    return;
                }
                catch (ValidationException) { }

                // failure, try another candidate if present
            }
        }
Example #11
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 #12
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 #13
0
        private void ChooseNewWinner()
        {
            //TODO if there is a valid blockchain with less work than invalid blockchains, it won't get picked up as this is currently implemented

            //TODO when there is a tie this method is not deterministic, causing TestSimpleBlockchainSplit to fail

            var leafChainedBlocks =
                this.CacheContext.ChainedBlockCache.FindLeafChainedBlocks()
                 .ToDictionary(x => x.BlockHash, x => x);

            while (true)
            {
                var newWinner = this._rules.SelectWinningChainedBlock(leafChainedBlocks.Values.ToList());
                if (newWinner.IsDefault)
                    break;

                leafChainedBlocks.Remove(newWinner.BlockHash);
                try
                {
                    // try to use the blockchain
                    using (var cancelToken = new CancellationTokenSource())
                    {
                        List<MissingDataException> missingData;
                        this._currentBlockchain = this._calculator.CalculateBlockchainFromExisting(this._currentBlockchain, newWinner, out missingData, cancelToken.Token);
                    }

                    // success, exit
                    return;
                }
                catch (ValidationException) { }

                // failure, try another candidate if present
            }
        }
Example #14
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 #15
0
        public void RevalidateBlockchain(Data.Blockchain blockchain, Block genesisBlock)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            try
            {
                //TODO delete corrupted data? could get stuck in a fail-loop on the winning chain otherwise

                // verify blockchain has blocks
                if (blockchain.BlockList.Count == 0)
                {
                    throw new ValidationException();
                }

                // verify genesis block hash
                if (blockchain.BlockList[0].BlockHash != genesisBlock.Hash)
                {
                    throw new ValidationException();
                }

                // get genesis block header
                var chainGenesisBlockHeader = this.CacheContext.GetBlockHeader(blockchain.BlockList[0].BlockHash);

                // verify genesis block header
                if (
                    genesisBlock.Header.Version != chainGenesisBlockHeader.Version ||
                    genesisBlock.Header.PreviousBlock != chainGenesisBlockHeader.PreviousBlock ||
                    genesisBlock.Header.MerkleRoot != chainGenesisBlockHeader.MerkleRoot ||
                    genesisBlock.Header.Time != chainGenesisBlockHeader.Time ||
                    genesisBlock.Header.Bits != chainGenesisBlockHeader.Bits ||
                    genesisBlock.Header.Nonce != chainGenesisBlockHeader.Nonce ||
                    genesisBlock.Hash != chainGenesisBlockHeader.Hash ||
                    genesisBlock.Hash != CalculateHash(chainGenesisBlockHeader))
                {
                    throw new ValidationException();
                }

                // setup expected previous block hash value to verify each chain actually does link
                var expectedPreviousBlockHash = genesisBlock.Header.PreviousBlock;
                for (var height = 0; height < blockchain.BlockList.Count; height++)
                {
                    // cooperative loop
                    this.shutdownToken.ThrowIfCancellationRequested();

                    // get the current link in the chain
                    var chainedBlock = blockchain.BlockList[height];

                    // verify height
                    if (chainedBlock.Height != height)
                    {
                        throw new ValidationException();
                    }

                    // verify blockchain linking
                    if (chainedBlock.PreviousBlockHash != expectedPreviousBlockHash)
                    {
                        throw new ValidationException();
                    }

                    // verify block exists
                    var blockHeader = this.CacheContext.GetBlockHeader(chainedBlock.BlockHash);

                    // verify block metadata matches header values
                    if (blockHeader.PreviousBlock != chainedBlock.PreviousBlockHash)
                    {
                        throw new ValidationException();
                    }

                    // verify block header hash
                    if (CalculateHash(blockHeader) != chainedBlock.BlockHash)
                    {
                        throw new ValidationException();
                    }

                    // next block metadata should have the current metadata's hash as its previous hash value
                    expectedPreviousBlockHash = chainedBlock.BlockHash;
                }

                // all validation passed
            }
            finally
            {
                stopwatch.Stop();
                Debug.WriteLine("Blockchain revalidation: {0:#,##0.000000}s".Format2(stopwatch.ElapsedSecondsFloat()));
            }
        }
Example #16
0
        private Guid UpdateCurrentBlockchain(Data.Blockchain newBlockchain)
        {
            var guid = Guid.NewGuid();

            this.currentBlockchainLock.DoWrite(() =>
            {
                this.lastCurrentBlockchainWrite = guid;
                this._currentBlockchain = newBlockchain;
            });

            var handler = this.OnCurrentBlockchainChanged;
            if (handler != null)
                handler(this, newBlockchain);

            return guid;
        }
Example #17
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 #18
0
        public virtual void ValidateBlock(Block block, Data.Blockchain blockchain, ImmutableDictionary <UInt256, UnspentTx> utxo, ImmutableDictionary <UInt256, ImmutableHashSet <int> > newTransactions /*, ImmutableDictionary<UInt256, Transaction> transactions*/)
        {
            //TODO
            if (BypassValidation)
            {
                return;
            }

            // calculate the next required target
            var requiredTarget = GetRequiredNextTarget(blockchain);

            // validate block's target against the required target
            var blockTarget = block.Header.CalculateTarget();

            if (blockTarget > requiredTarget)
            {
                throw new ValidationException("Failing block {0} at height {1}: Block target {2} did not match required target of {3}".Format2(block.Hash.ToHexNumberString(), blockchain.Height, blockTarget.ToHexNumberString(), requiredTarget.ToHexNumberString()));
            }

            // validate block's proof of work against its stated target
            if (block.Hash > blockTarget || block.Hash > requiredTarget)
            {
                throw new ValidationException("Failing block {0} at height {1}: Block did not match its own target of {2}".Format2(block.Hash.ToHexNumberString(), blockchain.Height, blockTarget.ToHexNumberString()));
            }

            // ensure there is at least 1 transaction
            if (block.Transactions.Length == 0)
            {
                throw new ValidationException("Failing block {0} at height {1}: Zero transactions present".Format2(block.Hash.ToHexNumberString(), blockchain.Height));
            }

            //TODO apply real coinbase rule
            // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
            var coinbaseTx = block.Transactions[0];

            // check that coinbase has only one input
            if (coinbaseTx.Inputs.Length != 1)
            {
                throw new ValidationException("Failing block {0} at height {1}: Coinbase transaction does not have exactly one input".Format2(block.Hash.ToHexNumberString(), blockchain.Height));
            }

            // validate transactions in parallel
            long unspentValue = 0L;

            try
            {
                Parallel.For(1, block.Transactions.Length, (txIndex, loopState) =>
                {
                    var tx = block.Transactions[txIndex];

                    long unspentValueInner;
                    //TODO utxo will not be correct at transaction if a tx hash is reused within the same block
                    ValidateTransaction(blockchain.Height, block, tx, txIndex, utxo, newTransactions, out unspentValueInner);

                    Interlocked.Add(ref unspentValue, unspentValueInner);
                });
            }
            catch (AggregateException e)
            {
                var validationException  = e.InnerExceptions.FirstOrDefault(x => x is ValidationException);
                var missingDataException = e.InnerExceptions.FirstOrDefault(x => x is MissingDataException);

                if (validationException != null)
                {
                    throw validationException;
                }

                if (missingDataException != null)
                {
                    throw missingDataException;
                }

                throw;
            }

            //TODO utxo will not be correct at transaction if a tx hash is reused within the same block
            ValidateTransactionScripts(block, utxo, newTransactions);

            // calculate the expected reward in coinbase
            var expectedReward = (long)(50 * SATOSHI_PER_BTC);

            if (blockchain.Height / 210000 <= 32)
            {
                expectedReward /= (long)Math.Pow(2, blockchain.Height / 210000);
            }
            expectedReward += unspentValue;

            // calculate the actual reward in coinbase
            var actualReward = 0L;

            foreach (var txOutput in coinbaseTx.Outputs)
            {
                actualReward += (long)txOutput.Value;
            }

            // ensure coinbase has correct reward
            if (actualReward > expectedReward)
            {
                throw new ValidationException("Failing block {0} at height {1}: Coinbase value is greater than reward + fees".Format2(block.Hash.ToHexNumberString(), blockchain.Height));
            }

            // all validation has passed
        }
Example #19
0
        public BlockchainKey WriteBlockchain(Data.Blockchain blockchain)
        {
            var guid          = Guid.NewGuid();
            var blockchainKey = new BlockchainKey(guid, blockchain.RootBlockHash);

            var connString = @"Server=localhost; Database=BitSharp_Blockchains; Trusted_Connection=true;";

            using (var conn = new SqlConnection(connString))
            {
                conn.Open();

                using (var trans = conn.BeginTransaction())
                {
                    // write out the metadata for the blockchain
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            INSERT INTO BlockchainMetadata (Guid, RootBlockHash, TotalWork, IsComplete)
                            VALUES (@guid, @rootBlockHash, @totalWork, 0);";

                        cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value          = blockchainKey.Guid.ToByteArray();
                        cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();
                        cmd.Parameters.SetValue("@totalWork", SqlDbType.Binary, 64).Value     = blockchain.TotalWork.ToDbByteArray();

                        cmd.ExecuteNonQuery();
                    }

                    // write out the block metadata comprising the blockchain
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            INSERT INTO ChainedBlocks (Guid, RootBlockHash, BlockHash, PreviousBlockHash, Height, TotalWork)
                            VALUES (@guid, @rootBlockHash, @blockHash, @previousBlockHash, @height, @totalWork)";

                        cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value          = blockchainKey.Guid.ToByteArray();
                        cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();
                        cmd.Parameters.Add(new SqlParameter {
                            ParameterName = "@blockHash", SqlDbType = SqlDbType.Binary, Size = 32
                        });
                        cmd.Parameters.Add(new SqlParameter {
                            ParameterName = "@previousBlockHash", SqlDbType = SqlDbType.Binary, Size = 32
                        });
                        cmd.Parameters.Add(new SqlParameter {
                            ParameterName = "@height", SqlDbType = SqlDbType.Int
                        });
                        cmd.Parameters.Add(new SqlParameter {
                            ParameterName = "@totalWork", SqlDbType = SqlDbType.Binary, Size = 64
                        });

                        foreach (var chainedBlock in blockchain.BlockList)
                        {
                            cmd.Parameters["@blockHash"].Value         = chainedBlock.BlockHash.ToDbByteArray();
                            cmd.Parameters["@previousBlockHash"].Value = chainedBlock.PreviousBlockHash.ToDbByteArray();
                            cmd.Parameters["@height"].Value            = chainedBlock.Height;
                            cmd.Parameters["@totalWork"].Value         = chainedBlock.TotalWork.ToDbByteArray();

                            cmd.ExecuteNonQuery();
                        }
                    }

                    // write out the utxo
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            INSERT INTO UtxoData (Guid, RootBlockhash, UtxoChunkBytes)
                            VALUES (@guid, @rootBlockHash, @utxoChunkBytes)";

                        cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value          = blockchainKey.Guid.ToByteArray();
                        cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();
                        cmd.Parameters.Add(new SqlParameter {
                            ParameterName = "@utxoChunkBytes", SqlDbType = SqlDbType.VarBinary
                        });

                        var chunkSize     = 100000;
                        var currentOffset = 0;
                        var chunkBytes    = new byte[4 + (36 * chunkSize)];

                        using (var utxoEnumerator = blockchain.Utxo.GetEnumerator())
                        {
                            // chunk outer loop
                            while (currentOffset < blockchain.Utxo.Count)
                            {
                                var chunkLength = Math.Min(chunkSize, blockchain.Utxo.Count - currentOffset);

                                var chunkStream = new MemoryStream(chunkBytes);
                                using (var chunkWriter = new BinaryWriter(chunkStream))
                                {
                                    chunkWriter.Write4Bytes((UInt32)chunkLength);

                                    // chunk inner loop
                                    for (var i = 0; i < chunkLength; i++)
                                    {
                                        // get the next output from the utxo
                                        if (!utxoEnumerator.MoveNext())
                                        {
                                            throw new Exception();
                                        }

                                        var output = utxoEnumerator.Current;
                                        //TODO chunkWriter.Write32Bytes(output.TxHash);
                                        //TODO chunkWriter.Write4Bytes((UInt32)output.TxOutputIndex);
                                    }

                                    cmd.Parameters["@utxoChunkBytes"].Size  = chunkBytes.Length;
                                    cmd.Parameters["@utxoChunkBytes"].Value = chunkBytes;
                                }

                                // write the chunk
                                cmd.ExecuteNonQuery();

                                currentOffset += chunkLength;
                            }

                            // there should be no items left in utxo at this point
                            if (utxoEnumerator.MoveNext())
                            {
                                throw new Exception();
                            }
                        }
                    }

                    // mark write as complete
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            UPDATE BlockchainMetadata SET IsComplete = 1
                            WHERE Guid = @guid AND RootBlockHash = @rootBlockHash;";

                        cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value          = blockchainKey.Guid.ToByteArray();
                        cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                        cmd.ExecuteNonQuery();
                    }


                    trans.Commit();
                }
            }

            return(blockchainKey);
        }
Example #20
0
        public Data.Blockchain RollbackBlockchain(Data.Blockchain blockchain, Block block)
        {
            List <TxOutputKey> spendOutputs, receiveOutputs;

            return(RollbackBlockchain(blockchain, block, out spendOutputs, out receiveOutputs));
        }
Example #21
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 #22
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 #23
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 #24
0
        private ImmutableDictionary <UInt256, UnspentTx> RollbackUtxo(Data.Blockchain blockchain, Block block, out List <TxOutputKey> spendOutputs, out List <TxOutputKey> receiveOutputs)
        {
            var blockHeight = blockchain.Height;
            var currentUtxo = blockchain.Utxo;

            // create builder for prev utxo
            var prevUtxoBuilder = currentUtxo.ToBuilder();

            spendOutputs   = new List <TxOutputKey>();
            receiveOutputs = new List <TxOutputKey>();

            //TODO apply real coinbase rule
            // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
            var coinbaseTx = block.Transactions[0];

            for (var outputIndex = 0; outputIndex < coinbaseTx.Outputs.Length; outputIndex++)
            {
                var txOutputKey = new TxOutputKey(coinbaseTx.Hash, (UInt32)outputIndex);
                if (blockHeight > 0)
                {
                    // remove new outputs from the rolled back utxo
                    if (prevUtxoBuilder.Remove(coinbaseTx.Hash))
                    {
                        receiveOutputs.Add(txOutputKey);
                    }
                    else
                    {
                        // missing transaction output
                        Debug.WriteLine("Missing transaction at block {0:#,##0}, {1}, tx {2}, output {3}".Format2(blockHeight, block.Hash.ToHexNumberString(), 0, outputIndex));
                        Debugger.Break();
                        //TODO throw new Validation();

                        //TODO this needs to be tracked so that blocks can be rolled back accurately
                        //TODO track these separately on the blockchain info? gonna be costly to track on every transaction
                    }
                }
            }

            for (var txIndex = block.Transactions.Length - 1; txIndex >= 1; txIndex--)
            {
                var tx = block.Transactions[txIndex];

                for (var outputIndex = tx.Outputs.Length - 1; outputIndex >= 0; outputIndex--)
                {
                    var output      = tx.Outputs[outputIndex];
                    var txOutputKey = new TxOutputKey(tx.Hash, (UInt32)outputIndex);
                    //TODO what if a transaction wasn't added to the utxo because it already existed?
                    //TODO the block would still pass without adding the tx to its utxo, but here it would get rolled back
                    //TODO maybe a flag bit to track this?

                    // remove new outputs from the rolled back utxo
                    if (prevUtxoBuilder.Remove(tx.Hash))
                    {
                        receiveOutputs.Add(txOutputKey);
                    }
                    else
                    {
                        // missing transaction output
                        Debug.WriteLine("Missing transaction at block {0:#,##0}, {1}, tx {2}, output {3}".Format2(blockHeight, block.Hash.ToHexNumberString(), txIndex, outputIndex));
                        Debugger.Break();
                        //TODO throw new Validation();

                        //TODO this needs to be tracked so that blocks can be rolled back accurately
                        //TODO track these separately on the blockchain info? gonna be costly to track on every transaction
                    }
                }

                for (var inputIndex = tx.Inputs.Length - 1; inputIndex >= 0; inputIndex--)
                {
                    var input = tx.Inputs[inputIndex];

                    // add spent outputs back into the rolled back utxo
                    if (prevUtxoBuilder.ContainsKey(input.PreviousTxOutputKey.TxHash))
                    {
                        var prevUnspentTx = prevUtxoBuilder[input.PreviousTxOutputKey.TxHash];

                        // check if output is out of bounds
                        if (input.PreviousTxOutputKey.TxOutputIndex >= prevUnspentTx.UnspentOutputs.Length)
                        {
                            throw new ValidationException();
                        }

                        // check that output isn't already considered unspent
                        if (prevUnspentTx.UnspentOutputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()])
                        {
                            throw new ValidationException();
                        }

                        // mark output as unspent
                        prevUtxoBuilder[input.PreviousTxOutputKey.TxHash] =
                            new UnspentTx(prevUnspentTx.BlockHash, prevUnspentTx.TxIndex, prevUnspentTx.TxHash,
                                          prevUnspentTx.UnspentOutputs.Set(input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked(), true));
                    }
                    else
                    {
                        // fully spent transaction being added back in during roll back
                        //TODO
                        throw new NotImplementedException();
                    }

                    //TODO
                    //if (prevUtxoBuilder.Add(input.PreviousTxOutputKey))
                    //{
                    //    spendOutputs.Add(input.PreviousTxOutputKey);
                    //}
                    //else
                    //{
                    //    // missing transaction output
                    //    Debug.WriteLine("Duplicate transaction at block {0:#,##0}, {1}, tx {2}, input {3}".Format2(blockHeight, block.Hash.ToHexNumberString(), txIndex, inputIndex));
                    //    Debugger.Break();
                    //    //TODO throw new Validation();

                    //    //TODO this needs to be tracked so that blocks can be rolled back accurately
                    //    //TODO track these separately on the blockchain info? gonna be costly to track on every transaction
                    //}
                }
            }

            return(prevUtxoBuilder.ToImmutable());
        }
Example #25
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 #26
0
        public virtual UInt256 GetRequiredNextTarget(Data.Blockchain blockchain)
        {
            try
            {
                // lookup genesis block header
                var genesisBlockHeader = this.CacheContext.GetBlockHeader(blockchain.BlockList[0].BlockHash);

                // lookup the latest block on the current blockchain
                var currentBlockHeader = this.CacheContext.GetBlockHeader(blockchain.RootBlockHash);

                // use genesis block difficulty if first adjusment interval has not yet been reached
                if (blockchain.Height < DifficultyInternal)
                {
                    return(genesisBlockHeader.CalculateTarget());
                }
                // not on an adjustment interval, reuse current block's target
                else if (blockchain.Height % DifficultyInternal != 0)
                {
                    return(currentBlockHeader.CalculateTarget());
                }
                // on an adjustment interval, calculate the required next target
                else
                {
                    // get the block difficultyInterval blocks ago
                    var startChainedBlock = blockchain.BlockList.Reverse().Skip(DifficultyInternal).First();
                    var startBlockHeader  = this.CacheContext.GetBlockHeader(startChainedBlock.BlockHash);
                    Debug.Assert(startChainedBlock.Height == blockchain.Height - DifficultyInternal);

                    var actualTimespan = (long)currentBlockHeader.Time - (long)startBlockHeader.Time;
                    var targetTimespan = DifficultyTargetTimespan;

                    // limit adjustment to 4x or 1/4x
                    if (actualTimespan < targetTimespan / 4)
                    {
                        actualTimespan = targetTimespan / 4;
                    }
                    else if (actualTimespan > targetTimespan * 4)
                    {
                        actualTimespan = targetTimespan * 4;
                    }

                    // calculate the new target
                    var target = startBlockHeader.CalculateTarget();
                    target *= actualTimespan;
                    target /= targetTimespan;

                    // make sure target isn't too high (too low difficulty)
                    if (target > HighestTarget)
                    {
                        target = HighestTarget;
                    }

                    return(target);
                }
            }
            catch (ArgumentException)
            {
                // invalid bits
                Debugger.Break();
                throw new ValidationException();
            }
        }
Example #27
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 == null)
            {
                // 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 #28
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 #29
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
                );
        }