Пример #1
0
        private JObject Process(string method, JArray _params)
        {
            switch (method)
            {
            case "unlock":
                if (wallet == null)
                {
                    return("wallet is null.");
                }

                if (_params.Count < 2)
                {
                    return("parameter is error.");
                }
                string password = _params[0].AsString();
                int    duration = (int)_params[1].AsNumber();

                bool   ok     = walletTimeLock.UnLock(password, duration);
                string result = ok ? "successfully" : "failed";
                return($"Wallet unlocked  {result}.");

            case "dumpprivkey":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied");
                }
                else
                {
                    UInt160       scriptHash = _params[0].AsString().ToScriptHash();
                    WalletAccount account    = wallet.GetAccount(scriptHash);
                    return(account.GetKey().Export());
                }

            case "getaccountstate":
            {
                UInt160      script_hash = _params[0].AsString().ToScriptHash();
                AccountState account     = Blockchain.Singleton.Store.GetAccounts().TryGet(script_hash) ?? new AccountState(script_hash);
                return(account.ToJson());
            }

            case "getassetstate":
            {
                UInt256    asset_id = UInt256.Parse(_params[0].AsString());
                AssetState asset    = Blockchain.Singleton.Store.GetAssets().TryGet(asset_id);
                return(asset?.ToJson() ?? throw new RpcException(-100, "Unknown asset"));
            }

            case "getbalance":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    JObject json = new JObject();
                    switch (UIntBase.Parse(_params[0].AsString()))
                    {
                    case UInt160 asset_id_160:         //BRC5 balance
                        json["balance"] = wallet.GetAvailable(asset_id_160).ToString();
                        break;

                    case UInt256 asset_id_256:         //Global Assets balance
                        IEnumerable <Coin> coins = wallet.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent) && p.Output.AssetId.Equals(asset_id_256));
                        json["balance"]   = coins.Sum(p => p.Output.Value).ToString();
                        json["confirmed"] = coins.Where(p => p.State.HasFlag(CoinState.Confirmed)).Sum(p => p.Output.Value).ToString();
                        break;
                    }
                    return(json);
                }

            case "getbestblockhash":
                return(Blockchain.Singleton.CurrentBlockHash.ToString());

            case "getblock":
            {
                Block block;
                if (_params[0] is JNumber)
                {
                    uint index = (uint)_params[0].AsNumber();
                    block = Blockchain.Singleton.Store.GetBlock(index);
                }
                else
                {
                    UInt256 hash = UInt256.Parse(_params[0].AsString());
                    block = Blockchain.Singleton.Store.GetBlock(hash);
                }
                if (block == null)
                {
                    throw new RpcException(-100, "Unknown block");
                }
                bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false);
                if (verbose)
                {
                    JObject json = block.ToJson();
                    json["confirmations"] = Blockchain.Singleton.Height - block.Index + 1;
                    UInt256 hash = Blockchain.Singleton.Store.GetNextBlockHash(block.Hash);
                    if (hash != null)
                    {
                        json["nextblockhash"] = hash.ToString();
                    }
                    return(json);
                }
                return(block.ToArray().ToHexString());
            }

            case "getblockcount":
                return(Blockchain.Singleton.Height + 1);

            case "getblockhash":
            {
                uint height = (uint)_params[0].AsNumber();
                if (height <= Blockchain.Singleton.Height)
                {
                    return(Blockchain.Singleton.GetBlockHash(height).ToString());
                }
                throw new RpcException(-100, "Invalid Height");
            }

            case "getblockheader":
            {
                Header header;
                if (_params[0] is JNumber)
                {
                    uint height = (uint)_params[0].AsNumber();
                    header = Blockchain.Singleton.Store.GetHeader(height);
                }
                else
                {
                    UInt256 hash = UInt256.Parse(_params[0].AsString());
                    header = Blockchain.Singleton.Store.GetHeader(hash);
                }
                if (header == null)
                {
                    throw new RpcException(-100, "Unknown block");
                }

                bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false);
                if (verbose)
                {
                    JObject json = header.ToJson();
                    json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1;
                    UInt256 hash = Blockchain.Singleton.Store.GetNextBlockHash(header.Hash);
                    if (hash != null)
                    {
                        json["nextblockhash"] = hash.ToString();
                    }
                    return(json);
                }

                return(header.ToArray().ToHexString());
            }

            case "getblocksysfee":
            {
                uint height = (uint)_params[0].AsNumber();
                if (height <= Blockchain.Singleton.Height)
                {
                    return(Blockchain.Singleton.Store.GetSysFeeAmount(height).ToString());
                }
                throw new RpcException(-100, "Invalid Height");
            }

            case "getconnectioncount":
                return(LocalNode.Singleton.ConnectedCount);

            case "getcontractstate":
            {
                UInt160       script_hash = UInt160.Parse(_params[0].AsString());
                ContractState contract    = Blockchain.Singleton.Store.GetContracts().TryGet(script_hash);
                return(contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract"));
            }

            case "getnewaddress":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    WalletAccount account = wallet.CreateAccount();
                    if (wallet is BRC6Wallet BRC6)
                    {
                        BRC6.Save();
                    }
                    return(account.Address);
                }

            case "getpeers":
            {
                JObject json = new JObject();
                json["unconnected"] = new JArray(LocalNode.Singleton.GetUnconnectedPeers().Select(p =>
                    {
                        JObject peerJson    = new JObject();
                        peerJson["address"] = p.Address.ToString();
                        peerJson["port"]    = p.Port;
                        return(peerJson);
                    }));
                json["bad"]       = new JArray();   //badpeers has been removed
                json["connected"] = new JArray(LocalNode.Singleton.GetRemoteNodes().Select(p =>
                    {
                        JObject peerJson    = new JObject();
                        peerJson["address"] = p.Remote.Address.ToString();
                        peerJson["port"]    = p.ListenerPort;
                        return(peerJson);
                    }));
                return(json);
            }

            case "getrawmempool":
                return(new JArray(Blockchain.Singleton.GetMemoryPool().Select(p => (JObject)p.Hash.ToString())));

            case "getrawtransaction":
            {
                UInt256     hash    = UInt256.Parse(_params[0].AsString());
                bool        verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false);
                Transaction tx      = Blockchain.Singleton.GetTransaction(hash);
                if (tx == null)
                {
                    throw new RpcException(-100, "Unknown transaction");
                }
                if (verbose)
                {
                    JObject json   = tx.ToJson();
                    uint?   height = Blockchain.Singleton.Store.GetTransactions().TryGet(hash)?.BlockIndex;
                    if (height != null)
                    {
                        Header header = Blockchain.Singleton.Store.GetHeader((uint)height);
                        json["blockhash"]     = header.Hash.ToString();
                        json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1;
                        json["blocktime"]     = header.Timestamp;
                    }
                    return(json);
                }
                return(tx.ToArray().ToHexString());
            }

            case "getstorage":
            {
                UInt160     script_hash = UInt160.Parse(_params[0].AsString());
                byte[]      key         = _params[1].AsString().HexToBytes();
                StorageItem item        = Blockchain.Singleton.Store.GetStorages().TryGet(new StorageKey
                    {
                        ScriptHash = script_hash,
                        Key        = key
                    }) ?? new StorageItem();
                return(item.Value?.ToHexString());
            }

            case "gettxout":
            {
                UInt256 hash  = UInt256.Parse(_params[0].AsString());
                ushort  index = (ushort)_params[1].AsNumber();
                return(Blockchain.Singleton.Store.GetUnspent(hash, index)?.ToJson(index));
            }

            case "createaddress":
            {
                byte[] privateKey = new byte[32];
                using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
                {
                    rng.GetBytes(privateKey);

                    KeyPair key    = new KeyPair(privateKey);
                    byte[]  pubkey = Contract.CreateSignatureRedeemScript(key.PublicKey);

                    JObject json = new JObject();
                    json["address"] = pubkey.ToScriptHash().ToAddress();
                    json["pubkey"]  = key.PublicKey.EncodePoint(true).ToHexString();
                    json["prikey"]  = key.PrivateKey.ToHexString();
                    json["wif"]     = key.Export();
                    json["script"]  = pubkey.ToHexString();
                    return(json);
                }
            }

            case "getutxos":
            {
                UInt160      script_hash = _params[0].AsString().ToScriptHash();
                AccountState account     = Blockchain.Singleton.Store.GetAccounts().TryGet(script_hash) ?? new AccountState(script_hash);
                return(account.ToJson());
            }

            case "getvalidators":
                using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot())
                {
                    var validators = snapshot.GetValidators();
                    return(snapshot.GetEnrollments().Select(p =>
                    {
                        JObject validator = new JObject();
                        validator["publickey"] = p.PublicKey.ToString();
                        validator["votes"] = p.Votes.ToString();
                        validator["active"] = validators.Contains(p.PublicKey);
                        return validator;
                    }).ToArray());
                }

            case "getversion":
            {
                JObject json = new JObject();
                json["port"]      = LocalNode.Singleton.ListenerPort;
                json["nonce"]     = LocalNode.Nonce;
                json["useragent"] = LocalNode.UserAgent;
                return(json);
            }

            case "getwalletheight":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    return((wallet.WalletHeight > 0) ? wallet.WalletHeight - 1 : 0);
                }

            case "invoke":
            {
                UInt160             script_hash = UInt160.Parse(_params[0].AsString());
                ContractParameter[] parameters  = ((JArray)_params[1]).Select(p => ContractParameter.FromJson(p)).ToArray();
                byte[] script;
                using (ScriptBuilder sb = new ScriptBuilder())
                {
                    script = sb.EmitAppCall(script_hash, parameters).ToArray();
                }
                return(GetInvokeResult(script));
            }

            case "invokefunction":
            {
                UInt160             script_hash = UInt160.Parse(_params[0].AsString());
                string              operation   = _params[1].AsString();
                ContractParameter[] args        = _params.Count >= 3 ? ((JArray)_params[2]).Select(p => ContractParameter.FromJson(p)).ToArray() : new ContractParameter[0];
                byte[]              script;
                using (ScriptBuilder sb = new ScriptBuilder())
                {
                    script = sb.EmitAppCall(script_hash, operation, args).ToArray();
                }
                return(GetInvokeResult(script));
            }

            case "invokescript":
            {
                byte[] script = _params[0].AsString().HexToBytes();
                return(GetInvokeResult(script));
            }

            case "listaddress":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    return(wallet.GetAccounts().Select(p =>
                    {
                        JObject account = new JObject();
                        account["address"] = p.Address;
                        account["haskey"] = p.HasKey;
                        account["label"] = p.Label;
                        account["watchonly"] = p.WatchOnly;
                        return account;
                    }).ToArray());
                }

            case "sendfrom":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    UIntBase        assetId    = UIntBase.Parse(_params[0].AsString());
                    AssetDescriptor descriptor = new AssetDescriptor(assetId);
                    UInt160         from       = _params[1].AsString().ToScriptHash();
                    UInt160         to         = _params[2].AsString().ToScriptHash();
                    BigDecimal      value      = BigDecimal.Parse(_params[3].AsString(), descriptor.Decimals);
                    if (value.Sign <= 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    Fixed8 fee = _params.Count >= 5 ? Fixed8.Parse(_params[4].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 6 ? _params[5].AsString().ToScriptHash() : null;
                    string      remark         = _params.Count >= 7 ? _params[6].AsString() : null;
                    Transaction tx             = wallet.MakeTransaction(null, new[]
                    {
                        new TransferOutput
                        {
                            AssetId    = assetId,
                            Value      = value,
                            ScriptHash = to
                        }
                    }, from: from, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    AddRemak(remark, tx);
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "transfer":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    UIntBase        assetId    = UIntBase.Parse(_params[0].AsString());
                    AssetDescriptor descriptor = new AssetDescriptor(assetId);
                    UInt160         from       = _params[1].AsString().ToScriptHash();
                    UInt160         to         = _params[2].AsString().ToScriptHash();
                    BigDecimal      value      = BigDecimal.Parse(_params[3].AsString(), descriptor.Decimals);
                    if (value.Sign <= 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    Fixed8 fee = _params.Count >= 5 ? Fixed8.Parse(_params[4].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 6 ? _params[5].AsString().ToScriptHash() : from;
                    string      remark         = _params.Count >= 7 ? _params[6].AsString() : null;
                    Transaction tx             = wallet.MakeTransaction(null, new[]
                    {
                        new TransferOutput
                        {
                            AssetId    = assetId,
                            Value      = value,
                            ScriptHash = to
                        }
                    }, from: from, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    AddRemak(remark, tx);
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "sendmany":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    JArray to = (JArray)_params[0];
                    if (to.Count == 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    TransferOutput[] outputs = new TransferOutput[to.Count];
                    for (int i = 0; i < to.Count; i++)
                    {
                        UIntBase        asset_id   = UIntBase.Parse(to[i]["asset"].AsString());
                        AssetDescriptor descriptor = new AssetDescriptor(asset_id);
                        outputs[i] = new TransferOutput
                        {
                            AssetId    = asset_id,
                            Value      = BigDecimal.Parse(to[i]["value"].AsString(), descriptor.Decimals),
                            ScriptHash = to[i]["address"].AsString().ToScriptHash()
                        };
                        if (outputs[i].Value.Sign <= 0)
                        {
                            throw new RpcException(-32602, "Invalid params");
                        }
                    }
                    Fixed8 fee = _params.Count >= 2 ? Fixed8.Parse(_params[1].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 3 ? _params[2].AsString().ToScriptHash() : null;
                    string      remark         = _params.Count >= 4 ? _params[3].AsString() : null;
                    Transaction tx             = wallet.MakeTransaction(null, outputs, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    AddRemak(remark, tx);
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "sendrawtransaction":
            {
                Transaction       tx     = Transaction.DeserializeFrom(_params[0].AsString().HexToBytes());
                RelayResultReason reason = system.Blockchain.Ask <RelayResultReason>(tx).Result;
                return(GetRelayResult(reason));
            }

            case "sendtoaddress":
                if (wallet == null || walletTimeLock.IsLocked())
                {
                    throw new RpcException(-400, "wallet is null or locked.");
                }
                else
                {
                    UIntBase        assetId    = UIntBase.Parse(_params[0].AsString());
                    AssetDescriptor descriptor = new AssetDescriptor(assetId);
                    UInt160         scriptHash = _params[1].AsString().ToScriptHash();
                    BigDecimal      value      = BigDecimal.Parse(_params[2].AsString(), descriptor.Decimals);
                    if (value.Sign <= 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    Fixed8 fee = _params.Count >= 4 ? Fixed8.Parse(_params[3].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 5 ? _params[4].AsString().ToScriptHash() : null;
                    string      remark         = _params.Count >= 6 ? _params[5].AsString() : null;
                    Transaction tx             = wallet.MakeTransaction(null, new[]
                    {
                        new TransferOutput
                        {
                            AssetId    = assetId,
                            Value      = value,
                            ScriptHash = scriptHash
                        }
                    }, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    AddRemak(remark, tx);
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "submitblock":
            {
                Block             block  = _params[0].AsString().HexToBytes().AsSerializable <Block>();
                RelayResultReason reason = system.Blockchain.Ask <RelayResultReason>(block).Result;
                return(GetRelayResult(reason));
            }

            case "validateaddress":
            {
                JObject json = new JObject();
                UInt160 scriptHash;
                try
                {
                    scriptHash = _params[0].AsString().ToScriptHash();
                }
                catch
                {
                    scriptHash = null;
                }
                json["address"] = _params[0];
                json["isvalid"] = scriptHash != null;
                return(json);
            }

            /*
             * case "backupwallet":
             *  if (wallet == null || walletTimeLock.IsLocked())
             *      throw new RpcException(-400, "wallet is null or locked.");
             *  else
             *  {
             *      return BackupWallet();
             *  }
             *
             * case "backupwallettosqlite":
             *  if (wallet == null)
             *      throw new RpcException(-400, "Access denied");
             *  else
             *  {
             *      try
             *      {
             *          string path = ((Bhp.Wallets.BRC6.BRC6Wallet)wallet).Path;
             *          string WalletName = _params.Count >= 1 ? _params[0].AsString().Trim() : path.Substring(path.LastIndexOf("\\") + 1).Trim();
             *
             *          int nIndex = WalletName.LastIndexOf('.');
             *          if (nIndex >= 0)
             *          {
             *              WalletName = WalletName.Substring(0, nIndex).Trim();
             *          }
             *          else
             *          {
             *              WalletName = WalletName.Trim();
             *          }
             *          return "Backup Wallet Success! Name:" + SQLiteOperateForBackupWallet.BackupWalletToSQLite(WifToList(), WalletName);
             *      }
             *      catch (Exception ex)
             *      {
             *          throw new Exception(ex.ToString());
             *          //throw new RpcException(-401, "SQLite Exception");
             *      }
             *  }
             *
             * case "recoverwalletfromsqlite":
             *  if (wallet == null)
             *      throw new RpcException(-400, "Access denied");
             *  else
             *  {
             *      if (_params.Count == 0)
             *      {
             *          return "Please Input BackupWalletName";
             *      }
             *      string BackWalletName = _params[0].AsString().Trim();
             *      //System.IO.Path.GetExtension(BackWalletName)
             *
             *      string FileName = "";//不含后缀
             *      int nIndex = BackWalletName.LastIndexOf('.');
             *      if (nIndex >= 0)
             *      {
             *          FileName = BackWalletName.Substring(0, nIndex) + ".sqlite";
             *      }
             *      else
             *      {
             *          FileName = BackWalletName + ".sqlite";
             *      }
             *      List<string> wifs = SQLiteOperateForBackupWallet.RecoverWalletFromSQLite(FileName);
             *      foreach (string wif in wifs)
             *      {
             *          WalletAccount account;
             *          try
             *          {
             *              account = wallet.Import(wif);
             *          }
             *          catch (FormatException)
             *          {
             *              continue;
             *          }
             *      }
             *      if (wallet is BRC6Wallet walletBrc6)
             *          walletBrc6.Save();
             *      return "Recover Wallet Success";
             *  }
             *
             * case "closewallet":
             *  if (wallet == null)
             *      throw new RpcException(-400, "Access denied");
             *  else
             *  {
             *      if (wallet is IDisposable disposable)
             *      {
             *          disposable.Dispose();
             *          wallet = null;
             *      }
             *      return "Close Wallet Success";
             *  }
             */
            default:
                throw new RpcException(-32601, "Method not found");
            }
        }
Пример #2
0
        private JObject Process(string method, JArray _params)
        {
            switch (method)
            {
            /*
             * case "dumpprivkey":
             *  if (wallet == null)
             *      throw new RpcException(-400, "Access denied");
             *  else
             *  {
             *      UInt160 scriptHash = _params[0].AsString().ToScriptHash();
             *      WalletAccount account = wallet.GetAccount(scriptHash);
             *      return account.GetKey().Export();
             *  }
             */
            case "getaccountstate":
            {
                UInt160      script_hash = _params[0].AsString().ToScriptHash();
                AccountState account     = Blockchain.Singleton.Store.GetAccounts().TryGet(script_hash) ?? new AccountState(script_hash);
                return(account.ToJson());
            }

            case "getassetstate":
            {
                UInt256    asset_id = UInt256.Parse(_params[0].AsString());
                AssetState asset    = Blockchain.Singleton.Store.GetAssets().TryGet(asset_id);
                return(asset?.ToJson() ?? throw new RpcException(-100, "Unknown asset"));
            }

            case "getbalance":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied.");
                }
                else
                {
                    JObject json = new JObject();
                    switch (UIntBase.Parse(_params[0].AsString()))
                    {
                    case UInt160 asset_id_160:         //BRC5 balance
                        json["balance"] = wallet.GetAvailable(asset_id_160).ToString();
                        break;

                    case UInt256 asset_id_256:         //Global Assets balance
                        IEnumerable <Coin> coins = wallet.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent) && p.Output.AssetId.Equals(asset_id_256));
                        json["balance"]   = coins.Sum(p => p.Output.Value).ToString();
                        json["confirmed"] = coins.Where(p => p.State.HasFlag(CoinState.Confirmed)).Sum(p => p.Output.Value).ToString();
                        break;
                    }
                    return(json);
                }

            case "getbestblockhash":
                return(Blockchain.Singleton.CurrentBlockHash.ToString());

            case "getblock":
            {
                Block block;
                if (_params[0] is JNumber)
                {
                    uint index = (uint)_params[0].AsNumber();
                    block = Blockchain.Singleton.Store.GetBlock(index);
                }
                else
                {
                    UInt256 hash = UInt256.Parse(_params[0].AsString());
                    block = Blockchain.Singleton.Store.GetBlock(hash);
                }
                if (block == null)
                {
                    throw new RpcException(-100, "Unknown block");
                }
                bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false);
                if (verbose)
                {
                    JObject json = block.ToJson();
                    json["confirmations"] = Blockchain.Singleton.Height - block.Index + 1;
                    UInt256 hash = Blockchain.Singleton.Store.GetNextBlockHash(block.Hash);
                    if (hash != null)
                    {
                        json["nextblockhash"] = hash.ToString();
                    }
                    return(json);
                }
                return(block.ToArray().ToHexString());
            }

            case "getblockcount":
                return(Blockchain.Singleton.Height + 1);

            case "getblockhash":
            {
                uint height = (uint)_params[0].AsNumber();
                if (height <= Blockchain.Singleton.Height)
                {
                    return(Blockchain.Singleton.GetBlockHash(height).ToString());
                }
                throw new RpcException(-100, "Invalid Height");
            }

            case "getblockheader":
            {
                Header header;
                if (_params[0] is JNumber)
                {
                    uint height = (uint)_params[0].AsNumber();
                    header = Blockchain.Singleton.Store.GetHeader(height);
                }
                else
                {
                    UInt256 hash = UInt256.Parse(_params[0].AsString());
                    header = Blockchain.Singleton.Store.GetHeader(hash);
                }
                if (header == null)
                {
                    throw new RpcException(-100, "Unknown block");
                }

                bool verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false);
                if (verbose)
                {
                    JObject json = header.ToJson();
                    json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1;
                    UInt256 hash = Blockchain.Singleton.Store.GetNextBlockHash(header.Hash);
                    if (hash != null)
                    {
                        json["nextblockhash"] = hash.ToString();
                    }
                    return(json);
                }

                return(header.ToArray().ToHexString());
            }

            case "getblocksysfee":
            {
                uint height = (uint)_params[0].AsNumber();
                if (height <= Blockchain.Singleton.Height)
                {
                    return(Blockchain.Singleton.Store.GetSysFeeAmount(height).ToString());
                }
                throw new RpcException(-100, "Invalid Height");
            }

            case "getconnectioncount":
                return(LocalNode.Singleton.ConnectedCount);

            case "getcontractstate":
            {
                UInt160       script_hash = UInt160.Parse(_params[0].AsString());
                ContractState contract    = Blockchain.Singleton.Store.GetContracts().TryGet(script_hash);
                return(contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract"));
            }

            case "getnewaddress":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied");
                }
                else
                {
                    WalletAccount account = wallet.CreateAccount();
                    if (wallet is BRC6Wallet BRC6)
                    {
                        BRC6.Save();
                    }
                    return(account.Address);
                }

            case "getpeers":
            {
                JObject json = new JObject();
                json["unconnected"] = new JArray(LocalNode.Singleton.GetUnconnectedPeers().Select(p =>
                    {
                        JObject peerJson    = new JObject();
                        peerJson["address"] = p.Address.ToString();
                        peerJson["port"]    = p.Port;
                        return(peerJson);
                    }));
                json["bad"]       = new JArray();   //badpeers has been removed
                json["connected"] = new JArray(LocalNode.Singleton.GetRemoteNodes().Select(p =>
                    {
                        JObject peerJson    = new JObject();
                        peerJson["address"] = p.Remote.Address.ToString();
                        peerJson["port"]    = p.ListenerPort;
                        return(peerJson);
                    }));
                return(json);
            }

            case "getrawmempool":
                return(new JArray(Blockchain.Singleton.GetMemoryPool().Select(p => (JObject)p.Hash.ToString())));

            case "getrawtransaction":
            {
                UInt256     hash    = UInt256.Parse(_params[0].AsString());
                bool        verbose = _params.Count >= 2 && _params[1].AsBooleanOrDefault(false);
                Transaction tx      = Blockchain.Singleton.GetTransaction(hash);
                if (tx == null)
                {
                    throw new RpcException(-100, "Unknown transaction");
                }
                if (verbose)
                {
                    JObject json   = tx.ToJson();
                    uint?   height = Blockchain.Singleton.Store.GetTransactions().TryGet(hash)?.BlockIndex;
                    if (height != null)
                    {
                        Header header = Blockchain.Singleton.Store.GetHeader((uint)height);
                        json["blockhash"]     = header.Hash.ToString();
                        json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1;
                        json["blocktime"]     = header.Timestamp;
                    }
                    return(json);
                }
                return(tx.ToArray().ToHexString());
            }

            case "getstorage":
            {
                UInt160     script_hash = UInt160.Parse(_params[0].AsString());
                byte[]      key         = _params[1].AsString().HexToBytes();
                StorageItem item        = Blockchain.Singleton.Store.GetStorages().TryGet(new StorageKey
                    {
                        ScriptHash = script_hash,
                        Key        = key
                    }) ?? new StorageItem();
                return(item.Value?.ToHexString());
            }

            case "gettxout":
            {
                UInt256 hash  = UInt256.Parse(_params[0].AsString());
                ushort  index = (ushort)_params[1].AsNumber();
                return(Blockchain.Singleton.Store.GetUnspent(hash, index)?.ToJson(index));
            }

            case "getvalidators":
                using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot())
                {
                    var validators = snapshot.GetValidators();
                    return(snapshot.GetEnrollments().Select(p =>
                    {
                        JObject validator = new JObject();
                        validator["publickey"] = p.PublicKey.ToString();
                        validator["votes"] = p.Votes.ToString();
                        validator["active"] = validators.Contains(p.PublicKey);
                        return validator;
                    }).ToArray());
                }

            case "getversion":
            {
                JObject json = new JObject();
                json["port"]      = LocalNode.Singleton.ListenerPort;
                json["nonce"]     = LocalNode.Nonce;
                json["useragent"] = LocalNode.UserAgent;
                return(json);
            }

            case "getwalletheight":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied.");
                }
                else
                {
                    return((wallet.WalletHeight > 0) ? wallet.WalletHeight - 1 : 0);
                }

            case "invoke":
            {
                UInt160             script_hash = UInt160.Parse(_params[0].AsString());
                ContractParameter[] parameters  = ((JArray)_params[1]).Select(p => ContractParameter.FromJson(p)).ToArray();
                byte[] script;
                using (ScriptBuilder sb = new ScriptBuilder())
                {
                    script = sb.EmitAppCall(script_hash, parameters).ToArray();
                }
                return(GetInvokeResult(script));
            }

            case "invokefunction":
            {
                UInt160             script_hash = UInt160.Parse(_params[0].AsString());
                string              operation   = _params[1].AsString();
                ContractParameter[] args        = _params.Count >= 3 ? ((JArray)_params[2]).Select(p => ContractParameter.FromJson(p)).ToArray() : new ContractParameter[0];
                byte[]              script;
                using (ScriptBuilder sb = new ScriptBuilder())
                {
                    script = sb.EmitAppCall(script_hash, operation, args).ToArray();
                }
                return(GetInvokeResult(script));
            }

            case "invokescript":
            {
                byte[] script = _params[0].AsString().HexToBytes();
                return(GetInvokeResult(script));
            }

            case "listaddress":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied.");
                }
                else
                {
                    return(wallet.GetAccounts().Select(p =>
                    {
                        JObject account = new JObject();
                        account["address"] = p.Address;
                        account["haskey"] = p.HasKey;
                        account["label"] = p.Label;
                        account["watchonly"] = p.WatchOnly;
                        return account;
                    }).ToArray());
                }

            case "sendfrom":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied");
                }
                else
                {
                    UIntBase        assetId    = UIntBase.Parse(_params[0].AsString());
                    AssetDescriptor descriptor = new AssetDescriptor(assetId);
                    UInt160         from       = _params[1].AsString().ToScriptHash();
                    UInt160         to         = _params[2].AsString().ToScriptHash();
                    BigDecimal      value      = BigDecimal.Parse(_params[3].AsString(), descriptor.Decimals);
                    if (value.Sign <= 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    Fixed8 fee = _params.Count >= 5 ? Fixed8.Parse(_params[4].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 6 ? _params[5].AsString().ToScriptHash() : null;
                    Transaction tx             = wallet.MakeTransaction(null, new[]
                    {
                        new TransferOutput
                        {
                            AssetId    = assetId,
                            Value      = value,
                            ScriptHash = to
                        }
                    }, from: from, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "transfer":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied");
                }
                else
                {
                    UIntBase        assetId    = UIntBase.Parse(_params[0].AsString());
                    AssetDescriptor descriptor = new AssetDescriptor(assetId);
                    UInt160         from       = _params[1].AsString().ToScriptHash();
                    UInt160         to         = _params[2].AsString().ToScriptHash();
                    BigDecimal      value      = BigDecimal.Parse(_params[3].AsString(), descriptor.Decimals);
                    if (value.Sign <= 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    Fixed8 fee = _params.Count >= 5 ? Fixed8.Parse(_params[4].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 6 ? _params[5].AsString().ToScriptHash() : from;
                    Transaction tx             = wallet.MakeTransaction(null, new[]
                    {
                        new TransferOutput
                        {
                            AssetId    = assetId,
                            Value      = value,
                            ScriptHash = to
                        }
                    }, from: from, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "sendmany":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied");
                }
                else
                {
                    JArray to = (JArray)_params[0];
                    if (to.Count == 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    TransferOutput[] outputs = new TransferOutput[to.Count];
                    for (int i = 0; i < to.Count; i++)
                    {
                        UIntBase        asset_id   = UIntBase.Parse(to[i]["asset"].AsString());
                        AssetDescriptor descriptor = new AssetDescriptor(asset_id);
                        outputs[i] = new TransferOutput
                        {
                            AssetId    = asset_id,
                            Value      = BigDecimal.Parse(to[i]["value"].AsString(), descriptor.Decimals),
                            ScriptHash = to[i]["address"].AsString().ToScriptHash()
                        };
                        if (outputs[i].Value.Sign <= 0)
                        {
                            throw new RpcException(-32602, "Invalid params");
                        }
                    }
                    Fixed8 fee = _params.Count >= 2 ? Fixed8.Parse(_params[1].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 3 ? _params[2].AsString().ToScriptHash() : null;
                    Transaction tx             = wallet.MakeTransaction(null, outputs, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "sendrawtransaction":
            {
                Transaction       tx     = Transaction.DeserializeFrom(_params[0].AsString().HexToBytes());
                RelayResultReason reason = system.Blockchain.Ask <RelayResultReason>(tx).Result;
                return(GetRelayResult(reason));
            }

            case "sendtoaddress":
                if (wallet == null)
                {
                    throw new RpcException(-400, "Access denied");
                }
                else
                {
                    UIntBase        assetId    = UIntBase.Parse(_params[0].AsString());
                    AssetDescriptor descriptor = new AssetDescriptor(assetId);
                    UInt160         scriptHash = _params[1].AsString().ToScriptHash();
                    BigDecimal      value      = BigDecimal.Parse(_params[2].AsString(), descriptor.Decimals);
                    if (value.Sign <= 0)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    Fixed8 fee = _params.Count >= 4 ? Fixed8.Parse(_params[3].AsString()) : Fixed8.Zero;
                    if (fee < Fixed8.Zero)
                    {
                        throw new RpcException(-32602, "Invalid params");
                    }
                    UInt160     change_address = _params.Count >= 5 ? _params[4].AsString().ToScriptHash() : null;
                    Transaction tx             = wallet.MakeTransaction(null, new[]
                    {
                        new TransferOutput
                        {
                            AssetId    = assetId,
                            Value      = value,
                            ScriptHash = scriptHash
                        }
                    }, change_address: change_address, fee: fee);
                    if (tx == null)
                    {
                        throw new RpcException(-300, "Insufficient funds");
                    }
                    ContractParametersContext context = new ContractParametersContext(tx);
                    wallet.Sign(context);
                    if (context.Completed)
                    {
                        tx.Witnesses = context.GetWitnesses();
                        wallet.ApplyTransaction(tx);
                        system.LocalNode.Tell(new LocalNode.Relay {
                            Inventory = tx
                        });
                        return(tx.ToJson());
                    }
                    else
                    {
                        return(context.ToJson());
                    }
                }

            case "submitblock":
            {
                Block             block  = _params[0].AsString().HexToBytes().AsSerializable <Block>();
                RelayResultReason reason = system.Blockchain.Ask <RelayResultReason>(block).Result;
                return(GetRelayResult(reason));
            }

            case "validateaddress":
            {
                JObject json = new JObject();
                UInt160 scriptHash;
                try
                {
                    scriptHash = _params[0].AsString().ToScriptHash();
                }
                catch
                {
                    scriptHash = null;
                }
                json["address"] = _params[0];
                json["isvalid"] = scriptHash != null;
                return(json);
            }

            default:
                throw new RpcException(-32601, "Method not found");
            }
        }