Пример #1
0
        /// <summary>
        /// query balance
        /// </summary>
        /// <param name="addresses"></param>
        /// <param name="assetId"></param>
        /// <param name="snapshot"></param>
        /// <returns></returns>
        public static List <BigDecimal> GetBalanceOf(this IEnumerable <UInt160> addresses, UInt160 assetId, StoreView snapshot)
        {
            var assetInfo = AssetCache.GetAssetInfo(assetId, snapshot);

            if (assetInfo == null)
            {
                throw new ArgumentException($"invalid assetId:[{assetId}]");
            }

            using var sb = new ScriptBuilder();
            foreach (var address in addresses)
            {
                sb.EmitAppCall(assetId, "balanceOf", address);
            }

            using ApplicationEngine engine = ApplicationEngine.Run(sb.ToArray(), snapshot, testMode: true);
            if (engine.State.HasFlag(VMState.FAULT))
            {
                throw new Exception($"query balance error");
            }

            var result = engine.ResultStack.Select(p => p.GetBigInteger());

            return(result.Select(bigInt => new BigDecimal(bigInt, assetInfo.Decimals)).ToList());
        }
Пример #2
0
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public async Task <object> GetAsset(UInt160 asset)
        {
            var assetInfo = AssetCache.GetAssetInfo(asset);

            if (assetInfo == null)
            {
                return(null);
            }
            var totalSupply = AssetCache.GetTotalSupply(asset);

            using var db = new TrackDB();
            var record = db.GetContract(asset);
            var trans  = db.QueryTransactions(new TransactionFilter()
            {
                Contracts = new List <UInt160>()
                {
                    asset
                }, PageSize = 0
            });

            return(new AssetInfoModel()
            {
                Asset = assetInfo.Asset,
                Decimals = assetInfo.Decimals,
                Name = assetInfo.Name,
                Symbol = assetInfo.Symbol,
                TotalSupply = totalSupply,
                CreateTime = record?.CreateTime,
                TransactionCount = trans.TotalCount,
            });
        }
Пример #3
0
        private (TransferRequestModel, string) ParseTransferRequest(TransferRequest request)
        {
            var model = new TransferRequestModel()
            {
                Receiver = request.Receiver,
                Sender   = request.Sender,
            };

            model.Asset = ConvertToAssetId(request.Asset, out var convertError);
            if (model.Asset == null)
            {
                return(null, $"asset is not valid:{convertError}");
            }
            var assetInfo = AssetCache.GetAssetInfo(model.Asset);

            if (assetInfo == null)
            {
                return(null, $"asset is not valid:{convertError}");
            }
            if (!BigDecimal.TryParse(request.Amount, assetInfo.Decimals, out BigDecimal sendAmount) || sendAmount.Sign <= 0)
            {
                return(null, "Incorrect Amount Format");
            }
            model.Amount = sendAmount;
            return(model, null);
        }
Пример #4
0
        /// <summary>
        /// query balance
        /// </summary>
        /// <param name="addresses"></param>
        /// <param name="assetId"></param>
        /// <param name="snapshot"></param>
        /// <returns></returns>
        public static List<BigDecimal> GetBalanceOf(this IEnumerable<UInt160> addresses, UInt160 assetId, DataCache snapshot)
        {
            var assetInfo = AssetCache.GetAssetInfo(assetId, snapshot);
            if (assetInfo == null)
            {
                throw new ArgumentException($"invalid assetId:[{assetId}]");
            }

            if (assetInfo.Asset == NativeContract.NEO.Hash)
            {
                return GetNativeBalanceOf(addresses, NativeContract.NEO, snapshot);
            }
            if (assetInfo.Asset == NativeContract.GAS.Hash)
            {
                return GetNativeBalanceOf(addresses, NativeContract.GAS, snapshot);
            }
            using var sb = new ScriptBuilder();
            foreach (var address in addresses)
            {
                sb.EmitDynamicCall(assetId, "balanceOf", address);
            }

            using ApplicationEngine engine = sb.ToArray().RunTestMode(snapshot);
            if (engine.State.HasFlag(VMState.FAULT))
            {
                throw new Exception($"query balance error");
            }

            var result = engine.ResultStack.Select(p => p.GetInteger());
            return result.Select(bigInt => new BigDecimal(bigInt, assetInfo.Decimals)).ToList();
        }
Пример #5
0
        private static void UpdateConsensusGas()
        {
            if (Blockchain.Singleton.Height > 0)
            {
                using var snapshot = Blockchain.Singleton.GetSnapshot();
                var validators = NativeContract.NEO.GetValidators(snapshot);
                var addresses  = new List <UInt160>();
                foreach (var ecPoint in validators)
                {
                    if (!_consensusMap.ContainsKey(ecPoint))
                    {
                        _consensusMap[ecPoint] = ecPoint.ToVerificationContract().ScriptHash;
                    }

                    addresses.Add(_consensusMap[ecPoint]);
                }
                using var db = new TrackDB();
                var gas      = AssetCache.GetAssetInfo(NativeContract.GAS.Hash);
                var balances = addresses.GetBalanceOf(NativeContract.GAS.Hash, snapshot);

                for (var index = 0; index < addresses.Count; index++)
                {
                    var address = addresses[index];
                    db.UpdateBalance(address, gas, balances[index].Value, snapshot.Height);
                }

                db.Commit();
            }
        }
Пример #6
0
        /// <summary>
        /// try to find "Transfer" event, then add record to db
        /// </summary>
        /// <param name="notification"></param>
        /// <param name="transaction"></param>
        /// <param name="block"></param>
        /// <param name="snapshot"></param>
        /// <returns></returns>
        private bool HasTransfer(NotificationInfo notification, Transaction transaction, Block block, SnapshotView snapshot)
        {
            var assetHash = UInt160.Parse(notification.Contract);
            var asset     = AssetCache.GetAssetInfo(assetHash, snapshot);

            if (asset == null)
            {
                //not nep5 asset
                return(false);
            }
            var notify = JStackItem.FromJson(notification.State);

            if (!(notify.Value is IList <JStackItem> notifyArray) || notifyArray.Count < 4)
            {
                return(false);
            }
            if (!"transfer".Equals(notifyArray[0].ValueString, StringComparison.OrdinalIgnoreCase))
            {
                return(false);
            }
            var from = notifyArray[1].Value as byte[];
            var to   = notifyArray[2].Value as byte[];

            if (from == null && to == null)
            {
                return(false);
            }
            if (!ConvertBigInteger(notifyArray[3], out var amount))
            {
                return(false);
            }

            var record = new TransferInfo
            {
                BlockHeight = block.Index,
                From        = from == null ? null : new UInt160(from),
                To          = to == null ? null : new UInt160(to),
                Asset       = asset.Asset,
                Amount      = amount,
                TxId        = transaction.Hash,
                TimeStamp   = block.Timestamp,
                AssetInfo   = asset,
            };

            _db.AddTransfer(record);

            if (record.From != null)
            {
                var fromBalance = record.From.GetBalanceOf(assetHash, snapshot);
                _db.UpdateBalance(record.From, asset, fromBalance.Value, snapshot.Height);
            }

            if (record.To != null && record.To != record.From)
            {
                var toBalance = record.To.GetBalanceOf(assetHash, snapshot);
                _db.UpdateBalance(record.To, asset, toBalance.Value, snapshot.Height);
            }
            return(true);
        }
Пример #7
0
        /// <summary>
        /// convert bigint to asset decimal value
        /// </summary>
        /// <param name="amount"></param>
        /// <param name="assetId"></param>
        /// <returns></returns>
        public static (BigDecimal amount, AssetInfo asset) GetAssetAmount(this BigInteger amount, UInt160 assetId)
        {
            var asset = AssetCache.GetAssetInfo(assetId);
            if (asset == null)
            {
                return (new BigDecimal(BigInteger.Zero, 0), null);
            }

            return (new BigDecimal(amount, asset.Decimals), asset);
        }
Пример #8
0
        /// <summary>
        /// query balance
        /// </summary>
        /// <param name="address"></param>
        /// <param name="assetId"></param>
        /// <param name="snapshot"></param>
        /// <returns></returns>
        public static BigDecimal GetBalanceOf(this UInt160 address, UInt160 assetId, DataCache snapshot)
        {
            var assetInfo = AssetCache.GetAssetInfo(assetId, snapshot);
            if (assetInfo == null)
            {
                return new BigDecimal(BigInteger.Zero, 0);
            }

            using var sb = new ScriptBuilder();
            sb.EmitDynamicCall(assetId, "balanceOf", address);
            using var engine = sb.ToArray().RunTestMode(snapshot);
            if (engine.State.HasFlag(VMState.FAULT))
            {
                return new BigDecimal(BigInteger.Zero, 0);
            }
            var balances = engine.ResultStack.Pop().GetInteger();
            return new BigDecimal(balances, assetInfo.Decimals);
        }
Пример #9
0
        /// <summary>
        /// query balance
        /// </summary>
        /// <param name="address"></param>
        /// <param name="assetId"></param>
        /// <param name="snapshot"></param>
        /// <returns></returns>
        public static BigDecimal GetBalanceOf(this UInt160 address, UInt160 assetId, StoreView snapshot)
        {
            var assetInfo = AssetCache.GetAssetInfo(assetId, snapshot);

            if (assetInfo == null)
            {
                return(new BigDecimal(0, 0));
            }

            using var sb = new ScriptBuilder();
            sb.EmitAppCall(assetId, "balanceOf", address);
            using var engine = ApplicationEngine.Run(sb.ToArray(), snapshot, testMode: true);
            if (engine.State.HasFlag(VMState.FAULT))
            {
                return(new BigDecimal(0, 0));
            }
            var balances = engine.ResultStack.Pop().GetBigInteger();

            return(new BigDecimal(balances, assetInfo.Decimals));
        }
Пример #10
0
        private void ProcessTransfer(NotifyEventArgs notification, Blockchain.ApplicationExecuted appExec)
        {
            var transfer = notification.ConvertToTransfer(); // HasTransfer(notification, transaction);

            if (transfer == null)
            {
                return;
            }

            var asset = AssetCache.GetAssetInfo(transfer.Asset, _snapshot);

            if (asset == null)
            {
                return;
            }
            if (transfer.From != null)
            {
                Result.BalanceChangeAccounts.Add(new AccountAsset(transfer.From, transfer.Asset));
            }
            if (transfer.To != null)
            {
                Result.BalanceChangeAccounts.Add(new AccountAsset(transfer.To, transfer.Asset));
            }

            if (appExec.Trigger == TriggerType.Application)
            {
                var transferStorageItem = new TransferStorageItem()
                {
                    From    = transfer.From,
                    To      = transfer.To,
                    Asset   = transfer.Asset,
                    Amount  = transfer.Amount,
                    TxId    = appExec?.Transaction?.Hash,
                    Trigger = appExec.Trigger,
                };
                Result.Transfers.Add(transferStorageItem);
            }
        }
Пример #11
0
        /// <summary>
        /// send asset
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="receivers"></param>
        /// <param name="asset"></param>
        /// <returns></returns>
        public async Task <object> SendToMultiAddress(MultiReceiverRequest[] receivers, string asset = "neo", UInt160 sender = null)
        {
            if (CurrentWallet == null)
            {
                return(Error(ErrorCode.WalletNotOpen));
            }

            if (receivers.IsEmpty())
            {
                return(Error(ErrorCode.ParameterIsNull, $"receivers is null!"));
            }
            UInt160 assetHash = ConvertToAssetId(asset, out var convertError);

            if (assetHash == null)
            {
                return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}"));
            }

            var assetInfo = AssetCache.GetAssetInfo(assetHash);

            if (assetInfo == null)
            {
                return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}"));
            }
            if (sender != null)
            {
                var account = CurrentWallet.GetAccount(sender);
                if (account == null)
                {
                    return(Error(ErrorCode.AddressNotFound));
                }
            }
            var toes = new List <(UInt160 scriptHash, BigDecimal amount)>();

            foreach (var receiver in receivers)
            {
                if (!BigDecimal.TryParse(receiver.Amount, assetInfo.Decimals, out BigDecimal sendAmount) || sendAmount.Sign <= 0)
                {
                    return(Error(ErrorCode.InvalidPara, $"Incorrect Amount Format:{receiver.Amount}"));
                }
                toes.Add((receiver.Address, sendAmount));
            }
            var outputs = toes.Select(t => new TransferOutput()
            {
                AssetId    = assetHash,
                Value      = t.amount,
                ScriptHash = t.scriptHash,
            }).ToArray();

            try
            {
                Transaction tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), outputs, sender);
                if (tx == null)
                {
                    return(Error(ErrorCode.BalanceNotEnough, "Insufficient funds"));
                }

                var(signSuccess, context) = CurrentWallet.TrySignTx(tx);
                if (!signSuccess)
                {
                    return(Error(ErrorCode.SignFail, context.SafeSerialize()));
                }
                await tx.Broadcast();

                return(new TransactionModel(tx));
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("Insufficient GAS"))
                {
                    return(Error(ErrorCode.GasNotEnough));
                }
                return(Error(ErrorCode.TransferError, ex.Message));
            }
        }
Пример #12
0
        /// <summary>
        /// send asset
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="receiver"></param>
        /// <param name="amount"></param>
        /// <param name="asset"></param>
        /// <returns></returns>
        public async Task <object> SendToAddress(UInt160 receiver, string amount, string asset = "neo", UInt160 sender = null)
        {
            if (CurrentWallet == null)
            {
                return(Error(ErrorCode.WalletNotOpen));
            }
            if (receiver == null)
            {
                return(Error(ErrorCode.ParameterIsNull, $"receiver address is null!"));
            }
            UInt160 assetHash = ConvertToAssetId(asset, out var convertError);

            if (assetHash == null)
            {
                return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}"));
            }
            var assetInfo = AssetCache.GetAssetInfo(assetHash);

            if (assetInfo == null)
            {
                return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}"));
            }
            if (!BigDecimal.TryParse(amount, assetInfo.Decimals, out BigDecimal sendAmount) || sendAmount.Sign <= 0)
            {
                return(Error(ErrorCode.InvalidPara, "Incorrect Amount Format"));
            }

            if (sender != null)
            {
                var account = CurrentWallet.GetAccount(sender);
                if (account == null)
                {
                    return(Error(ErrorCode.AddressNotFound));
                }
                var balance = sender.GetBalanceOf(assetHash);
                if (balance.Value < sendAmount.Value)
                {
                    return(Error(ErrorCode.BalanceNotEnough));
                }
            }

            try
            {
                Transaction tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), new[]
                {
                    new TransferOutput
                    {
                        AssetId    = assetHash,
                        Value      = sendAmount,
                        ScriptHash = receiver
                    }
                }, sender);

                if (tx == null)
                {
                    return(Error(ErrorCode.BalanceNotEnough, "Insufficient funds"));
                }

                var(signSuccess, context) = CurrentWallet.TrySignTx(tx);
                if (!signSuccess)
                {
                    return(Error(ErrorCode.SignFail, context.SafeSerialize()));
                }
                await tx.Broadcast();

                return(new TransactionModel(tx));
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("Insufficient GAS"))
                {
                    return(Error(ErrorCode.GasNotEnough));
                }
                return(Error(ErrorCode.TransferError, ex.Message));
            }
        }
Пример #13
0
        private void AnalysisAppExecuteResult(Blockchain.ApplicationExecuted appExec)
        {
            var         execResult  = new ExecuteResultInfo();
            Transaction transaction = appExec.Transaction;

            if (transaction != null)
            {
                //fee account
                Result.BalanceChangeAccounts.Add(new AccountAsset(transaction.Sender, NativeContract.GAS.Hash));
                execResult.TxId = transaction.Hash;
            }

            execResult.Trigger     = appExec.Trigger;
            execResult.VMState     = appExec.VMState;
            execResult.GasConsumed = appExec.GasConsumed;
            try
            {
                execResult.ResultStack = appExec.Stack.Select(q => q.ToContractParameter().ToJson()).ToArray();
            }
            catch (InvalidOperationException)
            {
                execResult.ResultStack = "error: recursive reference";
            }

            execResult.Notifications = appExec.Notifications.Select(n => n.ToNotificationInfo()).ToList();
            Result.ExecuteResultInfos.Add(execResult);

            foreach (var contract in execResult.Notifications.Select(n => n.Contract).Distinct())
            {
                var asset = AssetCache.GetAssetInfo(contract, _snapshot);
                if (asset != null)
                {
                    Result.AssetInfos[asset.Asset] = asset;
                }
            }

            if (execResult.VMState.HasFlag(VMState.FAULT))
            {
                //no need to track
                return;
            }

            if (execResult.Notifications.IsEmpty())
            {
                //no need to track
                return;
            }

            foreach (var notification in appExec.Notifications)
            {
                switch (notification.EventName)
                {
                case "transfer":
                case "Transfer":
                    ProcessTransfer(notification, appExec);
                    break;

                case "Deploy":
                    ProcessDeploy(notification, appExec);
                    break;

                case "Destory":
                    ProcessDestory(notification, appExec);
                    break;

                default:
                    break;
                }
            }
        }