/// <inheritdoc cref="BaseStore.AppendIndex(Guid, BlockHash)"/> public override long AppendIndex(Guid chainId, BlockHash hash) { long index = CountIndex(chainId); try { byte[] indexBytes = RocksDBStoreBitConverter.GetBytes(index); byte[] key = IndexKeyPrefix.Concat(indexBytes).ToArray(); ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); using var writeBatch = new WriteBatch(); writeBatch.Put(key, hash.ToByteArray(), cf); writeBatch.Put(IndexCountKey, RocksDBStoreBitConverter.GetBytes(index + 1), cf); _chainDb.Write(writeBatch); } catch (Exception e) { LogUnexpectedException(nameof(AppendIndex), e); } return(index); }
/// <inheritdoc/> public override bool DeleteBlock(HashDigest <SHA256> blockHash) { byte[] key = BlockKey(blockHash); if (!(_blockIndexDb.Get(key) is byte[] blockDbNameByte)) { return(false); } _rwBlockLock.EnterWriteLock(); try { string blockDbName = RocksDBStoreBitConverter.GetString(blockDbNameByte); if (!_blockDbCache.TryGetValue(blockDbName, out RocksDb blockDb)) { blockDb = RocksDBUtils.OpenRocksDb(_options, BlockDbPath(blockDbName)); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } _blockCache.Remove(blockHash); _blockIndexDb.Remove(key); blockDb.Remove(key); return(true); } finally { _rwBlockLock.ExitWriteLock(); } }
/// <inheritdoc/> public override IEnumerable <Tuple <HashDigest <SHA256>, long> > IterateStateReferences( Guid chainId, string key, long?highestIndex, long?lowestIndex, int?limit) { highestIndex ??= long.MaxValue; lowestIndex ??= 0; limit ??= int.MaxValue; if (highestIndex < lowestIndex) { var message = $"highestIndex({highestIndex}) must be greater than or equal to " + $"lowestIndex({lowestIndex})"; throw new ArgumentException( message, nameof(highestIndex)); } byte[] keyBytes = RocksDBStoreBitConverter.GetBytes(key); byte[] prefix = StateRefKeyPrefix.Concat(keyBytes).ToArray(); return(IterateStateReferences( chainId, prefix, highestIndex.Value, lowestIndex.Value, limit.Value)); }
/// <inheritdoc/> public override BlockDigest?GetBlockDigest(HashDigest <SHA256> blockHash) { if (_blockCache.TryGetValue(blockHash, out BlockDigest cachedDigest)) { return(cachedDigest); } byte[] key = BlockKey(blockHash); if (!(_blockIndexDb.Get(key) is byte[] blockDbNameBytes)) { return(null); } _rwBlockLock.EnterReadLock(); try { string blockDbName = RocksDBStoreBitConverter.GetString(blockDbNameBytes); if (!_blockDbCache.TryGetValue(blockDbName, out RocksDb blockDb)) { blockDb = RocksDBUtils.OpenRocksDb(_options, BlockDbPath(blockDbName)); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } byte[] blockBytes = blockDb.Get(key); BlockDigest blockDigest = BlockDigest.Deserialize(blockBytes); _blockCache.AddOrUpdate(blockHash, blockDigest); return(blockDigest); } finally { _rwBlockLock.ExitReadLock(); } }
/// <inheritdoc/> public override bool DeleteTransaction(TxId txid) { byte[] key = TxKey(txid); if (!(_txIndexDb.Get(key) is byte[] txDbNameBytes)) { return(false); } _rwTxLock.EnterWriteLock(); try { string txDbName = RocksDBStoreBitConverter.GetString(txDbNameBytes); if (!_txDbCache.TryGetValue(txDbName, out RocksDb txDb)) { txDb = RocksDBUtils.OpenRocksDb(_options, TxDbPath(txDbName)); _txDbCache.AddOrUpdate(txDbName, txDb); } _txCache.Remove(txid); _txIndexDb.Remove(key); txDb.Remove(key); return(true); } finally { _rwTxLock.ExitWriteLock(); } }
/// <inheritdoc/> public override void PutTransaction <T>(Transaction <T> tx) { if (_txCache.ContainsKey(tx.Id)) { return; } byte[] key = TxKey(tx.Id); if (!(_txIndexDb.Get(key) is null)) { return; } long timestamp = tx.Timestamp.ToUnixTimeSeconds(); string txDbName = $"epoch{(int)timestamp / _txEpochUnitSeconds}"; _rwTxLock.EnterWriteLock(); try { if (!_txDbCache.TryGetValue(txDbName, out RocksDb txDb)) { txDb = RocksDBUtils.OpenRocksDb(_options, TxDbPath(txDbName)); _txDbCache.AddOrUpdate(txDbName, txDb); } txDb.Put(key, tx.Serialize(true)); _txIndexDb.Put(key, RocksDBStoreBitConverter.GetBytes(txDbName)); _txCache.AddOrUpdate(tx.Id, tx); } finally { _rwTxLock.ExitWriteLock(); } }
/// <inheritdoc/> public override long CountIndex(Guid chainId) { ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); byte[] bytes = _chainDb.Get(IndexCountKey, cf); return(bytes is null ? 0 : RocksDBStoreBitConverter.ToInt64(bytes)); }
/// <inheritdoc/> public override void IncreaseTxNonce(Guid chainId, Address signer, long delta = 1) { ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); long nextNonce = GetTxNonce(chainId, signer) + delta; byte[] key = TxNonceKey(signer); byte[] bytes = RocksDBStoreBitConverter.GetBytes(nextNonce); _chainDb.Put(key, bytes, cf); }
private (Guid, long)? GetPreviousChainInfo(ColumnFamilyHandle cf) { if (_chainDb.Get(PreviousChainIdKey, cf) is { } prevChainId&& _chainDb.Get(PreviousChainIndexKey, cf) is { } prevChainIndex) { return(new Guid(prevChainId), RocksDBStoreBitConverter.ToInt64(prevChainIndex)); } return(null); }
private byte[] StateRefKey(string stateKey, long blockIndex) { byte[] stateKeyBytes = RocksDBStoreBitConverter.GetBytes(stateKey); byte[] blockIndexBytes = RocksDBStoreBitConverter.GetBytes(blockIndex); return(StateRefKeyPrefix .Concat(stateKeyBytes) .Concat(blockIndexBytes) .ToArray()); }
/// <inheritdoc/> public override long GetTxNonce(Guid chainId, Address address) { ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); byte[] key = TxNonceKey(address); byte[] bytes = _chainDb.Get(key, cf); return(bytes is null ? 0 : RocksDBStoreBitConverter.ToInt64(bytes)); }
/// <inheritdoc/> public override void ForkBlockIndexes( Guid sourceChainId, Guid destinationChainId, HashDigest <SHA256> branchPoint) { HashDigest <SHA256>?genesisHash = IterateIndexes(sourceChainId, 0, 1) .Cast <HashDigest <SHA256>?>() .FirstOrDefault(); if (genesisHash is null || branchPoint.Equals(genesisHash)) { return; } ColumnFamilyHandle cf = GetColumnFamily(_chainDb, destinationChainId); var writeBatch = new WriteBatch(); long index = 0; try { foreach (Iterator it in IterateDb(_chainDb, IndexKeyPrefix, sourceChainId)) { byte[] hashBytes = it.Value(); writeBatch.Put(it.Key(), hashBytes, cf); index += 1; if (writeBatch.Count() >= ForkWriteBatchSize) { _chainDb.Write(writeBatch); writeBatch.Dispose(); writeBatch = new WriteBatch(); } if (branchPoint.ToByteArray().SequenceEqual(hashBytes)) { break; } } } finally { _chainDb.Write(writeBatch); writeBatch.Dispose(); } _chainDb.Put( IndexCountKey, RocksDBStoreBitConverter.GetBytes(index), cf ); }
/// <inheritdoc/> public override void PutBlock <T>(Block <T> block) { if (_blockCache.ContainsKey(block.Hash)) { return; } byte[] key = BlockKey(block.Hash); if (!(_blockIndexDb.Get(key) is null)) { return; } long timestamp = block.Timestamp.ToUnixTimeSeconds(); foreach (Transaction <T> tx in block.Transactions) { PutTransaction(tx); } _rwBlockLock.EnterWriteLock(); try { string blockDbName = $"epoch{timestamp / _blockEpochUnitSeconds}"; RocksDb blockDb; lock (_blockDbCache) { if (!_blockDbCache.TryGetValue(blockDbName, out blockDb)) { blockDb = RocksDBUtils.OpenRocksDb(_options, BlockDbPath(blockDbName)); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } } BlockDigest digest = BlockDigest.FromBlock(block); byte[] value = digest.Serialize(); blockDb.Put(key, value); _blockIndexDb.Put(key, RocksDBStoreBitConverter.GetBytes(blockDbName)); _blockCache.AddOrUpdate(block.Hash, digest); } catch (Exception e) { LogUnexpectedException(nameof(PutBlock), e); } finally { _rwBlockLock.ExitWriteLock(); } }
/// <inheritdoc/> public override IEnumerable <KeyValuePair <Address, long> > ListTxNonces(Guid chainId) { byte[] prefix = TxNonceKeyPrefix; foreach (Iterator it in IterateDb(_chainDb, prefix, chainId)) { byte[] addressBytes = it.Key() .Skip(prefix.Length) .ToArray(); var address = new Address(addressBytes); long nonce = RocksDBStoreBitConverter.ToInt64(it.Value()); yield return(new KeyValuePair <Address, long>(address, nonce)); } }
/// <inheritdoc/> public override void IncreaseTxNonce(Guid chainId, Address signer, long delta = 1) { try { ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); long nextNonce = GetTxNonce(chainId, signer) + delta; byte[] key = TxNonceKey(signer); byte[] bytes = RocksDBStoreBitConverter.GetBytes(nextNonce); _chainDb.Put(key, bytes, cf); } catch (Exception e) { LogUnexpectedException(nameof(IncreaseTxNonce), e); } }
/// <inheritdoc/> public override long CountIndex(Guid chainId) { try { ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); byte[] bytes = _chainDb.Get(IndexCountKey, cf); return(bytes is null ? 0 : RocksDBStoreBitConverter.ToInt64(bytes)); } catch (Exception e) { LogUnexpectedException(nameof(CountIndex), e); } return(0); }
/// <inheritdoc/> public override Transaction <T> GetTransaction <T>(TxId txid) { if (_txCache.TryGetValue(txid, out object cachedTx)) { return((Transaction <T>)cachedTx); } byte[] key = TxKey(txid); if (!(_txIndexDb.Get(key) is byte[] txDbNameBytes)) { return(null); } string txDbName = RocksDBStoreBitConverter.GetString(txDbNameBytes); _rwTxLock.EnterReadLock(); try { RocksDb txDb; lock (_txDbCache) { if (!_txDbCache.TryGetValue(txDbName, out txDb)) { txDb = RocksDBUtils.OpenRocksDb(_options, TxDbPath(txDbName)); _txDbCache.AddOrUpdate(txDbName, txDb); } } byte[] txBytes = txDb.Get(key); Transaction <T> tx = Transaction <T> .Deserialize(txBytes, false); _txCache.AddOrUpdate(txid, tx); return(tx); } catch (Exception e) { LogUnexpectedException(nameof(GetTransaction), e); return(null); } finally { _rwTxLock.ExitReadLock(); } }
/// <inheritdoc/> public override long AppendIndex(Guid chainId, HashDigest <SHA256> hash) { long index = CountIndex(chainId); byte[] indexBytes = RocksDBStoreBitConverter.GetBytes(index); byte[] key = IndexKeyPrefix.Concat(indexBytes).ToArray(); ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); using var writeBatch = new WriteBatch(); writeBatch.Put(key, hash.ToByteArray(), cf); writeBatch.Put(IndexCountKey, RocksDBStoreBitConverter.GetBytes(index + 1), cf); _chainDb.Write(writeBatch); return(index); }
/// <inheritdoc/> public override long GetTxNonce(Guid chainId, Address address) { try { ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); byte[] key = TxNonceKey(address); byte[] bytes = _chainDb.Get(key, cf); return(bytes is null ? 0 : RocksDBStoreBitConverter.ToInt64(bytes)); } catch (Exception e) { LogUnexpectedException(nameof(GetTxNonce), e); } return(0); }
ListAllStateReferences( Guid chainId, long lowestIndex = 0, long highestIndex = long.MaxValue) { byte[] prefix = StateRefKeyPrefix; var stateRefs = new List <StateRef>(); foreach (Iterator it in IterateDb(_stateRefDb, prefix, chainId)) { byte[] key = it.Key(); int stateKeyLength = key.Length - sizeof(long) - prefix.Length; byte[] stateKeyBytes = key.Skip(prefix.Length).Take(stateKeyLength).ToArray(); string stateKey = RocksDBStoreBitConverter.GetString(stateKeyBytes); byte[] indexBytes = key.Skip(prefix.Length + stateKeyLength).ToArray(); long index = RocksDBStoreBitConverter.ToInt64(indexBytes); if (index < lowestIndex || index > highestIndex) { continue; } var hash = new HashDigest <SHA256>(it.Value()); var stateRef = new StateRef { StateKey = stateKey, BlockHash = hash, BlockIndex = index, }; stateRefs.Add(stateRef); } return(stateRefs .GroupBy(stateRef => stateRef.StateKey) .ToImmutableDictionary( g => g.Key, g => (IImmutableList <HashDigest <SHA256> >)g .Select(r => r.BlockHash).ToImmutableList() )); }
/// <inheritdoc cref="BaseStore.ForkBlockIndexes(Guid, Guid, BlockHash)"/> public override void ForkBlockIndexes( Guid sourceChainId, Guid destinationChainId, BlockHash branchpoint ) { BlockHash?genesisHash = IterateIndexes(sourceChainId, 0, 1).FirstOrDefault(); if (genesisHash is null || branchpoint.Equals(genesisHash)) { return; } ColumnFamilyHandle srcCf = GetColumnFamily(_chainDb, sourceChainId); ColumnFamilyHandle destCf = GetColumnFamily(_chainDb, destinationChainId); foreach (Iterator k in IterateDb(_chainDb, IndexKeyPrefix, destinationChainId)) { _chainDb.Remove(k.Key(), destCf); } long bpIndex = GetBlockIndex(branchpoint).Value; if (GetPreviousChainInfo(srcCf) is { } chainInfo&& chainInfo.Item2 == bpIndex) { ForkBlockIndexes(chainInfo.Item1, destinationChainId, branchpoint); return; } _chainDb.Put(PreviousChainIdKey, sourceChainId.ToByteArray(), destCf); _chainDb.Put( PreviousChainIndexKey, RocksDBStoreBitConverter.GetBytes(bpIndex), destCf ); _chainDb.Put( IndexCountKey, RocksDBStoreBitConverter.GetBytes(bpIndex + 1), destCf ); AddFork(srcCf, destinationChainId); }
private IEnumerable <Tuple <HashDigest <SHA256>, long> > IterateStateReferences( Guid chainId, byte[] prefix, long highestIndex, long lowestIndex, int limit) { ColumnFamilyHandle cf = GetColumnFamily(_stateRefDb, chainId); using Iterator it = _stateRefDb.NewIterator(cf); // FIXME: We need to change the state reference to be ordered by reverse byte-wise // and use the Seek function. it.SeekToLast(); while (it.Valid() && !it.Key().StartsWith(prefix)) { it.Prev(); } for (; it.Valid() && it.Key().StartsWith(prefix); it.Prev()) { byte[] indexBytes = it.Key().Skip(prefix.Length).ToArray(); long index = RocksDBStoreBitConverter.ToInt64(indexBytes); if (index > highestIndex) { continue; } if (index < lowestIndex || limit <= 0) { break; } byte[] hashBytes = it.Value(); var hash = new HashDigest <SHA256>(hashBytes); yield return(new Tuple <HashDigest <SHA256>, long>(hash, index)); limit--; } }
/// <inheritdoc/> public override IEnumerable <string> ListStateKeys(Guid chainId) { byte[] prefix = StateRefKeyPrefix; var prevStateKey = string.Empty; foreach (Iterator it in IterateDb(_stateRefDb, prefix, chainId)) { byte[] key = it.Key(); int stateKeyLength = key.Length - sizeof(long) - prefix.Length; byte[] stateKeyBytes = key.Skip(prefix.Length).Take(stateKeyLength).ToArray(); string stateKey = RocksDBStoreBitConverter.GetString(stateKeyBytes); if (stateKey != prevStateKey) { yield return(stateKey); prevStateKey = stateKey; } } }
/// <inheritdoc cref="BaseStore.IndexBlockHash(Guid, long)"/> public override BlockHash?IndexBlockHash(Guid chainId, long index) { if (index < 0) { index += CountIndex(chainId); if (index < 0) { return(null); } } ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); byte[] indexBytes = RocksDBStoreBitConverter.GetBytes(index); byte[] key = IndexKeyPrefix.Concat(indexBytes).ToArray(); byte[] bytes = _chainDb.Get(key, cf); return(bytes is null ? (BlockHash?)null : new BlockHash(bytes)); }
private BlockHash?IndexBlockHash(Guid chainId, long index, bool includeDeleted) { try { if (index < 0) { index += CountIndex(chainId); if (index < 0) { return(null); } } ColumnFamilyHandle cf = GetColumnFamily(_chainDb, chainId); if (!includeDeleted && IsDeletionMarked(cf)) { return(null); } if (GetPreviousChainInfo(cf) is { } chainInfo&& chainInfo.Item2 >= index) { return(IndexBlockHash(chainInfo.Item1, index, true)); } byte[] indexBytes = RocksDBStoreBitConverter.GetBytes(index); byte[] key = IndexKeyPrefix.Concat(indexBytes).ToArray(); byte[] bytes = _chainDb.Get(key, cf); return(bytes is null ? (BlockHash?)null : new BlockHash(bytes)); } catch (Exception e) { LogUnexpectedException(nameof(IndexBlockHash), e); } return(null); }
/// <inheritdoc cref="BaseStore.DeleteBlock(BlockHash)"/> public override bool DeleteBlock(BlockHash blockHash) { byte[] key = BlockKey(blockHash); if (!(_blockIndexDb.Get(key) is byte[] blockDbNameByte)) { return(false); } _rwBlockLock.EnterWriteLock(); try { string blockDbName = RocksDBStoreBitConverter.GetString(blockDbNameByte); RocksDb blockDb; lock (_blockDbCache) { if (!_blockDbCache.TryGetValue(blockDbName, out blockDb)) { blockDb = RocksDBUtils.OpenRocksDb(_options, BlockDbPath(blockDbName)); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } } _blockCache.Remove(blockHash); _blockIndexDb.Remove(key); blockDb.Remove(key); return(true); } catch (Exception e) { LogUnexpectedException(nameof(DeleteBlock), e); } finally { _rwBlockLock.ExitWriteLock(); } return(false); }
/// <inheritdoc/> public override void ForkStateReferences <T>( Guid sourceChainId, Guid destinationChainId, Block <T> branchPoint) { byte[] prefix = StateRefKeyPrefix; ColumnFamilyHandle destCf = GetColumnFamily(_stateRefDb, destinationChainId); foreach (Iterator it in IterateDb(_stateRefDb, prefix, sourceChainId)) { byte[] key = it.Key(); byte[] indexBytes = key.Skip(key.Length - sizeof(long)).ToArray(); long index = RocksDBStoreBitConverter.ToInt64(indexBytes); if (index > branchPoint.Index) { continue; } _stateRefDb.Put(key, it.Value(), destCf); } using Iterator destIt = _stateRefDb.NewIterator(destCf); destIt.Seek(prefix); if (!(destIt.Valid() && destIt.Key().StartsWith(prefix)) && CountIndex(sourceChainId) < 1) { throw new ChainIdNotFoundException( sourceChainId, "The source chain to be forked does not exist."); } _lastStateRefCaches.Remove(destinationChainId); }