Example #1
0
 public LevelDBBlockchain()
 {
     Slice value;
     db = DB.Open(Settings.Default.DataDirectoryPath);
     if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.CFG_Initialized), out value) && value.ToBoolean())
     {
         value = db.Get(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock));
         this.current_block = new UInt256(value.ToArray().Take(32).ToArray());
         this.current_height = BitConverter.ToUInt32(value.ToArray(), 32);
     }
     else
     {
         WriteBatch batch = new WriteBatch();
         ReadOptions options = new ReadOptions { FillCache = false };
         using (Iterator it = db.NewIterator(options))
         {
             for (it.SeekToFirst(); it.Valid(); it.Next())
             {
                 batch.Delete(it.Key());
             }
         }
         batch.Put(SliceBuilder.Begin(DataEntryPrefix.CFG_Version), 0);
         db.Write(WriteOptions.Default, batch);
         AddBlockToChain(GenesisBlock);
         db.Put(WriteOptions.Default, SliceBuilder.Begin(DataEntryPrefix.CFG_Initialized), true);
     }
     thread_persistence = new Thread(PersistBlocks);
     thread_persistence.Name = "LevelDBBlockchain.PersistBlocks";
     thread_persistence.Start();
     AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
 }
        public static void Put(this LevelDB.WriteBatch batch, byte table, byte[] key, byte[] value)
        {
            Span <byte> dbkey = stackalloc byte[key.Length + 1];

            dbkey[0] = table;
            key.AsSpan().CopyTo(dbkey.Slice(1));

            batch.Put(dbkey.ToArray(), value);
        }
Example #3
0
        public bool TryAddChainedHeader(ChainedHeader chainedHeader)
        {
            var key = MakeHeaderKey(chainedHeader.Hash);

            Slice existingValue;
            if (db.TryGet(ReadOptions.Default, key, out existingValue))
                return false;

            var writeBatch = new WriteBatch();
            try
            {
                writeBatch.Put(key, DataEncoder.EncodeChainedHeader(chainedHeader));
                writeBatch.Put(MakeTotalWorkKey(chainedHeader.Hash, chainedHeader.TotalWork), new byte[1]);

                db.Write(WriteOptions.Default, writeBatch);
            }
            finally
            {
                writeBatch.Dispose();
            }

            return true;
        }
Example #4
0
 /// <summary>
 /// 将区块链的状态回滚到指定的位置
 /// </summary>
 /// <param name="hash">
 /// 要回滚到的区块的散列值
 /// </param>
 private void Rollback(UInt256 hash)
 {
     if (hash == current_block_hash) return;
     List<Block> blocks = new List<Block>();
     UInt256 current = current_block_hash;
     while (current != hash)
     {
         if (current == GenesisBlock.Hash)
             throw new InvalidOperationException();
         Block block = GetBlockInternal(current, ReadOptions.Default);
         blocks.Add(block);
         current = block.PrevBlock;
     }
     WriteBatch batch = new WriteBatch();
     foreach (Block block in blocks)
     {
         batch.Delete(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(block.Hash));
         foreach (Transaction tx in block.Transactions)
         {
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash));
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(tx.Hash));
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(tx.Hash));
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_AntShare).Add(tx.Hash));
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(tx.Hash));
             if (tx.Type == TransactionType.RegisterTransaction)
             {
                 RegisterTransaction reg_tx = (RegisterTransaction)tx;
                 batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Asset).Add(reg_tx.Hash));
             }
         }
     }
     HashSet<UInt256> tx_hashes = new HashSet<UInt256>(blocks.SelectMany(p => p.Transactions).Select(p => p.Hash));
     foreach (var group in blocks.SelectMany(p => p.Transactions).SelectMany(p => p.GetAllInputs()).GroupBy(p => p.PrevHash).Where(g => !tx_hashes.Contains(g.Key)))
     {
         Transaction tx = GetTransaction(group.Key, ReadOptions.Default);
         Slice value = new byte[0];
         db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(tx.Hash), out value);
         IEnumerable<ushort> indexes = value.ToArray().GetUInt16Array().Union(group.Select(p => p.PrevIndex));
         batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(tx.Hash), indexes.ToByteArray());
         TransactionInput[] antshares = group.Where(p => tx.Outputs[p.PrevIndex].AssetId == AntShare.Hash).ToArray();
         if (antshares.Length > 0)
         {
             value = new byte[0];
             db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_AntShare).Add(tx.Hash), out value);
             indexes = value.ToArray().GetUInt16Array().Union(antshares.Select(p => p.PrevIndex));
             batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_AntShare).Add(tx.Hash), indexes.ToByteArray());
         }
         switch (tx.Type)
         {
             case TransactionType.EnrollmentTransaction:
                 if (group.Any(p => p.PrevIndex == 0))
                 {
                     batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(tx.Hash), true);
                 }
                 break;
             case TransactionType.VotingTransaction:
                 {
                     TransactionInput[] votes = group.Where(p => tx.Outputs[p.PrevIndex].AssetId == AntShare.Hash).ToArray();
                     if (votes.Length > 0)
                     {
                         value = new byte[0];
                         db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(tx.Hash), out value);
                         indexes = value.ToArray().GetUInt16Array().Union(votes.Select(p => p.PrevIndex));
                         batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(tx.Hash), indexes.ToByteArray());
                     }
                 }
                 break;
         }
     }
     //回滚AntCoin的发行量
     {
         Fixed8 amount_in = blocks.SelectMany(p => p.Transactions).SelectMany(p => p.References.Values.Where(o => o.AssetId == Blockchain.AntCoin.Hash)).Sum(p => p.Value);
         Fixed8 amount_out = blocks.SelectMany(p => p.Transactions).SelectMany(p => p.Outputs.Where(o => o.AssetId == Blockchain.AntCoin.Hash)).Sum(p => p.Value);
         if (amount_in != amount_out)
         {
             batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_QuantityIssued).Add(AntCoin.Hash), (GetQuantityIssued(AntCoin.Hash) - (amount_out - amount_in)).GetData());
         }
     }
     foreach (var result in blocks.SelectMany(p => p.Transactions).Where(p => p.Type == TransactionType.IssueTransaction).SelectMany(p => p.GetTransactionResults()).Where(p => p.Amount < Fixed8.Zero).GroupBy(p => p.AssetId, (k, g) => new
     {
         AssetId = k,
         Amount = -g.Sum(p => p.Amount)
     }))
     {
         batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_QuantityIssued).Add(result.AssetId), (GetQuantityIssued(result.AssetId) - result.Amount).GetData());
     }
     current_block_hash = current;
     current_block_height -= (uint)blocks.Count;
     batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(current_block_hash).Add(current_block_height));
     db.Write(WriteOptions.Default, batch);
 }
Example #5
0
 private void Persist(Block block)
 {
     MultiValueDictionary<UInt256, ushort> unspents = new MultiValueDictionary<UInt256, ushort>(p =>
     {
         Slice value = new byte[0];
         db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(p), out value);
         return new HashSet<ushort>(value.ToArray().GetUInt16Array());
     });
     MultiValueDictionary<UInt256, ushort> unspent_antshares = new MultiValueDictionary<UInt256, ushort>(p =>
     {
         Slice value = new byte[0];
         db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_AntShare).Add(p), out value);
         return new HashSet<ushort>(value.ToArray().GetUInt16Array());
     });
     MultiValueDictionary<UInt256, ushort> unspent_votes = new MultiValueDictionary<UInt256, ushort>(p =>
     {
         Slice value = new byte[0];
         db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(p), out value);
         return new HashSet<ushort>(value.ToArray().GetUInt16Array());
     });
     Dictionary<UInt256, Fixed8> quantities = new Dictionary<UInt256, Fixed8>();
     WriteBatch batch = new WriteBatch();
     batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(block.Hash), block.Trim());
     foreach (Transaction tx in block.Transactions)
     {
         batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash), tx.ToArray());
         switch (tx.Type)
         {
             case TransactionType.IssueTransaction:
                 foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero))
                 {
                     if (quantities.ContainsKey(result.AssetId))
                     {
                         quantities[result.AssetId] -= result.Amount;
                     }
                     else
                     {
                         quantities.Add(result.AssetId, -result.Amount);
                     }
                 }
                 break;
             case TransactionType.EnrollmentTransaction:
                 {
                     EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx;
                     batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(tx.Hash), true);
                 }
                 break;
             case TransactionType.VotingTransaction:
                 unspent_votes.AddEmpty(tx.Hash);
                 for (ushort index = 0; index < tx.Outputs.Length; index++)
                 {
                     if (tx.Outputs[index].AssetId == AntShare.Hash)
                     {
                         unspent_votes.Add(tx.Hash, index);
                     }
                 }
                 break;
             case TransactionType.RegisterTransaction:
                 {
                     RegisterTransaction reg_tx = (RegisterTransaction)tx;
                     batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Asset).Add(reg_tx.Hash), true);
                 }
                 break;
         }
         unspents.AddEmpty(tx.Hash);
         unspent_antshares.AddEmpty(tx.Hash);
         for (ushort index = 0; index < tx.Outputs.Length; index++)
         {
             unspents.Add(tx.Hash, index);
             if (tx.Outputs[index].AssetId == AntShare.Hash)
             {
                 unspent_antshares.Add(tx.Hash, index);
             }
         }
     }
     foreach (TransactionInput input in block.Transactions.SelectMany(p => p.GetAllInputs()))
     {
         if (input.PrevIndex == 0)
         {
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(input.PrevHash));
         }
         unspents.Remove(input.PrevHash, input.PrevIndex);
         unspent_antshares.Remove(input.PrevHash, input.PrevIndex);
         unspent_votes.Remove(input.PrevHash, input.PrevIndex);
     }
     //统计AntCoin的发行量
     {
         Fixed8 amount_in = block.Transactions.SelectMany(p => p.References.Values.Where(o => o.AssetId == AntCoin.Hash)).Sum(p => p.Value);
         Fixed8 amount_out = block.Transactions.SelectMany(p => p.Outputs.Where(o => o.AssetId == AntCoin.Hash)).Sum(p => p.Value);
         if (amount_in != amount_out)
         {
             quantities.Add(AntCoin.Hash, amount_out - amount_in);
         }
     }
     foreach (var unspent in unspents)
     {
         if (unspent.Value.Count == 0)
         {
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(unspent.Key));
         }
         else
         {
             batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(unspent.Key), unspent.Value.ToByteArray());
         }
     }
     foreach (var unspent in unspent_antshares)
     {
         if (unspent.Value.Count == 0)
         {
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_AntShare).Add(unspent.Key));
         }
         else
         {
             batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_AntShare).Add(unspent.Key), unspent.Value.ToByteArray());
         }
     }
     foreach (var unspent in unspent_votes)
     {
         if (unspent.Value.Count == 0)
         {
             batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(unspent.Key));
         }
         else
         {
             batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(unspent.Key), unspent.Value.ToByteArray());
         }
     }
     foreach (var quantity in quantities)
     {
         batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_QuantityIssued).Add(quantity.Key), (GetQuantityIssued(quantity.Key) + quantity.Value).GetData());
     }
     current_block_hash = block.Hash;
     current_block_height = block.Hash == GenesisBlock.Hash ? 0 : current_block_height + 1;
     batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(block.Hash).Add(current_block_height));
     db.Write(WriteOptions.Default, batch);
 }
Example #6
0
 private void OnAddHeader(Block header)
 {
     if (header.PrevBlock == current_header_hash)
     {
         current_header_hash = header.Hash;
         header_index.Add(header.Hash);
         uint height = header_chain.Nodes[current_header_hash].Height;
         if (height % 2000 == 0)
         {
             WriteBatch batch = new WriteBatch();
             while (height - 2000 > stored_header_count)
             {
                 using (MemoryStream ms = new MemoryStream())
                 using (BinaryWriter w = new BinaryWriter(ms))
                 {
                     w.Write(header_index.Skip((int)stored_header_count).Take(2000).Select(p => header_chain[p]).ToArray());
                     w.Flush();
                     batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_HeaderList).Add(stored_header_count), ms.ToArray());
                 }
                 stored_header_count += 2000;
             }
             db.Write(WriteOptions.Default, batch);
         }
     }
     else
     {
         TreeNode<Block> main = header_chain.Leaves.OrderByDescending(p => p.Height).First();
         if (main.Item.Hash != current_header_hash)
         {
             TreeNode<Block> fork = header_chain.Nodes[current_header_hash];
             current_header_hash = main.Item.Hash;
             TreeNode<Block> common = header_chain.FindCommonNode(main, fork);
             header_index.RemoveRange((int)common.Height + 1, header_index.Count - (int)common.Height - 1);
             for (TreeNode<Block> i = main; i != common; i = i.Parent)
             {
                 header_index.Insert((int)common.Height + 1, i.Item.Hash);
             }
             if (header_chain.Nodes[current_block_hash].Height > common.Height)
             {
                 Rollback(common.Item.Hash);
             }
         }
     }
 }
Example #7
0
 /// <summary>
 /// Put the batch of keys and values in the storage.
 /// </summary>
 /// <param name="batch">Batch of keys and values.</param>
 public void PutBatch(Dictionary<string, string> batch)
 {
     var writeBatch = new WriteBatch();
     foreach (var column in batch)
     {
         writeBatch.Put(column.Key, column.Value);
     }
     writeBatch.Commit(db);
 }
Example #8
0
        public void Batch_PutToDB(LevelDB.WriteBatch batch, LevelDB.DB db, byte[] key)
        {
            var _value = Helper.tagValue_Bytes.Concat(this.Value).ToArray();

            batch.Put(key, _value);
        }
Example #9
0
 protected override void AddInternal(TKey key, TValue value)
 {
     batch?.Put(prefix, key, value);
 }
        private bool TryRemoveBlockInner(UInt256 blockHash, WriteBatch writeBatch)
        {
            // lock all when performing the block write, so count update is thread-safe
            lock (countLock)
            {
                // get the current count
                int count;
                Slice countSlice;
                if (db.TryGet(ReadOptions.Default, COUNT_KEY, out countSlice))
                    count = countSlice.ToInt32();
                else
                    count = 0;

                // check if block exists before writing
                var existsKey = MakeExistsKey(blockHash);
                Slice existsSlice;
                if (db.TryGet(ReadOptions.Default, existsKey, out existsSlice))
                {
                    // update count and remove block existence key
                    writeBatch.Put(COUNT_KEY, count - 1);
                    writeBatch.Delete(existsKey);

                    db.Write(WriteOptions.Default, writeBatch);

                    return true;
                }
                else
                    return false;
            }
        }
        public bool TryAddBlockTransactions(UInt256 blockHash, IEnumerable<EncodedTx> blockTxes)
        {
            if (ContainsBlock(blockHash))
                return false;

            var writeBatch = new WriteBatch();
            try
            {
                int txCount;
                using (var snapshot = db.GetSnapshot())
                {
                    var readOptions = new ReadOptions { Snapshot = snapshot };

                    var txIndex = 0;
                    foreach (var tx in blockTxes)
                    {
                        var key = DbEncoder.EncodeBlockHashTxIndex(blockHash, txIndex);

                        Slice existingValue;
                        if (db.TryGet(readOptions, key, out existingValue))
                            return false;

                        var blockTx = new BlockTx(txIndex, tx);
                        var value = DataEncoder.EncodeBlockTxNode(blockTx);

                        writeBatch.Put(key, value);

                        txIndex++;
                    }

                    txCount = txIndex;
                }

                return TryAddBlockInner(blockHash, txCount, writeBatch);
            }
            finally
            {
                writeBatch.Dispose();
            }
        }