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 void Persist(Block block) { const int UnclaimedItemSize = sizeof(ushort) + sizeof(uint); 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, uint> unclaimed = new MultiValueDictionary <UInt256, ushort, uint>(p => { Slice value = new byte[0]; db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Unclaimed).Add(p), out value); byte[] data = value.ToArray(); return(Enumerable.Range(0, data.Length / UnclaimedItemSize).ToDictionary(i => BitConverter.ToUInt16(data, i * UnclaimedItemSize), i => BitConverter.ToUInt32(data, i * UnclaimedItemSize + sizeof(ushort)))); }); 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(); long amount_sysfee = GetSysFeeAmount(block.PrevBlock) + (long)block.Transactions.Sum(p => p.SystemFee); batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(block.Hash), SliceBuilder.Begin().Add(amount_sysfee).Add(block.Trim())); foreach (Transaction tx in block.Transactions) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash), SliceBuilder.Begin().Add(block.Height).Add(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.ClaimTransaction: foreach (TransactionInput input in ((ClaimTransaction)tx).Claims) { unclaimed.Remove(input.PrevHash, input.PrevIndex); } 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.PublishTransaction: foreach (byte[] script in ((PublishTransaction)tx).Contracts) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Contract).Add(script.ToScriptHash()), script); } break; } unspents.AddEmpty(tx.Hash); for (ushort index = 0; index < tx.Outputs.Length; index++) { unspents.Add(tx.Hash, index); } } foreach (var group in block.Transactions.SelectMany(p => p.GetAllInputs()).GroupBy(p => p.PrevHash)) { int height; Transaction tx = GetTransaction(ReadOptions.Default, group.Key, out height); foreach (TransactionInput input in group) { if (input.PrevIndex == 0) { batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(input.PrevHash)); } unspents.Remove(input.PrevHash, input.PrevIndex); unspent_votes.Remove(input.PrevHash, input.PrevIndex); if (tx?.Outputs[input.PrevIndex].AssetId == AntShare.Hash) { unclaimed.Add(input.PrevHash, input.PrevIndex, block.Height); } } } 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 spent in unclaimed) { if (spent.Value.Count == 0) { batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Unclaimed).Add(spent.Key)); } else { using (MemoryStream ms = new MemoryStream(spent.Value.Count * UnclaimedItemSize)) using (BinaryWriter w = new BinaryWriter(ms)) { foreach (var pair in spent.Value) { w.Write(pair.Key); w.Write(pair.Value); } w.Flush(); batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Unclaimed).Add(spent.Key), ms.ToArray()); } } } 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 void Persist(Block block) { const int UnclaimedItemSize = sizeof(ushort) + sizeof(uint); MultiValueDictionary <UInt256, ushort> unspents = new MultiValueDictionary <UInt256, ushort>(p => { Slice value; if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(p), out value)) { value = new byte[0]; } return(new HashSet <ushort>(value.ToArray().GetUInt16Array())); }); MultiValueDictionary <UInt256, ushort, uint> unclaimed = new MultiValueDictionary <UInt256, ushort, uint>(p => { Slice value; if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Unclaimed).Add(p), out value)) { value = new byte[0]; } byte[] data = value.ToArray(); return(Enumerable.Range(0, data.Length / UnclaimedItemSize).ToDictionary(i => BitConverter.ToUInt16(data, i * UnclaimedItemSize), i => BitConverter.ToUInt32(data, i * UnclaimedItemSize + sizeof(ushort)))); }); MultiValueDictionary <UInt256, ushort> unspent_votes = new MultiValueDictionary <UInt256, ushort>(p => { Slice value; if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.IX_Vote).Add(p), out value)) { value = new byte[0]; } return(new HashSet <ushort>(value.ToArray().GetUInt16Array())); }); Dictionary <UInt256, Fixed8> quantities = new Dictionary <UInt256, Fixed8>(); WriteBatch batch = new WriteBatch(); long amount_sysfee = GetSysFeeAmount(block.PrevBlock) + (long)block.Transactions.Sum(p => p.SystemFee); batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Header).Add(block.Hash), SliceBuilder.Begin().Add(amount_sysfee).Add(block.Trim())); foreach (Transaction tx in block.Transactions) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash), SliceBuilder.Begin().Add(block.Height).Add(tx.ToArray())); if (tx.Attributes.Any(p => p.Usage == TransactionAttributeUsage.Vote)) { 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); } } } 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.ClaimTransaction: foreach (TransactionInput input in ((ClaimTransaction)tx).Claims) { unclaimed.Remove(input.PrevHash, input.PrevIndex); } break; case TransactionType.EnrollmentTransaction: { EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx; batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(tx.Hash), true); } break; case TransactionType.PublishTransaction: foreach (byte[] script in ((PublishTransaction)tx).Contracts) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Contract).Add(script.ToScriptHash()), script); } break; } unspents.AddEmpty(tx.Hash); for (ushort index = 0; index < tx.Outputs.Length; index++) { unspents.Add(tx.Hash, index); } } foreach (var group in block.Transactions.SelectMany(p => p.GetAllInputs()).GroupBy(p => p.PrevHash)) { int height; Transaction tx = GetTransaction(ReadOptions.Default, group.Key, out height); foreach (TransactionInput input in group) { if (input.PrevIndex == 0) { batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Enrollment).Add(input.PrevHash)); } unspents.Remove(input.PrevHash, input.PrevIndex); unspent_votes.Remove(input.PrevHash, input.PrevIndex); if (tx?.Outputs[input.PrevIndex].AssetId == AntShare.Hash) { unclaimed.Add(input.PrevHash, input.PrevIndex, block.Height); } } } 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 spent in unclaimed) { if (spent.Value.Count == 0) { batch.Delete(SliceBuilder.Begin(DataEntryPrefix.IX_Unclaimed).Add(spent.Key)); } else { using (MemoryStream ms = new MemoryStream(spent.Value.Count * UnclaimedItemSize)) using (BinaryWriter w = new BinaryWriter(ms)) { foreach (var pair in spent.Value) { w.Write(pair.Key); w.Write(pair.Value); } w.Flush(); batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_Unclaimed).Add(spent.Key), ms.ToArray()); } } } 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()); } batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(block.Hash).Add(block.Height)); // There's a bug in .Net Core. // When calling DB.Write(), it will throw LevelDBException sometimes. // But when you try to catch the exception, the bug disappears. // We shall remove the "try...catch" clause when Microsoft fix the bug. byte retry = 0; while (true) { try { db.Write(WriteOptions.Default, batch); break; } catch (LevelDBException ex) { if (++retry >= 4) { throw; } File.AppendAllText("leveldb.log", ex.Message + "\r\n"); } } current_block_height = block.Height; }