예제 #1
0
        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);
        }
예제 #2
0
        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;
        }
예제 #3
0
        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;
        }