/// <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); }
/// <summary> /// query transaction info /// </summary> /// <param name="txId"></param> /// <returns></returns> public async Task <object> GetTransaction(UInt256 txId) { var snapshot = Helpers.GetDefaultSnapshot(); var transaction = snapshot.GetTransaction(txId); if (transaction == null) { return(Error(ErrorCode.TxIdNotFound)); } var model = new TransactionModel(transaction); var txState = snapshot.GetTransactionState(txId); if (txState != null) { Header header = snapshot.GetHeader(txState.BlockIndex); model.BlockHash = header.Hash; model.BlockHeight = txState.BlockIndex; model.Timestamp = header.Timestamp; model.Confirmations = snapshot.GetHeight() - header.Index + 1; } using var db = new TrackDB(); var trans = db.QueryTransfers(new TransferFilter() { TxIds = new List <UInt256>() { txId }, PageSize = int.MaxValue }).List; model.Transfers = trans.Select(tx => tx.ToTransferModel()).ToList(); var executeResult = db.GetExecuteLog(txId); if (executeResult?.Notifications.NotEmpty() == true) { model.Notifies.AddRange( executeResult.Notifications.Select(n => new NotifyModel() { Contract = n.Contract, EventName = n.EventName, State = JStackItem.FromJson(n.State), })); } return(model); }
/// <summary> /// query transaction info /// </summary> /// <param name="txId"></param> /// <returns></returns> public async Task <object> GetTransaction(UInt256 txId) { var transaction = Blockchain.Singleton.GetTransaction(txId); if (transaction == null) { return(Error(ErrorCode.TxIdNotFound)); } var model = new TransactionModel(transaction); TransactionState txState = Blockchain.Singleton.View.Transactions.TryGet(txId); if (txState != null) { Header header = Blockchain.Singleton.GetHeader(txState.BlockIndex); model.BlockHash = header.Hash; model.BlockHeight = txState.BlockIndex; model.Timestamp = header.Timestamp; model.Confirmations = Blockchain.Singleton.Height - header.Index + 1; } using var db = new TrackDB(); var trans = db.FindTransfer(new TransferFilter() { TxIds = new List <UInt256>() { txId }, PageSize = int.MaxValue }).List; model.Transfers = trans.Select(tx => tx.ToTransferModel()).ToList(); var executeResult = db.GetExecuteLog(txId); if (executeResult?.Notifications.NotEmpty() == true) { model.Notifies.AddRange( executeResult.Notifications.Select(n => new NotifyModel() { Contract = UInt160.Parse(n.Contract), State = JStackItem.FromJson(n.State), })); } return(model); }
public async Task <object> InvokeContract(InvokeContractParameterModel para) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (para.ContractHash == null || para.Method.IsNull()) { return(Error(ErrorCode.ParameterIsNull)); } using var snapshot = Blockchain.Singleton.GetSnapshot(); var contract = snapshot.Contracts.TryGet(para.ContractHash); if (contract == null) { return(Error(ErrorCode.UnknownContract)); } ContractParameter[] contractParameters = null; try { contractParameters = para.Parameters?.Select(p => { return(ContractParameter.FromJson(p)); //var parameterValue = new ContractParameter(p.Type); //parameterValue.SetValue(p.Value); //return parameterValue; }).ToArray(); } catch (Exception e) { return(Error(ErrorCode.InvalidPara)); } var signers = new List <Cosigner>(); if (para.Cosigners.NotEmpty()) { signers.AddRange(para.Cosigners.Select(s => new Cosigner() { Account = s.Account, Scopes = s.Scopes })); } Transaction tx = null; using ScriptBuilder sb = new ScriptBuilder(); sb.EmitAppCall(para.ContractHash, para.Method, contractParameters); try { tx = CurrentWallet.InitTransaction(sb.ToArray(), signers.ToArray()); } catch (InvalidOperationException) { return(Error(ErrorCode.EngineFault)); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } throw; } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } var result = new InvokeResultModel(); using ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx, testMode: true); result.VmState = engine.State; result.GasConsumed = new BigDecimal(tx.SystemFee, NativeContract.GAS.Decimals); result.ResultStack = engine.ResultStack.Select(p => JStackItem.FromJson(p.ToParameter().ToJson())).ToList(); if (engine.State.HasFlag(VMState.FAULT)) { return(Error(ErrorCode.EngineFault)); } if (!para.SendTx) { return(result); } await tx.Broadcast(); result.TxId = tx.Hash; return(result); }
public async Task <object> InvokeContract(InvokeContractParameterModel para) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (para.ContractHash == null || para.Method.IsNull()) { return(Error(ErrorCode.ParameterIsNull)); } var contract = para.ContractHash.GetContract(); if (contract == null) { return(Error(ErrorCode.UnknownContract)); } ContractParameter[] contractParameters = null; try { contractParameters = para.Parameters?.Select(JsonToContractParameter).ToArray(); } catch (Exception e) { return(Error(ErrorCode.InvalidPara)); } var signers = new List <Signer>(); if (para.Cosigners.NotEmpty()) { signers.AddRange(para.Cosigners.Select(s => new Signer() { Account = s.Account, Scopes = s.Scopes, AllowedContracts = new UInt160[0] })); } Transaction tx = null; using ScriptBuilder sb = new ScriptBuilder(); sb.EmitDynamicCall(para.ContractHash, para.Method, contractParameters); try { tx = CurrentWallet.InitTransaction(sb.ToArray(), null, signers.ToArray()); } catch (InvalidOperationException ex) { return(Error(ErrorCode.EngineFault, $"{ex.Message}\r\n InnerError:{ex.InnerException}")); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } throw; } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } var result = new InvokeResultModel(); using ApplicationEngine engine = tx.Script.RunTestMode(null, tx); result.VmState = engine.State; result.GasConsumed = new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals); result.ResultStack = engine.ResultStack.Select(p => JStackItem.FromJson(p.ToContractParameter().ToJson())).ToList(); result.Notifications = engine.Notifications?.Select(ConvertToEventModel).ToList(); if (engine.State.HasFlag(VMState.FAULT)) { return(Error(ErrorCode.EngineFault)); } if (!para.SendTx) { return(result); } await tx.Broadcast(); result.TxId = tx.Hash; return(result); }