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 void PruneElements(IEnumerable<KeyValuePair<UInt256, IEnumerable<int>>> blockTxIndices) { using (var snapshot = db.GetSnapshot()) { var readOptions = new ReadOptions { Snapshot = snapshot }; using (var iterator = db.NewIterator(readOptions)) { foreach (var keyPair in blockTxIndices) { var blockHash = keyPair.Key; var txIndices = keyPair.Value; var pruningCursor = new LevelDbMerkleTreePruningCursor(blockHash, iterator); // prune the transactions foreach (var index in txIndices) { var cachedCursor = new CachedMerkleTreePruningCursor<BlockTxNode>(pruningCursor); MerkleTree.PruneNode(cachedCursor, index); } var writeBatch = pruningCursor.CreateWriteBatch(); try { db.Write(new WriteOptions(), writeBatch); } finally { writeBatch.Dispose(); } } } } }
public override IEnumerable<RegisterTransaction> GetAssets() { yield return AntCoin; ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { foreach (Slice key in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_Asset), (k, v) => k)) { UInt256 hash = new UInt256(key.ToArray().Skip(1).ToArray()); yield return (RegisterTransaction)GetTransaction(hash, options); } } }
public void Snapshot() { // modify db Database.Put ("key1", "value1"); // create snapshot var snapshot = Database.CreateSnapshot (); // modify db again Database.Put ("key2", "value2"); // read from snapshot var readOptions = new ReadOptions () { Snapshot = snapshot }; var val1 = Database.Get (readOptions, "key1"); Assert.AreEqual ("value1", val1); var val2 = Database.Get (readOptions, "key2"); Assert.IsNull (val2); // read from non-snapshot readOptions.Snapshot = null; val1 = Database.Get (readOptions, "key1"); Assert.AreEqual ("value1", val1); val2 = Database.Get (readOptions, "key2"); Assert.AreEqual ("value2", val2); // release snapshot // GC calls ~Snapshot() for us }
public void Get() { Database.Put (null, "key1", "value1"); var value1 = Database.Get (null, "key1"); Assert.AreEqual ("value1", value1); Database.Put (null, "key2", "value2"); var value2 = Database.Get (null, "key2"); Assert.AreEqual ("value2", value2); Database.Put (null, "key3", "value3"); var value3 = Database.Get (null, "key3"); Assert.AreEqual ("value3", value3); // verify checksum var options = new ReadOptions () { VerifyChecksums = true }; value1 = Database.Get (options, "key1"); Assert.AreEqual ("value1", value1); // no fill cache options = new ReadOptions () { FillCache = false }; value2 = Database.Get (options, "key2"); Assert.AreEqual ("value2", value2); }
public ChainedHeader FindMaxTotalWork() { var maxTotalWork = BigInteger.Zero; var candidateHeaders = new List<ChainedHeader>(); using (var snapshot = db.GetSnapshot()) { var readOptions = new ReadOptions { Snapshot = snapshot }; using (var iterator = db.NewIterator(readOptions)) { // totalWork will be sorted lowest to highest iterator.SeekToLast(); while (iterator.Valid()) { var key = iterator.Key().ToArray(); if (key[0] != TOTAL_WORK_PREFIX) break; UInt256 blockHash; BigInteger totalWork; DecodeTotalWorkKey(key, out blockHash, out totalWork); // check if this block is valid bool isValid; var blockInvalidKey = MakeBlockInvalidKey(blockHash); Slice blockInvalidSlice; if (db.TryGet(readOptions, blockInvalidKey, out blockInvalidSlice)) { var blockInvalidBytes = blockInvalidSlice.ToArray(); isValid = !(blockInvalidBytes.Length == 1 && blockInvalidBytes[0] == 1); } else isValid = true; if (isValid) { var headerKey = MakeHeaderKey(blockHash); Slice headerSlice; if (db.TryGet(readOptions, headerKey, out headerSlice)) { // decode chained header var chainedHeader = DataDecoder.DecodeChainedHeader(headerSlice.ToArray()); // initialize max total work, if it isn't yet if (maxTotalWork == BigInteger.Zero) maxTotalWork = chainedHeader.TotalWork; if (chainedHeader.TotalWork > maxTotalWork) { maxTotalWork = chainedHeader.TotalWork; candidateHeaders = new List<ChainedHeader>(); } // add this header as a candidate if it ties the max total work if (chainedHeader.TotalWork >= maxTotalWork) candidateHeaders.Add(chainedHeader); else break; } } iterator.Prev(); } } } // take the earliest header seen with the max total work candidateHeaders.Sort((left, right) => left.DateSeen.CompareTo(right.DateSeen)); return candidateHeaders.FirstOrDefault(); }
public override IEnumerable<Vote> GetVotes(IEnumerable<Transaction> others) { ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { foreach (var kv in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_Vote), (k, v) => new { Key = k, Value = v })) { UInt256 hash = new UInt256(kv.Key.ToArray().Skip(1).ToArray()); ushort[] indexes = kv.Value.ToArray().GetUInt16Array().Except(others.SelectMany(p => p.GetAllInputs()).Where(p => p.PrevHash == hash).Select(p => p.PrevIndex)).ToArray(); if (indexes.Length == 0) continue; VotingTransaction tx = (VotingTransaction)GetTransaction(hash, options); yield return new Vote { Enrollments = tx.Enrollments, Count = indexes.Sum(p => tx.Outputs[p].Value) }; } } foreach (VotingTransaction tx in others.OfType<VotingTransaction>()) { yield return new Vote { Enrollments = tx.Enrollments, Count = tx.Outputs.Where(p => p.AssetId == AntShare.Hash).Sum(p => p.Value) }; } }
public override IEnumerable<TransactionOutput> GetUnspentAntShares() { ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { foreach (var kv in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_AntShare), (k, v) => new { Key = k, Value = v })) { UInt256 hash = new UInt256(kv.Key.ToArray().Skip(1).ToArray()); ushort[] indexes = kv.Value.ToArray().GetUInt16Array(); Transaction tx = GetTransaction(hash, options); foreach (ushort index in indexes) { yield return tx.Outputs[index]; } } } }
private Transaction GetTransaction(UInt256 hash, ReadOptions options) { Slice value; if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value)) return null; return Transaction.DeserializeFrom(value.ToArray()); }
private IEnumerator<BlockTxNode> ReadBlockTransactions(UInt256 blockHash, bool requireTx) { 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) yield break; var value = iterator.Value().ToArray(); var blockTxNode = DataDecoder.DecodeBlockTxNode(value); if (blockTxNode.Pruned && requireTx) throw new MissingDataException(blockHash); yield return blockTxNode; iterator.Next(); } } } }
private UInt256 GetNextBlockHash(UInt256 hash, ReadOptions options) { Slice value; if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.IX_PrevBlock).Add(hash), out value)) return null; return new UInt256(value.ToArray()); }
private Block GetBlockAndHeight(UInt256 hash, ReadOptions options, out uint height) { Slice value; if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) { height = 0; return null; } byte[] data = value.ToArray(); height = BitConverter.ToUInt32(data, 0); return Block.FromTrimmedData(data, sizeof(uint), p => GetTransaction(p, options)); }
public override Block GetNextBlock(UInt256 hash) { ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { hash = GetNextBlockHash(hash, options); uint height; return GetBlockAndHeight(hash, options, out height); } }
public Iterator NewIterator(ReadOptions options) { return(new Iterator(LevelDBInterop.leveldb_create_iterator(Handle, options.Handle))); }
public IEnumerable<ChainedHeader> ReadChainedHeaders() { using (var snapshot = db.GetSnapshot()) { var readOptions = new ReadOptions { Snapshot = snapshot }; using (var iterator = db.NewIterator(readOptions)) { iterator.SeekToFirst(); while (iterator.Valid() && iterator.Key().ToArray()[0] == HEADER_PREFIX) { var chainedHeader = DataDecoder.DecodeChainedHeader(iterator.Value().ToArray()); yield return chainedHeader; iterator.Next(); } } } }
private Block GetBlockInternal(UInt256 hash, ReadOptions options) { Slice value; if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash), out value)) return null; return Block.FromTrimmedData(value.ToArray(), 0, p => GetTransaction(p, options)); }
public override IEnumerable<EnrollmentTransaction> GetEnrollments(IEnumerable<Transaction> others) { ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { foreach (Slice key in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment), (k, v) => k)) { UInt256 hash = new UInt256(key.ToArray().Skip(1).Take(32).ToArray()); if (others.SelectMany(p => p.GetAllInputs()).Any(p => p.PrevHash == hash && p.PrevIndex == 0)) continue; yield return (EnrollmentTransaction)GetTransaction(hash, options); } } foreach (EnrollmentTransaction tx in others.OfType<EnrollmentTransaction>()) { yield return tx; } }
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 override TransactionOutput GetUnspent(UInt256 hash, ushort index) { TransactionOutput unspent = base.GetUnspent(hash, index); if (unspent != null) return unspent; ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { Slice value; if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(hash), out value)) return null; if (!value.ToArray().GetUInt16Array().Contains(index)) return null; return GetTransaction(hash, options).Outputs[index]; } }
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 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; }
/// <summary> /// If the database contains an entry for "key" return the value, /// otherwise return null. /// </summary> public string Get(string key, ReadOptions options) { var value = Get(Encoding.UTF8.GetBytes(key), options); return(value != null?Encoding.UTF8.GetString(value) : null); }
public override bool IsDoubleSpend(Transaction tx) { TransactionInput[] inputs = tx.GetAllInputs().ToArray(); if (inputs.Length == 0) return false; lock (MemoryPool) { if (MemoryPool.Values.SelectMany(p => p.GetAllInputs()).Intersect(inputs).Count() > 0) return true; } ReadOptions options = new ReadOptions(); using (options.Snapshot = db.GetSnapshot()) { foreach (var group in inputs.GroupBy(p => p.PrevHash)) { Slice value; if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(group.Key), out value)) return true; HashSet<ushort> unspents = new HashSet<ushort>(value.ToArray().GetUInt16Array()); if (group.Any(p => !unspents.Contains(p.PrevIndex))) return true; } } return false; }
/// <summary> /// Return an iterator over the contents of the database. /// The result of CreateIterator is initially invalid (caller must /// call one of the Seek methods on the iterator before using it). /// </summary> public Iterator CreateIterator(ReadOptions options) { return(new Iterator(LevelDBInterop.leveldb_create_iterator(this.Handle, options.Handle), _encoding)); }