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); }
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; }
public LevelDBBlockchain(string path) { header_index.Add(GenesisBlock.Hash); Version version; Slice value; db = DB.Open(path, new Options { CreateIfMissing = true }); if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.CFG_Version), out value) && Version.TryParse(value.ToString(), out version) && version >= Version.Parse("0.4")) { ReadOptions options = new ReadOptions { FillCache = false }; value = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock)); this.current_block_hash = new UInt256(value.ToArray().Take(32).ToArray()); this.current_block_height = BitConverter.ToUInt32(value.ToArray(), 32); foreach (Block header in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.DATA_HeaderList), (k, v) => { using (MemoryStream ms = new MemoryStream(v.ToArray(), false)) using (BinaryReader r = new BinaryReader(ms)) { return new { Index = BitConverter.ToUInt32(k.ToArray(), 1), Headers = r.ReadSerializableArray<Block>() }; } }).OrderBy(p => p.Index).SelectMany(p => p.Headers).ToArray()) { if (header.Hash != GenesisBlock.Hash) { header_chain.Add(header.Hash, header, header.PrevBlock); header_index.Add(header.Hash); } stored_header_count++; } if (stored_header_count == 0) { Dictionary<UInt256, Block> table = db.Find(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block), (k, v) => Block.FromTrimmedData(v.ToArray(), 0)).ToDictionary(p => p.PrevBlock); for (UInt256 hash = GenesisBlock.Hash; hash != current_block_hash;) { Block header = table[hash]; header_chain.Add(header.Hash, header, header.PrevBlock); header_index.Add(header.Hash); hash = header.Hash; } } else if (current_block_height >= stored_header_count) { List<Block> list = new List<Block>(); for (UInt256 hash = current_block_hash; hash != header_index[(int)stored_header_count - 1];) { Block header = Block.FromTrimmedData(db.Get(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash)).ToArray(), 0); list.Add(header); header_index.Insert((int)stored_header_count, hash); hash = header.PrevBlock; } for (int i = list.Count - 1; i >= 0; i--) { header_chain.Add(list[i].Hash, list[i], list[i].PrevBlock); } } this.current_header_hash = header_index[header_index.Count - 1]; } 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()); } } db.Write(WriteOptions.Default, batch); Persist(GenesisBlock); db.Put(WriteOptions.Default, SliceBuilder.Begin(DataEntryPrefix.CFG_Version), Assembly.GetExecutingAssembly().GetName().Version.ToString()); } thread_persistence = new Thread(PersistBlocks); thread_persistence.Name = "LevelDBBlockchain.PersistBlocks"; thread_persistence.Start(); AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; }
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); } } } }
/// <summary> /// Delete the batch of keys. /// </summary> /// <param name="batch">Batch of keys to delete.</param> public void DeleteBatch(IEnumerable<string> batch) { var writeBatch = new WriteBatch(); foreach (var key in batch) { writeBatch.Delete(key); } writeBatch.Commit(db); }
public void WriteBatch() { Database.Put ("key1", "value1"); var writeBatch = new WriteBatch (). Delete ("key1"). Put ("key2", "value2"); Database.Write (writeBatch); var value1 = Database.Get ("key1"); Assert.IsNull (value1); var value2 = Database.Get ("key2"); Assert.AreEqual ("value2", value2); writeBatch.Delete ("key2").Clear (); Database.Write (writeBatch); value2 = Database.Get ("key2"); Assert.AreEqual ("value2", value2); }
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); }
/// <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); }
public bool TryRemoveChainedHeader(UInt256 blockHash) { var key = MakeHeaderKey(blockHash); Slice existingValue; if (!db.TryGet(ReadOptions.Default, key, out existingValue)) return false; var chainedHeader = DataDecoder.DecodeChainedHeader(existingValue.ToArray()); var writeBatch = new WriteBatch(); try { writeBatch.Delete(key); writeBatch.Delete(MakeTotalWorkKey(blockHash, chainedHeader.TotalWork)); db.Write(WriteOptions.Default, writeBatch); } finally { writeBatch.Dispose(); } return true; }
public void Write(WriteBatch batch) { Write(batch, new WriteOptions()); }
public void DeleteElements(IEnumerable<KeyValuePair<UInt256, IEnumerable<int>>> blockTxIndices) { var writeBatch = new WriteBatch(); try { foreach (var keyPair in blockTxIndices) { var blockHash = keyPair.Key; var txIndices = keyPair.Value; // prune the transactions foreach (var index in txIndices) { writeBatch.Delete(DbEncoder.EncodeBlockHashTxIndex(blockHash, index)); } } db.Write(new WriteOptions(), writeBatch); } finally { writeBatch.Dispose(); } }
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); }
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 TryRemoveBlockTransactions(UInt256 blockHash) { if (!ContainsBlock(blockHash)) return false; var writeBatch = new WriteBatch(); try { using (var snapshot = db.GetSnapshot()) { var readOptions = new ReadOptions { Snapshot = snapshot }; using (var iterator = db.NewIterator(readOptions)) { iterator.Seek(DbEncoder.EncodeBlockHashTxIndex(blockHash, 0)); while (iterator.Valid()) { var key = iterator.Key().ToArray(); UInt256 iteratorBlockHash; int txIndex; DbEncoder.DecodeBlockHashTxIndex(key, out iteratorBlockHash, out txIndex); if (iteratorBlockHash != blockHash) break; writeBatch.Delete(key); iterator.Next(); } } } return TryRemoveBlockInner(blockHash, writeBatch); } finally { writeBatch.Dispose(); } }
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(); } }
public void Write(WriteBatch writeBatch) { Write(null, writeBatch); }
public void Write(WriteBatch batch) { Write(batch, _writeOptions); }
/// <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); }
public DbCache(DB db, byte prefix, WriteBatch batch = null) { this.db = db; this.batch = batch; this.prefix = prefix; }