コード例 #1
0
 public override void Put(byte prefix, byte[] key, byte[] value)
 {
     db.Put(WriteOptions.Default, SliceBuilder.Begin(prefix).Add(key), value);
 }
コード例 #2
0
 public override void PutSync(byte prefix, byte[] key, byte[] value)
 {
     db.Put(new WriteOptions {
         Sync = true
     }, SliceBuilder.Begin(prefix).Add(key), value);
 }
コード例 #3
0
        static WalletIndexer()
        {
            string path = Path.GetFullPath($"Index_{Settings.Default.Magic:X8}");

            Directory.CreateDirectory(path);
            db = DB.Open(path, new Options {
                CreateIfMissing = true
            });
            if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), out Slice value) && Version.TryParse(value.ToString(), out Version version) && version >= Version.Parse("2.5.4"))
            {
                ReadOptions options = new ReadOptions {
                    FillCache = false
                };
                foreach (var group in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_Group), (k, v) => new
                {
                    Height = k.ToUInt32(1),
                    Id = v.ToArray()
                }))
                {
                    UInt160[] accounts = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.IX_Accounts).Add(group.Id)).ToArray().AsSerializableArray <UInt160>();
                    indexes.Add(group.Height, new HashSet <UInt160>(accounts));
                    foreach (UInt160 account in accounts)
                    {
                        accounts_tracked.Add(account, new HashSet <CoinReference>());
                    }
                }
                foreach (Coin coin in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.ST_Coin), (k, v) => new Coin
                {
                    Reference = k.ToArray().Skip(1).ToArray().AsSerializable <CoinReference>(),
                    Output = v.ToArray().AsSerializable <TransactionOutput>(),
                    State = (CoinState)v.ToArray()[60]
                }))
                {
                    accounts_tracked[coin.Output.ScriptHash].Add(coin.Reference);
                    coins_tracked.Add(coin.Reference, coin);
                }
            }
            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());
                    }
                }
                batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_Version), Assembly.GetExecutingAssembly().GetName().Version.ToString());
                db.Write(WriteOptions.Default, batch);
            }
            Thread thread = new Thread(ProcessBlocks)
            {
                IsBackground = true,
                Name         = $"{nameof(WalletIndexer)}.{nameof(ProcessBlocks)}"
            };

            thread.Start();
        }
コード例 #4
0
 private void ProcessBlock(Block block, HashSet <UInt160> accounts, WriteBatch batch)
 {
     foreach (Transaction tx in block.Transactions)
     {
         HashSet <UInt160> accounts_changed = new HashSet <UInt160>();
         for (ushort index = 0; index < tx.Outputs.Length; index++)
         {
             TransactionOutput output = tx.Outputs[index];
             if (accounts_tracked.ContainsKey(output.ScriptHash))
             {
                 CoinReference reference = new CoinReference
                 {
                     PrevHash  = tx.Hash,
                     PrevIndex = index
                 };
                 if (coins_tracked.TryGetValue(reference, out Coin coin))
                 {
                     coin.State |= CoinState.Confirmed;
                 }
                 else
                 {
                     accounts_tracked[output.ScriptHash].Add(reference);
                     coins_tracked.Add(reference, coin = new Coin
                     {
                         Reference = reference,
                         Output    = output,
                         State     = CoinState.Confirmed
                     });
                 }
                 batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(reference), SliceBuilder.Begin().Add(output).Add((byte)coin.State));
                 accounts_changed.Add(output.ScriptHash);
             }
         }
         foreach (CoinReference input in tx.Inputs)
         {
             if (coins_tracked.TryGetValue(input, out Coin coin))
             {
                 if (coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))
                 {
                     coin.State |= CoinState.Spent | CoinState.Confirmed;
                     batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(input), SliceBuilder.Begin().Add(coin.Output).Add((byte)coin.State));
                 }
                 else
                 {
                     accounts_tracked[coin.Output.ScriptHash].Remove(input);
                     coins_tracked.Remove(input);
                     batch.Delete(DataEntryPrefix.ST_Coin, input);
                 }
                 accounts_changed.Add(coin.Output.ScriptHash);
             }
         }
         if (tx is ClaimTransaction ctx)
         {
             foreach (CoinReference claim in ctx.Claims)
             {
                 if (coins_tracked.TryGetValue(claim, out Coin coin))
                 {
                     accounts_tracked[coin.Output.ScriptHash].Remove(claim);
                     coins_tracked.Remove(claim);
                     batch.Delete(DataEntryPrefix.ST_Coin, claim);
                     accounts_changed.Add(coin.Output.ScriptHash);
                 }
             }
         }
         if (accounts_changed.Count > 0)
         {
             foreach (UInt160 account in accounts_changed)
             {
                 batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Transaction).Add(account).Add(tx.Hash), false);
             }
             BalanceChanged?.Invoke(null, new BalanceEventArgs
             {
                 Transaction     = tx,
                 RelatedAccounts = accounts_changed.ToArray(),
                 Height          = block.Index,
                 Time            = block.Timestamp
             });
         }
     }
 }
コード例 #5
0
 public static T Get <T>(this Database db, ReadOptions options, byte prefix, ISerializable key, Func <Slice, T> resultSelector)
 {
     return(resultSelector(db.Get(options, SliceBuilder.Begin(prefix).Add(key))));
 }
コード例 #6
0
 public static void Put(this WriteBatch batch, byte prefix, ISerializable key, ISerializable value)
 {
     batch.Put(SliceBuilder.Begin(prefix).Add(key), value.ToArray());
 }
コード例 #7
0
 public static IEnumerable <T> Find <T>(this Database db, ReadOptions options, byte prefix) where T : class, ISerializable, new()
 {
     return(Find(db, options, SliceBuilder.Begin(prefix), (k, v) => v.ToArray().AsSerializable <T>()));
 }
コード例 #8
0
 public static T Get <T>(this Database db, ReadOptions options, byte prefix, ISerializable key) where T : class, ISerializable, new()
 {
     return(db.Get(options, SliceBuilder.Begin(prefix).Add(key)).ToArray().AsSerializable <T>());
 }
コード例 #9
0
ファイル: DbCache.cs プロジェクト: camchain/cam
 protected override IEnumerable <KeyValuePair <TKey, TValue> > FindInternal(byte[] key_prefix)
 {
     return(db.Find(options, SliceBuilder.Begin(prefix).Add(key_prefix), (k, v) => new KeyValuePair <TKey, TValue>(k.ToArray().AsSerializable <TKey>(1), v.ToArray().AsSerializable <TValue>())));
 }
コード例 #10
0
 public static void Delete(this WriteBatch batch, byte prefix, ISerializable key)
 {
     batch.Delete(SliceBuilder.Begin(prefix).Add(key));
 }
コード例 #11
0
        private void Persist(Block block)
        {
            WriteBatch batch = new WriteBatch();
            DbCache <UInt160, AccountState>        accounts         = new DbCache <UInt160, AccountState>(db, DataEntryPrefix.ST_Account, batch);
            DbCache <UInt256, UnspentCoinState>    unspentcoins     = new DbCache <UInt256, UnspentCoinState>(db, DataEntryPrefix.ST_Coin, batch);
            DbCache <UInt256, SpentCoinState>      spentcoins       = new DbCache <UInt256, SpentCoinState>(db, DataEntryPrefix.ST_SpentCoin, batch);
            DbCache <ECPoint, ValidatorState>      validators       = new DbCache <ECPoint, ValidatorState>(db, DataEntryPrefix.ST_Validator, batch);
            DbCache <UInt256, AssetState>          assets           = new DbCache <UInt256, AssetState>(db, DataEntryPrefix.ST_Asset, batch);
            DbCache <UInt160, ContractState>       contracts        = new DbCache <UInt160, ContractState>(db, DataEntryPrefix.ST_Contract, batch);
            DbCache <StorageKey, StorageItem>      storages         = new DbCache <StorageKey, StorageItem>(db, DataEntryPrefix.ST_Storage, batch);
            DbMetaDataCache <ValidatorsCountState> validators_count = new DbMetaDataCache <ValidatorsCountState>(db, DataEntryPrefix.IX_ValidatorsCount);
            CachedScriptTable script_table = new CachedScriptTable(contracts);
            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(output.ScriptHash));
                    if (account.Balances.ContainsKey(output.AssetId))
                    {
                        account.Balances[output.AssetId] += output.Value;
                    }
                    else
                    {
                        account.Balances[output.AssetId] = output.Value;
                    }
                    if (output.AssetId.Equals(GoverningToken.Hash) && account.Votes.Length > 0)
                    {
                        foreach (ECPoint pubkey in account.Votes)
                        {
                            validators.GetAndChange(pubkey, () => new ValidatorState(pubkey)).Votes += output.Value;
                        }
                        validators_count.GetAndChange().Votes[account.Votes.Length - 1] += output.Value;
                    }
                }
                foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash))
                {
                    Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out int height);
                    foreach (CoinReference input in group)
                    {
                        unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent;
                        TransactionOutput out_prev = tx_prev.Outputs[input.PrevIndex];
                        AccountState      account  = accounts.GetAndChange(out_prev.ScriptHash);
                        if (out_prev.AssetId.Equals(GoverningToken.Hash))
                        {
                            spentcoins.GetAndChange(input.PrevHash, () => new SpentCoinState
                            {
                                TransactionHash   = input.PrevHash,
                                TransactionHeight = (uint)height,
                                Items             = new Dictionary <ushort, uint>()
                            }).Items.Add(input.PrevIndex, block.Index);
                            if (account.Votes.Length > 0)
                            {
                                foreach (ECPoint pubkey in account.Votes)
                                {
                                    ValidatorState validator = validators.GetAndChange(pubkey);
                                    validator.Votes -= out_prev.Value;
                                    if (!validator.Registered && validator.Votes.Equals(Fixed8.Zero))
                                    {
                                        validators.Delete(pubkey);
                                    }
                                }
                                validators_count.GetAndChange().Votes[account.Votes.Length - 1] -= out_prev.Value;
                            }
                        }
                        account.Balances[out_prev.AssetId] -= out_prev.Value;
                    }
                }
                switch (tx)
                {
#pragma warning disable CS0612
                case RegisterTransaction tx_register:
                    assets.Add(tx.Hash, new AssetState
                    {
                        AssetId    = tx_register.Hash,
                        AssetType  = tx_register.AssetType,
                        Name       = tx_register.Name,
                        Amount     = tx_register.Amount,
                        Available  = Fixed8.Zero,
                        Precision  = tx_register.Precision,
                        Fee        = Fixed8.Zero,
                        FeeAddress = new UInt160(),
                        Owner      = tx_register.Owner,
                        Admin      = tx_register.Admin,
                        Issuer     = tx_register.Admin,
                        Expiration = block.Index + 2 * 2000000,
                        IsFrozen   = false
                    });
                    break;

#pragma warning restore CS0612
                case IssueTransaction _:
                    foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero))
                    {
                        assets.GetAndChange(result.AssetId).Available -= result.Amount;
                    }
                    break;

                case ClaimTransaction _:
                    foreach (CoinReference input in ((ClaimTransaction)tx).Claims)
                    {
                        if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true)
                        {
                            spentcoins.GetAndChange(input.PrevHash);
                        }
                    }
                    break;

#pragma warning disable CS0612
                case EnrollmentTransaction tx_enrollment:
                    validators.GetAndChange(tx_enrollment.PublicKey, () => new ValidatorState(tx_enrollment.PublicKey)).Registered = true;
                    break;

#pragma warning restore CS0612
                case StateTransaction tx_state:
                    foreach (StateDescriptor descriptor in tx_state.Descriptors)
                    {
                        switch (descriptor.Type)
                        {
                        case StateType.Account:
                            ProcessAccountStateDescriptor(descriptor, accounts, validators, validators_count);
                            break;

                        case StateType.Validator:
                            ProcessValidatorStateDescriptor(descriptor, validators);
                            break;
                        }
                    }
                    break;

#pragma warning disable CS0612
                case PublishTransaction tx_publish:
                    contracts.GetOrAdd(tx_publish.ScriptHash, () => new ContractState
                    {
                        Script             = tx_publish.Script,
                        ParameterList      = tx_publish.ParameterList,
                        ReturnType         = tx_publish.ReturnType,
                        ContractProperties = (ContractPropertyState)Convert.ToByte(tx_publish.NeedStorage),
                        Name        = tx_publish.Name,
                        CodeVersion = tx_publish.CodeVersion,
                        Author      = tx_publish.Author,
                        Email       = tx_publish.Email,
                        Description = tx_publish.Description
                    });
                    break;

#pragma warning restore CS0612
                case InvocationTransaction tx_invocation:
                    using (StateMachine service = new StateMachine(block, accounts, assets, contracts, storages))
                    {
                        ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx_invocation, script_table, service, tx_invocation.Gas);
                        engine.LoadScript(tx_invocation.Script, false);
                        if (engine.Execute())
                        {
                            service.Commit();
                        }
                        ApplicationExecuted?.Invoke(this, new ApplicationExecutedEventArgs(tx_invocation, service.Notifications.ToArray(), engine));
                    }
                    break;
                }
                foreach (UInt160 hash in tx.Outputs.Select(p => p.ScriptHash).Distinct())
                {
                    ContractState contract = contracts.TryGet(hash);
                    if (contract == null)
                    {
                        continue;
                    }
                    using (StateMachine service = new StateMachine(block, accounts, assets, contracts, storages))
                    {
                        ApplicationEngine engine = new ApplicationEngine(TriggerType.ApplicationR, tx, script_table, service, Fixed8.Zero);
                        engine.LoadScript(contract.Script, false);
                        using (ScriptBuilder sb = new ScriptBuilder())
                        {
                            sb.EmitPush(0);
                            sb.Emit(OpCode.PACK);
                            sb.EmitPush("received");
                            engine.LoadScript(sb.ToArray(), false);
                        }
                        if (engine.Execute())
                        {
                            service.Commit();
                        }
                    }
                }
            }
            accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero));
            accounts.Commit();
            unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent)));
            unspentcoins.Commit();
            spentcoins.DeleteWhere((k, v) => v.Items.Count == 0);
            spentcoins.Commit();
            validators.Commit();
            assets.Commit();
            contracts.Commit();
            storages.Commit();
            validators_count.Commit(batch);
            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;
        }
コード例 #12
0
 private void OnAddHeader(Header header, WriteBatch batch)
 {
     header_index.Add(header.Hash);
     while ((int)header.Index - 2000 >= stored_header_count)
     {
         using (MemoryStream ms = new MemoryStream())
             using (BinaryWriter w = new BinaryWriter(ms))
             {
                 w.Write(header_index.Skip((int)stored_header_count).Take(2000).ToArray());
                 w.Flush();
                 batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList).Add(stored_header_count), ms.ToArray());
             }
         stored_header_count += 2000;
     }
     batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(header.Hash), SliceBuilder.Begin().Add(0L).Add(header.ToArray()));
     batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), SliceBuilder.Begin().Add(header.Hash).Add(header.Index));
 }
コード例 #13
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.SYS_Version), out value) && Version.TryParse(value.ToString(), out version) && version >= Version.Parse("2.7.4"))
            {
                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_Block), (k, v) => Header.FromTrimmedData(v.ToArray(), sizeof(long))).OrderBy(p => p.Index).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_Block).Add(hash)).ToArray(), sizeof(long));
                        header_index.Insert((int)stored_header_count, hash);
                        hash = header.PrevHash;
                    }
                }
            }
            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.SYS_Version), GetType().GetTypeInfo().Assembly.GetName().Version.ToString());
            }
            thread_persistence          = new Thread(PersistBlocks);
            thread_persistence.Name     = "LevelDBBlockchain.PersistBlocks";
            thread_persistence.Priority = ThreadPriority.AboveNormal;
            thread_persistence.Start();
        }
コード例 #14
0
        public override bool ContainsTransaction(UInt256 hash)
        {
            Slice value;

            return(db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value));
        }
コード例 #15
0
        private void ProcessBlock(Block block, HashSet <UInt160> accounts, WriteBatch batch)
        {
            foreach (Transaction tx in block.Transactions)
            {
                HashSet <UInt160> accounts_changed = new HashSet <UInt160>();
                for (ushort index = 0; index < tx.Outputs.Length; index++)
                {
                    TransactionOutput output = tx.Outputs[index];
                    if (accounts_tracked.ContainsKey(output.ScriptHash))
                    {
                        CoinReference reference = new CoinReference
                        {
                            PrevHash  = tx.Hash,
                            PrevIndex = index
                        };
                        if (coins_tracked.TryGetValue(reference, out Coin coin))
                        {
                            coin.State |= CoinState.Confirmed;
                        }
                        else
                        {
                            accounts_tracked[output.ScriptHash].Add(reference);
                            coins_tracked.Add(reference, coin = new Coin
                            {
                                Reference = reference,
                                Output    = output,
                                State     = CoinState.Confirmed
                            });
                        }
                        batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(reference), SliceBuilder.Begin().Add(output).Add((byte)coin.State));
                        accounts_changed.Add(output.ScriptHash);
                    }
                }
                foreach (CoinReference input in tx.Inputs)
                {
                    if (coins_tracked.TryGetValue(input, out Coin coin))
                    {
                        if (coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash))
                        {
                            coin.State |= CoinState.Spent | CoinState.Confirmed;
                            batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(input), SliceBuilder.Begin().Add(coin.Output).Add((byte)coin.State));
                        }
                        else
                        {
                            accounts_tracked[coin.Output.ScriptHash].Remove(input);
                            coins_tracked.Remove(input);
                            batch.Delete(DataEntryPrefix.ST_Coin, input);
                        }
                        accounts_changed.Add(coin.Output.ScriptHash);
                    }
                }
                switch (tx)
                {
                case MinerTransaction _:
                case ContractTransaction _:
#pragma warning disable CS0612
                case PublishTransaction _:
#pragma warning restore CS0612
                    break;

                case ClaimTransaction tx_claim:
                    foreach (CoinReference claim in tx_claim.Claims)
                    {
                        if (coins_tracked.TryGetValue(claim, out Coin coin))
                        {
                            accounts_tracked[coin.Output.ScriptHash].Remove(claim);
                            coins_tracked.Remove(claim);
                            batch.Delete(DataEntryPrefix.ST_Coin, claim);
                            accounts_changed.Add(coin.Output.ScriptHash);
                        }
                    }
                    break;

#pragma warning disable CS0612
                case EnrollmentTransaction tx_enrollment:
                    if (accounts_tracked.ContainsKey(tx_enrollment.ScriptHash))
                    {
                        accounts_changed.Add(tx_enrollment.ScriptHash);
                    }
                    break;

                case RegisterTransaction tx_register:
                    if (accounts_tracked.ContainsKey(tx_register.OwnerScriptHash))
                    {
                        accounts_changed.Add(tx_register.OwnerScriptHash);
                    }
                    break;

#pragma warning restore CS0612
                default:
                    foreach (UInt160 hash in tx.Witnesses.Select(p => p.ScriptHash))
                    {
                        if (accounts_tracked.ContainsKey(hash))
                        {
                            accounts_changed.Add(hash);
                        }
                    }
                    break;
                }
                if (accounts_changed.Count > 0)
                {
                    foreach (UInt160 account in accounts_changed)
                    {
                        batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Transaction).Add(account).Add(tx.Hash), false);
                    }
                    WalletTransaction?.Invoke(null, new WalletTransactionEventArgs
                    {
                        Transaction     = tx,
                        RelatedAccounts = accounts_changed.ToArray(),
                        Height          = block.Index,
                        Time            = block.Timestamp
                    });
                }
            }
        }