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 => data.ToUInt16(i * UnclaimedItemSize), i => data.ToUInt32(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 (CoinReference 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: { PublishTransaction publish_tx = (PublishTransaction)tx; batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Contract).Add(publish_tx.Code.ScriptHash), publish_tx.Code.ToArray()); } 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.Inputs).GroupBy(p => p.PrevHash)) { int height; Transaction tx = GetTransaction(ReadOptions.Default, group.Key, out height); foreach (CoinReference 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)); db.Write(WriteOptions.Default, batch); current_block_height = block.Height; }
private void Persist(Block block) { WriteBatch batch = new WriteBatch(); DbCache <UInt160, AccountState> accounts = new DbCache <UInt160, AccountState>(db, DataEntryPrefix.ST_Account); DbCache <UInt256, UnspentCoinState> unspentcoins = new DbCache <UInt256, UnspentCoinState>(db, DataEntryPrefix.ST_Coin); DbCache <UInt256, SpentCoinState> spentcoins = new DbCache <UInt256, SpentCoinState>(db, DataEntryPrefix.ST_SpentCoin); DbCache <ECPoint, ValidatorState> validators = new DbCache <ECPoint, ValidatorState>(db, DataEntryPrefix.ST_Validator); DbCache <UInt256, AssetState> assets = new DbCache <UInt256, AssetState>(db, DataEntryPrefix.ST_Asset); DbCache <UInt160, ContractState> contracts = new DbCache <UInt160, ContractState>(db, DataEntryPrefix.ST_Contract); DbCache <StorageKey, StorageItem> storages = new DbCache <StorageKey, StorageItem>(db, DataEntryPrefix.ST_Storage); long amount_sysfee = GetSysFeeAmount(block.PrevHash) + (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.Index).Add(tx.ToArray())); unspentcoins.Add(tx.Hash, new UnspentCoinState { Items = Enumerable.Repeat(CoinState.Confirmed, tx.Outputs.Length).ToArray() }); foreach (TransactionOutput output in tx.Outputs) { AccountState account = accounts.GetAndChange(output.ScriptHash, () => new AccountState { ScriptHash = output.ScriptHash, IsFrozen = false, Votes = new ECPoint[0], Balances = new Dictionary <UInt256, Fixed8>() }); if (account.Balances.ContainsKey(output.AssetId)) { account.Balances[output.AssetId] += output.Value; } else { account.Balances[output.AssetId] = output.Value; } } foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) { int height; Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out height); foreach (CoinReference input in group) { unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent; if (tx_prev.Outputs[input.PrevIndex].AssetId.Equals(SystemShare.Hash)) { spentcoins.GetAndChange(input.PrevHash, () => new SpentCoinState { TransactionHash = input.PrevHash, TransactionHeight = (uint)height, Items = new Dictionary <ushort, uint>() }).Items.Add(input.PrevIndex, block.Index); } accounts.GetAndChange(tx_prev.Outputs[input.PrevIndex].ScriptHash).Balances[tx_prev.Outputs[input.PrevIndex].AssetId] -= tx_prev.Outputs[input.PrevIndex].Value; } } switch (tx.Type) { case TransactionType.RegisterTransaction: { RegisterTransaction rtx = (RegisterTransaction)tx; assets.Add(tx.Hash, new AssetState { AssetId = rtx.Hash, AssetType = rtx.AssetType, Name = rtx.Name, Amount = rtx.Amount, Available = Fixed8.Zero, Precision = rtx.Precision, Fee = Fixed8.Zero, FeeAddress = new UInt160(), Owner = rtx.Owner, Admin = rtx.Admin, Issuer = rtx.Admin, Expiration = block.Index + 2000000, IsFrozen = false }); } break; case TransactionType.IssueTransaction: foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) { assets.GetAndChange(result.AssetId).Available -= result.Amount; } break; case TransactionType.ClaimTransaction: foreach (CoinReference input in ((ClaimTransaction)tx).Claims) { if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true) { spentcoins.GetAndChange(input.PrevHash); } } break; case TransactionType.EnrollmentTransaction: { EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx; validators.Add(enroll_tx.PublicKey, new ValidatorState { PublicKey = enroll_tx.PublicKey }); } break; case TransactionType.PublishTransaction: { PublishTransaction publish_tx = (PublishTransaction)tx; contracts.GetOrAdd(publish_tx.Code.ScriptHash, () => new ContractState { Code = publish_tx.Code, HasStorage = publish_tx.NeedStorage, Name = publish_tx.Name, CodeVersion = publish_tx.CodeVersion, Author = publish_tx.Author, Email = publish_tx.Email, Description = publish_tx.Description }); } break; case TransactionType.InvocationTransaction: { InvocationTransaction itx = (InvocationTransaction)tx; CachedScriptTable script_table = new CachedScriptTable(contracts); StateMachine service = new StateMachine(accounts, validators, assets, contracts, storages); ApplicationEngine engine = new ApplicationEngine(itx, script_table, service, itx.Gas); engine.LoadScript(itx.Script, false); if (engine.Execute()) { service.Commit(); } } break; } } accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero)); accounts.Commit(batch); unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent))); unspentcoins.Commit(batch); spentcoins.DeleteWhere((k, v) => v.Items.Count == 0); spentcoins.Commit(batch); validators.Commit(batch); assets.Commit(batch); HashSet <UInt160> contracts_deleted = new HashSet <UInt160>(contracts.GetChangeSet().Where(p => p.State == TrackState.Deleted).Select(p => p.Key)); contracts.Commit(batch); if (contracts_deleted.Count > 0) { storages.DeleteWhere((k, v) => contracts_deleted.Contains(k.ScriptHash)); } storages.Commit(batch); foreach (UInt160 script_hash in contracts_deleted) { foreach (Slice key in db.Find(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.ST_Storage).Add(script_hash), (k, v) => k)) { batch.Delete(key); } } batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(block.Hash).Add(block.Index)); db.Write(WriteOptions.Default, batch); current_block_height = block.Index; }
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.6.6043.32131")) { ReadOptions options = new ReadOptions { FillCache = false }; value = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock)); UInt256 current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); this.current_block_height = value.ToArray().ToUInt32(32); uint current_header_height = current_block_height; if (db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), out value)) { current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); current_header_height = value.ToArray().ToUInt32(32); } foreach (UInt256 hash in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList), (k, v) => { using (MemoryStream ms = new MemoryStream(v.ToArray(), false)) using (BinaryReader r = new BinaryReader(ms)) { return(new { Index = k.ToArray().ToUInt32(1), Hashes = r.ReadSerializableArray <UInt256>() }); } }).OrderBy(p => p.Index).SelectMany(p => p.Hashes).ToArray()) { if (!hash.Equals(GenesisBlock.Hash)) { header_index.Add(hash); } stored_header_count++; } if (stored_header_count == 0) { Header[] headers = db.Find(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Header), (k, v) => Header.FromTrimmedData(v.ToArray(), sizeof(long))).OrderBy(p => p.Height).ToArray(); for (int i = 1; i < headers.Length; i++) { header_index.Add(headers[i].Hash); } } else if (current_header_height >= stored_header_count) { for (UInt256 hash = current_header_hash; hash != header_index[(int)stored_header_count - 1];) { Header header = Header.FromTrimmedData(db.Get(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Header).Add(hash)).ToArray(), sizeof(long)); header_index.Insert((int)stored_header_count, hash); hash = header.PrevBlock; } } } 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), GetType().GetTypeInfo().Assembly.GetName().Version.ToString()); } thread_persistence = new Thread(PersistBlocks); thread_persistence.Name = "LevelDBBlockchain.PersistBlocks"; thread_persistence.Start(); }
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.5")) { 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(), sizeof(long))).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(), sizeof(long)); 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> /// 将区块链的状态回滚到指定的位置 /// </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); }
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; 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; }
public static void Delete(this WriteBatch batch, DataEntryPrefix prefix, ISerializable key) { batch.Delete(SliceBuilder.Begin(prefix).Add(key)); }