Ejemplo n.º 1
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;
        }
Ejemplo n.º 2
0
        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;
        }
Ejemplo n.º 3
0
        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();
        }
Ejemplo n.º 4
0
        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;
        }
Ejemplo n.º 5
0
        /// <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);
        }
Ejemplo n.º 6
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);
        }
Ejemplo n.º 7
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;
        }
Ejemplo n.º 8
0
 public static void Delete(this WriteBatch batch, DataEntryPrefix prefix, ISerializable key)
 {
     batch.Delete(SliceBuilder.Begin(prefix).Add(key));
 }