public Transaction GetTransaction() { var cOutputs = txOutListBox1.Items.Where(p => p.AssetId is UInt160).GroupBy(p => new { AssetId = (UInt160)p.AssetId, Account = p.ScriptHash }, (k, g) => new { k.AssetId, Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value), k.Account }).ToArray(); Transaction tx; List <TransactionAttribute> attributes = new List <TransactionAttribute>(); if (cOutputs.Length == 0) { tx = new ContractTransaction(); } else { UInt160[] addresses = Program.CurrentWallet.GetAccounts().Select(p => p.ScriptHash).ToArray(); HashSet <UInt160> sAttributes = new HashSet <UInt160>(); using (ScriptBuilder sb = new ScriptBuilder()) { foreach (var output in cOutputs) { byte[] script; using (ScriptBuilder sb2 = new ScriptBuilder()) { foreach (UInt160 address in addresses) { sb2.EmitAppCall(output.AssetId, "balanceOf", address); } sb2.Emit(OpCode.DEPTH, OpCode.PACK); script = sb2.ToArray(); } ApplicationEngine engine = ApplicationEngine.Run(script); if (engine.State.HasFlag(VMState.FAULT)) { return(null); } var balances = ((VMArray)engine.ResultStack.Pop()).AsEnumerable().Reverse().Zip(addresses, (i, a) => new { Account = a, Value = i.GetBigInteger() }).ToArray(); BigInteger sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); if (sum < output.Value) { return(null); } if (sum != output.Value) { balances = balances.OrderByDescending(p => p.Value).ToArray(); BigInteger amount = output.Value; int i = 0; while (balances[i].Value <= amount) { amount -= balances[i++].Value; } if (amount == BigInteger.Zero) { balances = balances.Take(i).ToArray(); } else { balances = balances.Take(i).Concat(new[] { balances.Last(p => p.Value >= amount) }).ToArray(); } sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); } sAttributes.UnionWith(balances.Select(p => p.Account)); for (int i = 0; i < balances.Length; i++) { BigInteger value = balances[i].Value; if (i == 0) { BigInteger change = sum - output.Value; if (change > 0) { value -= change; } } sb.EmitAppCall(output.AssetId, "transfer", balances[i].Account, output.Account, value); sb.Emit(OpCode.THROWIFNOT); } } tx = new InvocationTransaction { Version = 1, Script = sb.ToArray() }; } attributes.AddRange(sAttributes.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = p.ToArray() })); } if (!string.IsNullOrEmpty(remark)) { attributes.Add(new TransactionAttribute { Usage = TransactionAttributeUsage.Remark, Data = Encoding.UTF8.GetBytes(remark) }); } tx.Attributes = attributes.ToArray(); tx.Outputs = txOutListBox1.Items.Where(p => p.AssetId is UInt256).Select(p => p.ToTxOutput()).ToArray(); if (tx is ContractTransaction ctx) { tx = Program.CurrentWallet.MakeTransaction(ctx, change_address: ChangeAddress, fee: Fee); } return(tx); }
private void Persist(Block block) { WriteBatch batch = new WriteBatch(); DbCache <UInt160, AccountState> accounts = new DbCache <UInt160, AccountState>(db, DataEntryPrefix.ST_Account); DbCache <UInt256, UnspentCoinState> unspentcoins = new DbCache <UInt256, UnspentCoinState>(db, DataEntryPrefix.ST_Coin); DbCache <UInt256, SpentCoinState> spentcoins = new DbCache <UInt256, SpentCoinState>(db, DataEntryPrefix.ST_SpentCoin); DbCache <ECPoint, ValidatorState> validators = new DbCache <ECPoint, ValidatorState>(db, DataEntryPrefix.ST_Validator); DbCache <UInt256, AssetState> assets = new DbCache <UInt256, AssetState>(db, DataEntryPrefix.ST_Asset); DbCache <UInt160, ContractState> contracts = new DbCache <UInt160, ContractState>(db, DataEntryPrefix.ST_Contract); DbCache <StorageKey, StorageItem> storages = new DbCache <StorageKey, StorageItem>(db, DataEntryPrefix.ST_Storage); List <NotifyEventArgs> notifications = new List <NotifyEventArgs>(); 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; } } foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) { int height; Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out height); foreach (CoinReference input in group) { unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent; if (tx_prev.Outputs[input.PrevIndex].AssetId.Equals(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); } accounts.GetAndChange(tx_prev.Outputs[input.PrevIndex].ScriptHash).Balances[tx_prev.Outputs[input.PrevIndex].AssetId] -= tx_prev.Outputs[input.PrevIndex].Value; } } switch (tx.Type) { case TransactionType.RegisterTransaction: { #pragma warning disable CS0612 RegisterTransaction rtx = (RegisterTransaction)tx; assets.Add(tx.Hash, new AssetState { AssetId = rtx.Hash, AssetType = rtx.AssetType, Name = rtx.Name, Amount = rtx.Amount, Available = Fixed8.Zero, Precision = rtx.Precision, Fee = Fixed8.Zero, FeeAddress = new UInt160(), Owner = rtx.Owner, Admin = rtx.Admin, Issuer = rtx.Admin, Expiration = block.Index + 2 * 2000000, IsFrozen = false }); #pragma warning restore CS0612 } break; case TransactionType.IssueTransaction: foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) { assets.GetAndChange(result.AssetId).Available -= result.Amount; } break; case TransactionType.ClaimTransaction: foreach (CoinReference input in ((ClaimTransaction)tx).Claims) { if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true) { spentcoins.GetAndChange(input.PrevHash); } } break; case TransactionType.EnrollmentTransaction: { #pragma warning disable CS0612 EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx; validators.GetOrAdd(enroll_tx.PublicKey, () => new ValidatorState { PublicKey = enroll_tx.PublicKey }); #pragma warning restore CS0612 } break; case TransactionType.PublishTransaction: { #pragma warning disable CS0612 PublishTransaction publish_tx = (PublishTransaction)tx; contracts.GetOrAdd(publish_tx.ScriptHash, () => new ContractState { Script = publish_tx.Script, ParameterList = publish_tx.ParameterList, ReturnType = publish_tx.ReturnType, HasStorage = publish_tx.NeedStorage, Name = publish_tx.Name, CodeVersion = publish_tx.CodeVersion, Author = publish_tx.Author, Email = publish_tx.Email, Description = publish_tx.Description }); #pragma warning restore CS0612 } break; case TransactionType.InvocationTransaction: { InvocationTransaction itx = (InvocationTransaction)tx; CachedScriptTable script_table = new CachedScriptTable(contracts); StateMachine service = new StateMachine(accounts, validators, assets, contracts, storages); ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, itx, script_table, service, itx.Gas); engine.LoadScript(itx.Script, false); if (engine.Execute()) { service.Commit(); notifications.AddRange(service.Notifications); } } break; } } if (notifications.Count > 0) { OnNotify(block, notifications.ToArray()); } accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero)); accounts.Commit(batch); unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent))); unspentcoins.Commit(batch); spentcoins.DeleteWhere((k, v) => v.Items.Count == 0); spentcoins.Commit(batch); validators.Commit(batch); assets.Commit(batch); contracts.Commit(batch); storages.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; }
/// <summary> /// 创建交易 /// </summary> /// <param name="attributes"></param> /// <param name="outputs"></param> /// <param name="from"></param> /// <param name="change_address"></param> /// <param name="fee"></param> /// <returns></returns> public Transaction MakeTransaction(List <TransactionAttribute> attributes, IEnumerable <TransferOutput> outputs, UInt160 from = null, UInt160 change_address = null, Fixed8 fee = default(Fixed8)) { var cOutputs = outputs.Where(p => !p.IsGlobalAsset).GroupBy(p => new { AssetId = (UInt160)p.AssetId, Account = p.ScriptHash }, (k, g) => new { k.AssetId, Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value), k.Account }).ToArray(); Transaction tx; if (attributes == null) { attributes = new List <TransactionAttribute>(); } if (cOutputs.Length == 0) { tx = new ContractTransaction(); } else { UInt160[] accounts = from == null?GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).ToArray() : new[] { from }; HashSet <UInt160> sAttributes = new HashSet <UInt160>(); using (ScriptBuilder sb = new ScriptBuilder()) { foreach (var output in cOutputs) { byte[] script; using (ScriptBuilder sb2 = new ScriptBuilder()) { foreach (UInt160 account in accounts) { sb2.EmitAppCall(output.AssetId, "balanceOf", account); } sb2.Emit(OpCode.DEPTH, OpCode.PACK); script = sb2.ToArray(); } ApplicationEngine engine = ApplicationEngine.Run(script); if (engine.State.HasFlag(VMState.FAULT)) { return(null); } var balances = ((IEnumerable <StackItem>)(VMArray) engine.EvaluationStack.Pop()).Reverse().Zip(accounts, (i, a) => new { Account = a, Value = i.GetBigInteger() }).ToArray(); BigInteger sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); if (sum < output.Value) { return(null); } if (sum != output.Value) { balances = balances.OrderByDescending(p => p.Value).ToArray(); BigInteger amount = output.Value; int i = 0; while (balances[i].Value <= amount) { amount -= balances[i++].Value; } if (amount == BigInteger.Zero) { balances = balances.Take(i).ToArray(); } else { balances = balances.Take(i).Concat(new[] { balances.Last(p => p.Value >= amount) }).ToArray(); } sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); } sAttributes.UnionWith(balances.Select(p => p.Account)); for (int i = 0; i < balances.Length; i++) { BigInteger value = balances[i].Value; if (i == 0) { BigInteger change = sum - output.Value; if (change > 0) { value -= change; } } sb.EmitAppCall(output.AssetId, "transfer", balances[i].Account, output.Account, value); sb.Emit(OpCode.THROWIFNOT); } } byte[] nonce = new byte[8]; rand.NextBytes(nonce); sb.Emit(OpCode.RET, nonce); tx = new InvocationTransaction { Version = 1, Script = sb.ToArray() }; } attributes.AddRange(sAttributes.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = p.ToArray() })); } tx.Attributes = attributes.ToArray(); tx.Inputs = new CoinReference[0]; tx.Outputs = outputs.Where(p => p.IsGlobalAsset).Select(p => p.ToTxOutput()).ToArray(); tx.Scripts = new Witness[0]; if (tx is InvocationTransaction itx) { ApplicationEngine engine = ApplicationEngine.Run(itx.Script, itx); if (engine.State.HasFlag(VMState.FAULT)) { return(null); } tx = new InvocationTransaction { Version = itx.Version, Script = itx.Script, Gas = InvocationTransaction.GetGas(engine.GasConsumed), Attributes = itx.Attributes, Inputs = itx.Inputs, Outputs = itx.Outputs }; } tx = MakeTransaction(tx, from, change_address, fee); return(tx); }
//Transfer NEP-5 Asset public static Transaction CreateNep5Transfer(SignDelegate sign) { var from = "AS8UDW7aLhrywLVHFL3ny5tSBaVhWTeZjT".ToScriptHash(); var assetId = new UInt160("ceab719b8baa2310f232ee0d277c061704541cfb".HexToBytes().Reverse().ToArray()); var to = "AS8UDW7aLhrywLVHFL3ny5tSBaVhWTeZjT".ToScriptHash(); var value = 100; //交易输入是 1 GAS var inputs = new List <CoinReference> { //coin reference A new CoinReference() { PrevHash = new UInt256("0x51ac4f7f1662d8c9379ccce3fa7cd2085b9a865edfa53ad892352a41768dd1de".Remove(0, 2).HexToBytes().Reverse().ToArray()), PrevIndex = 0 } }.ToArray(); //交易输出是 0.999 GAS,找回到原地址 var outputs = new List <TransactionOutput> { new TransactionOutput() { AssetId = Blockchain.UtilityToken.Hash, //Asset Id, this is NEO ScriptHash = "AJd31a8rYPEBkY1QSxpsGy8mdU4vTYTD4U".ToScriptHash(), //Receiver Value = new Fixed8((long)(0.999 * (long)Math.Pow(10, 8))) //Value (satoshi unit) } }.ToArray(); //则手续费是 0.001 GAS //Query Balances using (ScriptBuilder sb2 = new ScriptBuilder()) { byte[] script; sb2.EmitAppCall(assetId, "balanceOf", from); sb2.Emit(OpCode.DEPTH, OpCode.PACK); script = sb2.ToArray(); ApplicationEngine engine = ApplicationEngine.Run(script); if (engine.State.HasFlag(VMState.FAULT)) { return(null); } var balances = ((VMArray)engine.ResultStack.Pop())[0]; BigInteger sum = balances.GetBigInteger(); if (sum < value) { Console.WriteLine("Insufficient balance"); return(null); } } //Transfer using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitAppCall(assetId, "transfer", from, to, value); sb.Emit(OpCode.THROWIFNOT); byte[] nonce = new byte[8]; Random rand = new Random(); rand.NextBytes(nonce); sb.Emit(OpCode.RET, nonce); var tx = new InvocationTransaction { Version = 1, Script = sb.ToArray(), Outputs = outputs, Inputs = inputs, Attributes = new TransactionAttribute[0], Witnesses = new Witness[0] }; return(sign.Invoke(tx)); } }
private void button5_Click(object sender, EventArgs e) { byte[] script; try { script = textBox6.Text.Trim().HexToBytes(); } catch (FormatException ex) { MessageBox.Show(ex.Message); return; } if (tx == null) { tx = new InvocationTransaction(); } tx.Version = 1; tx.Script = script; tx.Attributes = temp_signatures.Select(p => p.Unwrap()).ToArray(); if (tx.Inputs == null) { tx.Inputs = new CoinReference[0]; } if (tx.Outputs == null) { tx.Outputs = new TransactionOutput[0]; } if (tx.Witnesses == null) { tx.Witnesses = new Witness[0]; } if (tx.Attributes != null) { try { ContractParametersContext context; context = new ContractParametersContext(tx); Program.CurrentWallet.Sign(context); tx.Witnesses = context.GetWitnesses(); } catch (InvalidOperationException) { MessageBox.Show(Strings.UnsynchronizedBlock); return; } } else { tx.Witnesses = new Witness[0]; } using (ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx, testMode: true)) { StringBuilder sb = new StringBuilder(); sb.AppendLine($"VM State: {engine.State}"); sb.AppendLine($"Gas Consumed: {engine.GasConsumed}"); sb.AppendLine($"Evaluation Stack: {new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson()))}"); JObject notifications = engine.Service.Notifications.Select(q => { JObject notification = new JObject(); notification["contract"] = q.ScriptHash.ToString(); try { notification["state"] = q.State.ToParameter().ToJson(); } catch (InvalidOperationException) { notification["state"] = "error: recursive reference"; } return(notification); }).ToArray(); sb.AppendLine($"Notifications: {notifications}"); textBox7.Text = sb.ToString(); if (!engine.State.HasFlag(VMState.FAULT)) { tx.Gas = engine.GasConsumed - Fixed8.FromDecimal(10); if (tx.Gas < Fixed8.Zero) { tx.Gas = Fixed8.Zero; } tx.Gas = tx.Gas.Ceiling(); Fixed8 fee = tx.Gas; label7.Text = fee + " gas"; button3.Enabled = true; } else { MessageBox.Show(Strings.ExecutionFailed); } } }
private static JArray GetMempoolTransaction(UInt160 ScriptHash) { JArray result = new JArray(); foreach (Transaction tx in LocalNode.GetMemoryPool()) { UInt160 from_script = null; UInt160 to_script = null; JObject obj = new JObject(); if (tx is ContractTransaction) { try { foreach (CoinReference input in tx.Inputs) { Transaction inputTx = Blockchain.Default.GetTransaction(input.PrevHash); if (inputTx != null) { from_script = inputTx.Outputs[input.PrevIndex].ScriptHash; } else { foreach (Transaction inTx in LocalNode.GetMemoryPool()) { if (inTx.Hash.Equals(input.PrevHash)) { from_script = inTx.Outputs[input.PrevIndex].ScriptHash; } } } } } catch (Exception ex) { } if (tx.Outputs.Length > 0) { to_script = tx.Outputs[0].ScriptHash; } Dictionary <UInt256, Fixed8> balance = new Dictionary <UInt256, Fixed8>(); Dictionary <UInt256, Fixed8> fee = new Dictionary <UInt256, Fixed8>(); for (int i = 0; i < tx.Outputs.Length; i++) { AssetState state = Blockchain.Default.GetAssetState(tx.Outputs[i].AssetId); if (tx.Outputs[i].ScriptHash.Equals(to_script)) { if (balance.ContainsKey(tx.Outputs[i].AssetId)) { balance[tx.Outputs[i].AssetId] += tx.Outputs[i].Value; } else { balance[tx.Outputs[i].AssetId] = tx.Outputs[i].Value; } } if (!fee.ContainsKey(tx.Outputs[i].AssetId)) { if (tx.Outputs[i].Fee != Fixed8.Zero) { fee[tx.Outputs[i].AssetId] = tx.Outputs[i].Fee; } } } Fixed8 qrgFee = fee.Sum(p => p.Value); if (balance.Count > 0) { UInt256 assetId = balance.First().Key; Fixed8 value = balance.Sum(p => p.Value); AssetState assetState = Blockchain.Default.GetAssetState(assetId); obj["asset"] = assetState.GetName(); obj["amount"] = value.ToString(); obj["fee"] = qrgFee.ToString(); } } else if (tx is ClaimTransaction) { ClaimTransaction claimTx = (ClaimTransaction)tx; if (claimTx.Outputs.Length > 0) { from_script = to_script = tx.Outputs[0].ScriptHash; } Dictionary <UInt256, Fixed8> balance = new Dictionary <UInt256, Fixed8>(); Dictionary <UInt256, Fixed8> fee = new Dictionary <UInt256, Fixed8>(); for (int i = 0; i < tx.Outputs.Length; i++) { AssetState state = Blockchain.Default.GetAssetState(tx.Outputs[i].AssetId); if (tx.Outputs[i].ScriptHash.Equals(to_script)) { if (balance.ContainsKey(tx.Outputs[i].AssetId)) { balance[tx.Outputs[i].AssetId] += tx.Outputs[i].Value; } else { balance[tx.Outputs[i].AssetId] = tx.Outputs[i].Value; } } if (!fee.ContainsKey(tx.Outputs[i].AssetId)) { if (tx.Outputs[i].Fee != Fixed8.Zero) { fee[tx.Outputs[i].AssetId] = tx.Outputs[i].Fee; } } } obj["asset"] = Blockchain.UtilityToken.Name; obj["amount"] = balance[Blockchain.UtilityToken.Hash].value.ToString(); obj["fee"] = Fixed8.Zero.ToString(); } else if (tx is InvocationTransaction) { InvocationTransaction invocTx = (InvocationTransaction)tx; try { foreach (CoinReference input in tx.Inputs) { from_script = tx.References[input].ScriptHash; } } catch (Exception ex) { } obj["asset"] = Blockchain.UtilityToken.Name; obj["amount"] = invocTx.Gas.ToString(); obj["fee"] = Fixed8.Zero.ToString(); } else if (tx is IssueTransaction) { AssetState state = Blockchain.Default.GetAssetState(Blockchain.UtilityToken.Hash); IssueTransaction issueTx = (IssueTransaction)tx; try { foreach (CoinReference input in tx.Inputs) { from_script = tx.References[input].ScriptHash; } } catch (Exception ex) { } if (tx.Outputs.Length > 0) { to_script = tx.Outputs[0].ScriptHash; } if (tx.Outputs.Length > 0) { state = Blockchain.Default.GetAssetState(tx.Outputs[0].AssetId); } Fixed8 totalAmount = Fixed8.Zero; foreach (TransactionOutput output in tx.Outputs) { if (output.AssetId != Blockchain.UtilityToken.Hash) { totalAmount += output.Value; } } Fixed8 fee = issueTx.NetworkFee + issueTx.SystemFee; obj["asset"] = state.GetName(); obj["amount"] = totalAmount.ToString(); obj["fee"] = fee.ToString(); } if (!ScriptHash.Equals(from_script) && !ScriptHash.Equals(to_script)) { continue; } if (from_script != null) { obj["from"] = Wallet.ToAddress(from_script); } else { obj["from"] = ""; } if (to_script != null) { obj["to"] = Wallet.ToAddress(to_script); } else { obj["to"] = ""; } obj["txType"] = tx.Type; obj["blockHeight"] = Fixed8.Zero.ToString(); obj["txid"] = tx.Hash.ToString(); obj["timestamp"] = DateTime.Now.ToTimestamp(); obj["status"] = 0; result.Add(obj); } return(result); }
protected override JObject Process(string method, JArray _params) { switch (method) { case "getbalance": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied."); } else { UInt256 assetId = UInt256.Parse(_params[0].AsString()); IEnumerable <Coin> coins = Program.Wallet.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent) && p.Output.AssetId.Equals(assetId)); JObject json = new JObject(); 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(); return(json); } case "sendtoaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UIntBase assetId = UIntBase.Parse(_params[0].AsString()); AssetDescriptor descriptor = new AssetDescriptor(assetId); UInt160 scriptHash = Wallet.ToScriptHash(_params[1].AsString()); 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 ? Wallet.ToScriptHash(_params[4].AsString()) : null; Transaction tx = Program.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); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(tx.ToJson()); } else { return(context.ToJson()); } } case "sendmany": if (Program.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 = Wallet.ToScriptHash(to[i]["address"].AsString()) }; 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 ? Wallet.ToScriptHash(_params[2].AsString()) : null; Transaction tx = Program.Wallet.MakeTransaction(null, outputs, change_address: change_address, fee: fee); if (tx == null) { throw new RpcException(-300, "Insufficient funds"); } ContractParametersContext context = new ContractParametersContext(tx); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(tx.ToJson()); } else { return(context.ToJson()); } } case "getnewaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { return(Program.Wallet.CreateAccount().Address); } case "dumpprivkey": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UInt160 scriptHash = Wallet.ToScriptHash(_params[0].AsString()); WalletAccount account = Program.Wallet.GetAccount(scriptHash); return(account.GetKey().Export()); } case "invoke": case "invokefunction": case "invokescript": JObject result = base.Process(method, _params); if (Program.Wallet != null) { InvocationTransaction tx = new InvocationTransaction { Version = 1, Script = result["script"].AsString().HexToBytes(), Gas = Fixed8.Parse(result["gas_consumed"].AsString()) }; tx.Gas -= Fixed8.FromDecimal(10); if (tx.Gas < Fixed8.Zero) { tx.Gas = Fixed8.Zero; } tx.Gas = tx.Gas.Ceiling(); tx = Program.Wallet.MakeTransaction(tx); if (tx != null) { ContractParametersContext context = new ContractParametersContext(tx); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); } else { tx = null; } } result["tx"] = tx?.ToArray().ToHexString(); } return(result); default: return(base.Process(method, _params)); } }
public bool InvokeTransactionOnBlockchain(InvocationTransaction tx, UInt160 contractHash, InvokeOptions options) { Blockchain.PersistCompleted += Blockchain_PersistCompleted; if (options.AttachedNeo > Fixed8.Zero) { var neoOutput = new TransactionOutput(); neoOutput.AssetId = Blockchain.GoverningToken.Hash; neoOutput.Value = options.AttachedNeo; neoOutput.ScriptHash = contractHash; var transactionOutputs = tx.Outputs.ToList(); transactionOutputs.Add(neoOutput); tx.Outputs = transactionOutputs.ToArray(); } if (options.AttachedGas > Fixed8.Zero) { var neoOutput = new TransactionOutput(); neoOutput.AssetId = Blockchain.UtilityToken.Hash; neoOutput.Value = options.AttachedGas; neoOutput.ScriptHash = contractHash; var transactionOutputs = tx.Outputs.ToList(); transactionOutputs.Add(neoOutput); tx.Outputs = transactionOutputs.ToArray(); } // if (Client.CurrentWallet.WalletHeight > Blockchain.Default.HeaderHeight) // { // throw new ApplicationException("Wallet height is ahead of the blockchain height! It may need a rebuild"); // } var walletSyncAttempts = 0; while (Client.CurrentWallet.NeoWallet.WalletHeight < Blockchain.Default.HeaderHeight) { walletSyncAttempts++; Thread.Sleep(1000); //get the wallet in sync or else MakeTransaction will fail if (walletSyncAttempts >= 30) { throw new WalletException("could not get the wallet in sync after 30 attempts"); } } var walletTx = Client.CurrentWallet.NeoWallet.MakeTransaction(new InvocationTransaction { Version = tx.Version, Script = tx.Script, Gas = tx.Gas, Attributes = tx.Attributes, Inputs = tx.Inputs, Outputs = tx.Outputs }, fee: options.Fee); //include a small fee if (walletTx == null) { throw new NeoTransactionBuildException("Wallet TX was null. Possibly insufficient funds. If not wallet may need a rebuild"); } var context = new ContractParametersContext(walletTx); var sign = Client.CurrentWallet.NeoWallet.Sign(context); //fail here with index out of bounds if (context.Completed) { context.Verifiable.Scripts = context.GetScripts(); Client.CurrentWallet.NeoWallet.ApplyTransaction(walletTx); //changes with different versions of NEO //Wallet.ApplyTransaction(walletTx); var relay = _node.Relay(walletTx); //var originalHeight = Blockchain.Default.Height; //store the height we sent at then wait for the next block //possibly check if sign/relay/save has actually worked? //while (Blockchain.Default.Height <= originalHeight + 2) Thread.Sleep(1000); //wait for next block //while (this._wallet.WalletHeight <= originalHeight + 2) Thread.Sleep(1000); //wait for wallet to sync too! TxFound = false; WatchForTx = walletTx.Hash; Console.WriteLine(walletTx.Hash); //Console.WriteLine(tx.Hash); var count = 0; while (TxFound == false) //wait until the transaction is confirmed { Thread.Sleep(1000); count++; if (count > 30) { Blockchain.PersistCompleted -= Blockchain_PersistCompleted; return(false); } } // while(Client.CurrentWallet.WalletHeight < TxFoundInBlock && WalletIndexer.IndexHeight < TxFoundInBlock) //make sure the wallet gets this block // { // Thread.Sleep(1000); // } //ensure we have an unspent coin back to use? //seems like the WalletIndexer is running on a background thread so the block may not be fully processed //e.g. the unconfirmed array in the wallet may not be updated in real time. The only event we have is that BlockChain.PersistCompleted was done which means we have the block stored to disk locally, this does not mean //that the wallet has completed updating based on the new block! while (Client.CurrentWallet.NeoWallet.FindUnspentCoins(Blockchain.UtilityToken.Hash, walletTx.NetworkFee, new UInt160[] { Client.CurrentWallet.GetAddresses().First() }) == null) { Thread.Sleep(500); } } else { Blockchain.PersistCompleted -= Blockchain_PersistCompleted; throw new ApplicationException("Incompleted Signature"); } Blockchain.PersistCompleted -= Blockchain_PersistCompleted; return(true); }
public NotifyMessages InvokeBlockchainMethod(UInt160 contractHash, string methodToInvoke, InvokeOptions options, params ContractParameter[] userSpecifiedParameters) { //TODO: refactor this as it is copy/paste from Invoking locally now. var parameters = new List <ContractParameter>(); var methodParameter = new ContractParameter(ContractParameterType.ByteArray); methodParameter.Value = Encoding.UTF8.GetBytes(methodToInvoke); parameters.Add(methodParameter); if (userSpecifiedParameters.Length > 0) { var args = new ContractParameter(); args.Type = ContractParameterType.Array; args.Value = userSpecifiedParameters.ToList(); parameters.Add(args); } else { parameters.Add(new ContractParameter(ContractParameterType.Array)); } using (var sb = new ScriptBuilder()) { for (var i = 0; i < options.NumberOfTimesToRunInTransaction; i++) { PushParameters(sb, parameters); sb.EmitAppCall(contractHash, false); } //useful for debugging to compare to what neo GUI does var customScriptText = sb.ToArray().ToHexString(); //UI: 00c1013151c1086765744f776e6572677917c149bf660121556a4bc88b6adcb0b12b04f9 var tx = new InvocationTransaction(); tx.Version = 1; tx.Script = sb.ToArray(); if (tx.Attributes == null) { tx.Attributes = new TransactionAttribute[0]; } if (tx.Inputs == null) { tx.Inputs = new CoinReference[0]; } if (tx.Outputs == null) { tx.Outputs = new TransactionOutput[0]; } if (tx.Scripts == null) { tx.Scripts = new Witness[0]; } var l = tx.Scripts.ToList(); var messages = new NotifyMessages(); EventHandler <NotifyEventArgs> handleNotify = (sender, args) => { messages.AddMessage(args); }; StateReader.Notify += handleNotify; //listen to the blockchain and associate any messages you get to this transaction InvokeTransactionOnBlockchain(tx, contractHash, options); StateReader.Notify -= handleNotify; //stop listening. return(messages); } }
private bool OnInvokeCommand(string[] args) { var scriptHash = UInt160.Parse(args[1]); List <ContractParameter> contractParameters = new List <ContractParameter>(); for (int i = 3; i < args.Length; i++) { contractParameters.Add(new ContractParameter() { // TODO: support contract params of type other than string. Type = ContractParameterType.String, Value = args[i] }); } ContractParameter[] parameters = { new ContractParameter { Type = ContractParameterType.String, Value = args[2] }, new ContractParameter { Type = ContractParameterType.Array, Value = contractParameters.ToArray() } }; var tx = new InvocationTransaction(); using (ScriptBuilder scriptBuilder = new ScriptBuilder()) { scriptBuilder.EmitAppCall(scriptHash, parameters); Console.WriteLine($"Invoking script with: '{scriptBuilder.ToArray().ToHexString()}'"); tx.Script = scriptBuilder.ToArray(); } if (tx.Attributes == null) { tx.Attributes = new TransactionAttribute[0]; } if (tx.Inputs == null) { tx.Inputs = new CoinReference[0]; } if (tx.Outputs == null) { tx.Outputs = new TransactionOutput[0]; } if (tx.Witnesses == null) { tx.Witnesses = new Witness[0]; } ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx); StringBuilder sb = new StringBuilder(); sb.AppendLine($"VM State: {engine.State}"); sb.AppendLine($"Gas Consumed: {engine.GasConsumed}"); sb.AppendLine( $"Evaluation Stack: {new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson()))}"); Console.WriteLine(sb.ToString()); if (engine.State.HasFlag(VMState.FAULT)) { Console.WriteLine("Engine faulted."); return(true); } tx = DecorateInvocationTransaction(tx); return(SignAndSendTx(tx)); }
protected override JObject Process(string method, JArray _params) { switch (method) { case "getapplicationlog": { UInt256 hash = UInt256.Parse(_params[0].AsString()); string path = Path.Combine(Settings.Default.Paths.ApplicationLogs, $"{hash}.json"); return(File.Exists(path) ? JObject.Parse(File.ReadAllText(path)) : throw new RpcException(-100, "Unknown transaction")); } case "getbalance": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied."); } else { UInt256 assetId = UInt256.Parse(_params[0].AsString()); IEnumerable <Coin> coins = Program.Wallet.GetCoins().Where(p => !p.State.HasFlag(CoinState.Spent) && p.Output.AssetId.Equals(assetId)); JObject json = new JObject(); 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(); return(json); } case "listaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied."); } else { return(Program.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 (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UIntBase assetId = UIntBase.Parse(_params[0].AsString()); AssetDescriptor descriptor = new AssetDescriptor(assetId); UInt160 from = Wallet.ToScriptHash(_params[1].AsString()); UInt160 to = Wallet.ToScriptHash(_params[2].AsString()); 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 ? Wallet.ToScriptHash(_params[5].AsString()) : null; Transaction tx = Program.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); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(tx.ToJson()); } else { return(context.ToJson()); } } case "sendtoaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UIntBase assetId = UIntBase.Parse(_params[0].AsString()); AssetDescriptor descriptor = new AssetDescriptor(assetId); UInt160 scriptHash = Wallet.ToScriptHash(_params[1].AsString()); 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 ? Wallet.ToScriptHash(_params[4].AsString()) : null; Transaction tx = Program.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); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(tx.ToJson()); } else { return(context.ToJson()); } } case "sendmany": if (Program.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 = Wallet.ToScriptHash(to[i]["address"].AsString()) }; 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 ? Wallet.ToScriptHash(_params[2].AsString()) : null; Transaction tx = Program.Wallet.MakeTransaction(null, outputs, change_address: change_address, fee: fee); if (tx == null) { throw new RpcException(-300, "Insufficient funds"); } ContractParametersContext context = new ContractParametersContext(tx); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(tx.ToJson()); } else { return(context.ToJson()); } } case "getnewaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { WalletAccount account = Program.Wallet.CreateAccount(); if (Program.Wallet is NEP6Wallet wallet) { wallet.Save(); } return(account.Address); } case "dumpprivkey": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UInt160 scriptHash = Wallet.ToScriptHash(_params[0].AsString()); WalletAccount account = Program.Wallet.GetAccount(scriptHash); return(account.GetKey().Export()); } case "signdata": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { byte[] data = System.Text.Encoding.UTF8.GetBytes(_params[0].AsString()); KeyPair keys = Program.Wallet.GetAccounts().First().GetKey(); using (keys.Decrypt()) { byte[] pubkey = keys.PublicKey.EncodePoint(false).Skip(1).ToArray(); return(Convert.ToBase64String(Crypto.Default.Sign(data, keys.PrivateKey, pubkey))); } } case "invoke": case "invokefunction": case "invokescript": JObject result = base.Process(method, _params); if (Program.Wallet != null) { InvocationTransaction tx = new InvocationTransaction { Version = 1, Script = result["script"].AsString().HexToBytes(), Gas = Fixed8.Parse(result["gas_consumed"].AsString()) }; tx.Gas -= Fixed8.FromDecimal(10); if (tx.Gas < Fixed8.Zero) { tx.Gas = Fixed8.Zero; } tx.Gas = tx.Gas.Ceiling(); tx = Program.Wallet.MakeTransaction(tx); if (tx != null) { ContractParametersContext context = new ContractParametersContext(tx); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); } else { tx = null; } } result["tx"] = tx?.ToArray().ToHexString(); } return(result); case "executescript": { if (Program.Wallet == null) { return(false); } byte[] script = _params[0].AsString().HexToBytes(); ApplicationEngine engine = ApplicationEngine.Run(script); Fixed8 gas = engine.GasConsumed - Fixed8.FromDecimal(10); if (gas < Fixed8.Zero) { gas = Fixed8.Zero; } gas = gas.Ceiling(); Fixed8 fee = gas.Equals(Fixed8.Zero) ? Fixed8.FromDecimal(0.001m) : Fixed8.Zero; InvocationTransaction tx = Program.Wallet.MakeTransaction(new InvocationTransaction { Version = 1, Script = script, Gas = gas, Attributes = new TransactionAttribute[0], Inputs = new CoinReference[0], Outputs = new TransactionOutput[0] }, fee: fee); if (tx == null) { return(false); } ContractParametersContext context; try { context = new ContractParametersContext(tx); } catch (Exception) { return(false); } Program.Wallet.Sign(context); if (!context.Completed) { return(false); } context.Verifiable.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); return(LocalNode.Relay(tx)); } default: return(base.Process(method, _params)); } }
public static void Verify() { var inputs = new List <CoinReference> { new CoinReference() { PrevHash = new UInt256("0xdb4c4f1a17b365a68497ef0e118db89b827db24f67ee71d317d38c68c84424ef".Remove(0, 2).HexToBytes().Reverse().ToArray()), PrevIndex = 0 //1 } }.ToArray(); var outputs = new List <TransactionOutput> { new TransactionOutput() { AssetId = Blockchain.UtilityToken.Hash, //Asset Id, this is GAS ScriptHash = SgasAddress, //SGAS 地址 Value = new Fixed8((long)(1 * (long)Math.Pow(10, 8))) //Value } }.ToArray(); Transaction tx = null; var verificationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(2); sb.EmitPush("1"); verificationScript = sb.ToArray(); } var witness = new Witness { InvocationScript = verificationScript, //未部署的合约不能执行 Storage.Get() 方法,所以要将合约部署,而不是调用本地的 AVM 文件 //VerificationScript = File.ReadAllBytes("C:\\Users\\chenz\\Documents\\1Code\\chenzhitong\\NeoContract5\\NeoContract\\bin\\Debug\\SGAS.avm") VerificationScript = Blockchain.Default.GetContract(ScriptHash).Script }; tx = new InvocationTransaction { Version = 0, Script = new byte[0], Outputs = outputs, Inputs = inputs, Attributes = new TransactionAttribute[0], Scripts = new Witness[] { witness } }; if (tx == null) { Console.WriteLine("Create Transaction Failed"); Console.ReadLine(); return; } try { tx = Transaction.DeserializeFrom(tx.ToArray()); } catch (Exception) { Console.WriteLine("Invalid Transaction Format"); } if (tx.Verify(new List <Transaction> { tx })) { Console.WriteLine("Verify Transaction: True"); Console.WriteLine("Raw Transaction:"); Console.WriteLine(tx.ToArray().ToHexString()); //Console.WriteLine(tx.ToJson()); //Then Call neo-cli API:sendrawtransaction in postman. } else { Console.WriteLine("Verify Transaction: False"); } }
//SGAS MintTokens public static void MintTokens() { var inputs = new List <CoinReference> { //coin reference A new CoinReference() { PrevHash = new UInt256("0xf5088ce508d86197c991ff0ef7651ddf01f3e555f257039c972082250e899210".Remove(0, 2).HexToBytes().Reverse().ToArray()), PrevIndex = 0 //16639 GAS } }.ToArray(); var outputs = new List <TransactionOutput> { new TransactionOutput() { AssetId = Blockchain.UtilityToken.Hash, //Asset Id, this is GAS ScriptHash = SgasAddress, //SGAS 地址 Value = new Fixed8((long)(1 * (long)Math.Pow(10, 8))) //Value } }.ToArray(); Transaction tx = null; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitAppCall(ScriptHash, "mintTokens"); sb.Emit(OpCode.THROWIFNOT); byte[] nonce = new byte[8]; Random rand = new Random(); rand.NextBytes(nonce); sb.Emit(OpCode.RET, nonce); tx = new InvocationTransaction { Version = 1, Script = sb.ToArray(), Outputs = outputs, Inputs = inputs, Attributes = new TransactionAttribute[0], Scripts = new Witness[0] }; } if (tx == null) { Console.WriteLine("Create Transaction Failed"); Console.ReadLine(); return; } //Open wallet var wallet = new Neo.Implementations.Wallets.NEP6.NEP6Wallet("0.json"); try { wallet.Unlock("1"); } catch (Exception) { Console.WriteLine("password error"); } //Sign in wallet var context = new ContractParametersContext(tx); wallet.Sign(context); if (context.Completed) { Console.WriteLine("Sign Successful"); tx.Scripts = context.GetScripts(); } else { Console.WriteLine("Sign Faild"); } try { tx = Transaction.DeserializeFrom(tx.ToArray()); } catch (Exception) { Console.WriteLine("Invalid Transaction Format"); } Console.WriteLine("Verify Transaction:" + tx.Verify(new List <Transaction> { tx })); Console.WriteLine("Raw Transaction:"); Console.WriteLine(tx.ToArray().ToHexString()); //Then Call neo-cli API:sendrawtransaction in postman. }
public static void Refund() { var inputs = new List <CoinReference> { new CoinReference() { PrevHash = new UInt256("0xdb4c4f1a17b365a68497ef0e118db89b827db24f67ee71d317d38c68c84424ef".Remove(0, 2).HexToBytes().Reverse().ToArray()), PrevIndex = 0 //1 } }.ToArray(); var outputs = new List <TransactionOutput> { new TransactionOutput() { AssetId = Blockchain.UtilityToken.Hash, //Asset Id, this is GAS ScriptHash = SgasAddress, //SGAS 地址 Value = new Fixed8((long)(1 * (long)Math.Pow(10, 8))) //Value } }.ToArray(); Transaction tx = null; var applicationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitAppCall(ScriptHash, "refund", User); sb.Emit(OpCode.THROWIFNOT); applicationScript = sb.ToArray(); } tx = new InvocationTransaction { Version = 0, Script = applicationScript, Outputs = outputs, Inputs = inputs, Attributes = new TransactionAttribute[] { new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = User.ToArray()//附加人的 Script Hash } } }; if (tx == null) { Console.WriteLine("Create Transaction Failed"); Console.ReadLine(); return; } //Open wallet var wallet = new Neo.Implementations.Wallets.NEP6.NEP6Wallet("0.json"); try { wallet.Unlock("1"); } catch (Exception) { Console.WriteLine("password error"); } //Sign in wallet 生成附加人的签名 var context = new ContractParametersContext(tx); var additionalSignature = new byte[0]; foreach (UInt160 hash in context.ScriptHashes) { if (hash == User) { WalletAccount account = wallet.GetAccount(hash); if (account?.HasKey != true) { continue; } KeyPair key = account.GetKey(); additionalSignature = context.Verifiable.Sign(key); } } var additionalVerificationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(additionalSignature); additionalVerificationScript = sb.ToArray(); } var verificationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(2); sb.EmitPush("1"); verificationScript = sb.ToArray(); } var witness = new Witness { InvocationScript = verificationScript, VerificationScript = Blockchain.Default.GetContract(ScriptHash).Script }; var additionalWitness = new Witness { InvocationScript = additionalVerificationScript, VerificationScript = UserScript }; var witnesses = new Witness[2] { witness, additionalWitness }; tx.Scripts = witnesses.ToList().OrderBy(p => p.ScriptHash).ToArray(); try { tx = Transaction.DeserializeFrom(tx.ToArray()); } catch (Exception) { Console.WriteLine("Invalid Transaction Format"); } if (tx.Verify(new List <Transaction> { tx })) { Console.WriteLine("Verify Transaction: True"); Console.WriteLine("Raw Transaction:"); Console.WriteLine(tx.ToArray().ToHexString()); //Console.WriteLine(tx.ToJson()); //Then Call neo-cli API:sendrawtransaction in postman. } else { Console.WriteLine("Verify Transaction: False"); } }
public static string SendInvocationTransaction(byte[] script, int m, KeyPair[] keypairs, string chainHash, Fixed8 gasLimit, Fixed8 gasPrice) { InvocationTransaction tx = MakeMultiSignatureTransaction(script, m, keypairs, gasLimit, gasPrice); return(SendRawTransaction(tx.ToArray().ToHexString(), chainHash)); }
public T InvokeLocalMethod <T>(UInt160 scriptHash, string methodName, params ContractParameter[] userSpecifiedParameters) { var parameters = new List <ContractParameter>(); var methodParameter = new ContractParameter(ContractParameterType.ByteArray); methodParameter.Value = Encoding.UTF8.GetBytes(methodName); parameters.Add(methodParameter); if (userSpecifiedParameters.Length > 0) { var args = new ContractParameter(); args.Type = ContractParameterType.Array; args.Value = userSpecifiedParameters.ToList(); parameters.Add(args); } else { parameters.Add(new ContractParameter(ContractParameterType.Array)); } //parameters.Add(new ContractParameter(ContractParameterType.Array)); var walletSyncAttempts = 0; while (Client.CurrentWallet.NeoWallet.WalletHeight < Blockchain.Default.HeaderHeight) { walletSyncAttempts++; Thread.Sleep(1000); //get the wallet in sync or else MakeTransaction will fail if (walletSyncAttempts >= 30) { throw new WalletException("could not get the wallet in sync after 30 attempts"); } } using (var sb = new ScriptBuilder()) { PushParameters(sb, parameters); sb.EmitAppCall(scriptHash, false); var customScriptText = sb.ToArray().ToHexString(); var tx = new InvocationTransaction(); tx.Version = 1; tx.Script = sb.ToArray(); if (tx.Attributes == null) { tx.Attributes = new TransactionAttribute[0]; } if (tx.Inputs == null) { tx.Inputs = new CoinReference[0]; } if (tx.Outputs == null) { tx.Outputs = new TransactionOutput[0]; } if (tx.Scripts == null) { tx.Scripts = new Witness[0]; } // InvocationTransaction walletTx = null; // walletTx = Client.CurrentWallet.NeoWallet.MakeTransaction(new InvocationTransaction // { // Version = tx.Version, // Script = tx.Script, // Gas = tx.Gas, // Attributes = tx.Attributes, // Inputs = tx.Inputs, // Outputs = tx.Outputs // }, fee: Fixed8.Zero); //all transactions need something so include a small fee //if (walletTx == null) throw new NeoTransactionBuildException("Creating a TX resulted in a null transaction"); //TODO: make the address a selection tx.Attributes = new TransactionAttribute[] { new TransactionAttribute() { Usage = TransactionAttributeUsage.Script, Data = Client.CurrentWallet.GetAddresses().First().ToArray() } }; // ContractParametersContext context; // try // { // context = new ContractParametersContext(tx); // } // catch (InvalidOperationException) // { // throw new ApplicationException("unsynchronized block"); // } // // var sign = Client.CurrentWallet.NeoWallet.Sign(context); var engine = ApplicationEngine.Run(tx.Script, tx); var results = new StringBuilder(); results.AppendLine($"Called: {methodName}"); results.AppendLine($"VM State: {engine.State}"); results.AppendLine($"Gas Consumed: {engine.GasConsumed}"); if (engine.State.HasFlag(VMState.FAULT)) { throw new NeoExecutionException(); } var result = engine.EvaluationStack.Pop(); if (typeof(T) == typeof(string)) { return((T)Convert.ChangeType(result.GetString(), typeof(T))); } if (typeof(T) == typeof(bool)) { return((T)Convert.ChangeType(result.GetBoolean(), typeof(T))); } if (typeof(T) == typeof(BigInteger)) { return((T)Convert.ChangeType(result.GetBigInteger(), typeof(T))); } if (typeof(T) == typeof(byte[])) { var stackItems = result.GetByteArray(); return((T)Convert.ChangeType(stackItems, typeof(T))); } throw new NotImplementedException(typeof(T).ToString()); } }
static void Main(string[] args) { Console.WriteLine("Hello World!"); // those you need to set up------------------------------------------------------------ rpcClient = new RpcClient("http://localhost:20002"); // your node url, such as "http://47.89.240.111:12332" nep2Key = "6PYLjXkQzADs7r36XQjByJXoggm3beRh6UzxuN59NZiBxFBnm1HPvv3ytM"; // you can find this in your wallet, the "key" field string password = "******"; // your password UInt160 from = "AQzRMe3zyGS8W177xLJfewRRQZY2kddMun".ToScriptHash(); // your account address, such as "APPmjituYcgfNxjuQDy9vP73R2PmhFsYJR" UInt160 scriptHash = UInt160.Parse("0x6b4f6926c28523519c758ec2015a60ddfe8b37dc"); // your contract script hash, such as "0x5be5fc0641e44b0003262b3fda775ea60133cb05" //byte[] param = new byte[] { }; // your contract parameter BigInteger param = 2; //------------------------------------------------------------------------------------------- ScriptBuilder sb = new ScriptBuilder().EmitAppCall(scriptHash, "getProxyHash", new ContractParameter[] { new ContractParameter() { Type = ContractParameterType.Integer, Value = param } }); var script = sb.ToArray(); InvocationTransaction itx = new InvocationTransaction(); itx.Inputs = new CoinReference[] { }; itx.Outputs = new TransactionOutput[] { }; itx.Attributes = new TransactionAttribute[] { }; itx.Witnesses = new Witness[] { }; itx.Version = 1; itx.Script = script; itx.Gas = GetGasConsumed(script); Fixed8 fee = itx.Gas; if (itx.Size > 1024) { fee += Fixed8.FromDecimal(0.001m); fee += Fixed8.FromDecimal(itx.Size * 0.00001m); } var(inputs, sum) = GetTransactionInputs(from, Blockchain.UtilityToken.Hash, fee); if (sum > fee) { itx.Outputs = itx.Outputs.Concat(new[] { new TransactionOutput() { AssetId = Blockchain.UtilityToken.Hash, Value = sum - fee, ScriptHash = from } }).ToArray(); } itx.Inputs = itx.Inputs.Concat(inputs).ToArray(); // sign the itx Random random = new Random(); var nonce = new byte[32]; random.NextBytes(nonce); TransactionAttribute[] attributes = new TransactionAttribute[] { new TransactionAttribute() { Usage = TransactionAttributeUsage.Script, Data = from.ToArray() }, new TransactionAttribute() { Usage = TransactionAttributeUsage.Remark1, Data = nonce } // if a transaction has no inputs and outputs, need to add nonce for duplication }; itx.Attributes = itx.Attributes.Concat(attributes).ToArray(); KeyPair keyPair = WalletHelper.KeyPairFromNep2(nep2Key, password); Witness witness = WalletHelper.CreateTransactionWitness(itx, keyPair); itx.Witnesses = itx.Witnesses.Concat(new[] { witness }).ToArray(); var raw = itx.ToArray(); var txid = rpcClient.SendRawTransaction(raw); Console.WriteLine(txid.ToString()); }
public static void Refund() { var inputs = new List <CoinReference> { new CoinReference() { PrevHash = new UInt256("0x44d5a5ef32c8ec780de59ca59cb799efd1bf3051d9a2c94a2b1d13af34abe7ca".Remove(0, 2).HexToBytes().Reverse().ToArray()), PrevIndex = 0 } }.ToArray(); var outputs = new List <TransactionOutput> { new TransactionOutput() { AssetId = Blockchain.UtilityToken.Hash, //Asset Id, this is GAS ScriptHash = ScriptHash, //CGAS 地址 Value = new Fixed8((long)(9.99 * (long)Math.Pow(10, 8))) } }.ToArray(); Transaction tx = null; var applicationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitAppCall(ScriptHash, "refund", User); sb.Emit(OpCode.THROWIFNOT); applicationScript = sb.ToArray(); } tx = new InvocationTransaction { Version = 0, Script = applicationScript, Outputs = outputs, Inputs = inputs, Attributes = new TransactionAttribute[] { new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = User.ToArray()//附加人的 Script Hash } } }; //Open wallet var wallet = new Neo.Wallets.NEP6.NEP6Wallet(new WalletIndexer("Index_0001E240"), "1.json"); try { wallet.Unlock("11111111"); } catch (Exception) { Console.WriteLine("password error"); } //Sign in wallet 生成附加人的签名 var context = new ContractParametersContext(tx); var additionalSignature = new byte[0]; foreach (UInt160 hash in context.ScriptHashes) { if (hash == User) { WalletAccount account = wallet.GetAccount(hash); if (account?.HasKey != true) { continue; } KeyPair key = account.GetKey(); additionalSignature = context.Verifiable.Sign(key); } } var additionalVerificationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(additionalSignature); additionalVerificationScript = sb.ToArray(); } var verificationScript = new byte[0]; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(2); sb.EmitPush("1"); verificationScript = sb.ToArray(); } var witness = new Witness { InvocationScript = verificationScript, VerificationScript = Blockchain.Singleton.Store.GetContracts().TryGet(ScriptHash).Script }; var additionalWitness = new Witness { InvocationScript = additionalVerificationScript, VerificationScript = UserScript }; var witnesses = new Witness[2] { witness, additionalWitness }; tx.Witnesses = witnesses.ToList().OrderBy(p => p.ScriptHash).ToArray(); Verify(tx); }
private Transaction GenerateTransaction() { var cOutputs = this.Items.Where(p => p.AssetId is UInt160).GroupBy(p => new { AssetId = (UInt160)p.AssetId, Account = p.ScriptHash }, (k, g) => new { AssetId = k.AssetId, Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value), Account = k.Account }).ToArray(); Transaction tx; var attributes = new List <TransactionAttribute>(); if (cOutputs.Length == 0) { tx = new ContractTransaction(); } else { var addresses = this.walletController.GetAccounts().Select(p => p.ScriptHash).ToArray(); var sAttributes = new HashSet <UInt160>(); using (var builder = new ScriptBuilder()) { foreach (var output in cOutputs) { byte[] script; using (var builder2 = new ScriptBuilder()) { foreach (var address in addresses) { builder2.EmitAppCall(output.AssetId, "balanceOf", address); } builder2.Emit(OpCode.DEPTH, OpCode.PACK); script = builder2.ToArray(); } var engine = ApplicationEngine.Run(script); if (engine.State.HasFlag(VMState.FAULT)) { return(null); } var balances = engine.EvaluationStack.Pop().GetArray().Reverse().Zip(addresses, (i, a) => new { Account = a, Value = i.GetBigInteger() }).ToArray(); var sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); if (sum < output.Value) { return(null); } if (sum != output.Value) { balances = balances.OrderByDescending(p => p.Value).ToArray(); var amount = output.Value; var i = 0; while (balances[i].Value <= amount) { amount -= balances[i++].Value; } balances = amount == BigInteger.Zero ? balances.Take(i).ToArray() : balances.Take(i).Concat(new[] { balances.Last(p => p.Value >= amount) }).ToArray(); sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); } sAttributes.UnionWith(balances.Select(p => p.Account)); for (int i = 0; i < balances.Length; i++) { var value = balances[i].Value; if (i == 0) { var change = sum - output.Value; if (change > 0) { value -= change; } } builder.EmitAppCall(output.AssetId, "transfer", balances[i].Account, output.Account, value); builder.Emit(OpCode.THROWIFNOT); } } tx = new InvocationTransaction { Version = 1, Script = builder.ToArray() }; } attributes.AddRange(sAttributes.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = p.ToArray() })); } if (!string.IsNullOrEmpty(remark)) { attributes.Add(new TransactionAttribute { Usage = TransactionAttributeUsage.Remark, Data = Encoding.UTF8.GetBytes(remark) }); } tx.Attributes = attributes.ToArray(); tx.Outputs = this.Items.Where(p => p.AssetId is UInt256).Select(p => p.ToTxOutput()).ToArray(); if (tx is ContractTransaction ctx) { tx = this.walletController.MakeTransaction(ctx); } return(tx); }
public Transaction GetTransaction() { var cOutputs = txOutListBox1.Items.Where(p => p.AssetId is UInt160).GroupBy(p => new { AssetId = (UInt160)p.AssetId, Account = p.ScriptHash }, (k, g) => new { k.AssetId, Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value), k.Account }).ToArray(); Transaction tx; List <TransactionAttribute> attributes = new List <TransactionAttribute>(); if (cOutputs.Length == 0) { tx = new ContractTransaction(); } else { UInt160[] addresses = Program.CurrentWallet.GetAccounts().Select(p => p.ScriptHash).ToArray(); HashSet <UInt160> sAttributes = new HashSet <UInt160>(); using (ScriptBuilder sb = new ScriptBuilder()) { foreach (var output in cOutputs) { var balances = new List <(UInt160 Account, BigInteger Value)>(); using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { ContractState asset = snapshot.Contracts.TryGet(output.AssetId); foreach (UInt160 address in addresses) { StorageKey key = new StorageKey { ScriptHash = asset.ScriptHash, Key = address.ToArray() }; StorageItem item = snapshot.Storages.TryGet(key); balances.Add((address, item == null ? BigInteger.Zero : new BigInteger(item.Value))); } } BigInteger sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); if (sum < output.Value) { return(null); } if (sum != output.Value) { balances = balances.OrderByDescending(p => p.Value).ToList(); BigInteger amount = output.Value; int i = 0; while (balances[i].Value <= amount) { amount -= balances[i++].Value; } if (amount == BigInteger.Zero) { balances = balances.Take(i).ToList(); } else { balances = balances.Take(i).Concat(new[] { balances.Last(p => p.Value >= amount) }).ToList(); } sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); } sAttributes.UnionWith(balances.Select(p => p.Account)); for (int i = 0; i < balances.Count; i++) { BigInteger value = balances[i].Value; if (i == 0) { BigInteger change = sum - output.Value; if (change > 0) { value -= change; } } sb.EmitAppCall(output.AssetId, "transfer", balances[i].Account, output.Account, value); sb.Emit(OpCode.THROWIFNOT); } } tx = new InvocationTransaction { Version = 1, Script = sb.ToArray() }; } attributes.AddRange(sAttributes.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = p.ToArray() })); } if (!string.IsNullOrEmpty(remark)) { attributes.Add(new TransactionAttribute { Usage = TransactionAttributeUsage.Remark, Data = Encoding.UTF8.GetBytes(remark) }); } tx.Attributes = attributes.ToArray(); tx.Outputs = txOutListBox1.Items.Where(p => p.AssetId is UInt256).Select(p => p.ToTxOutput()).ToArray(); if (tx is ContractTransaction ctx) { tx = Program.CurrentWallet.MakeTransaction(ctx, change_address: ChangeAddress, fee: Fee); } return(tx); }
public Transaction GetTransaction() { var cOutputs = txOutListBox1.Items.Where(p => p.AssetId is UInt160).GroupBy(p => new { AssetId = (UInt160)p.AssetId, Account = p.ScriptHash }, (k, g) => new { k.AssetId, Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value), k.Account }).ToArray(); Transaction tx; List <TransactionAttribute> attributes = new List <TransactionAttribute>(); if (comboBoxFrom.SelectedItem == null) { FromAddress = null; } else { FromAddress = ((string)comboBoxFrom.SelectedItem).ToScriptHash(); } if (cOutputs.Length == 0) { tx = new ContractTransaction(); } else { UInt160[] addresses; if (FromAddress != null) { addresses = Program.CurrentWallet.GetAccounts().Where(e => e.ScriptHash.Equals(FromAddress)).Select(p => p.ScriptHash).ToArray(); } else { addresses = Program.CurrentWallet.GetAccounts().Where(e => !e.WatchOnly).Select(p => p.ScriptHash).ToArray(); } HashSet <UInt160> sAttributes = new HashSet <UInt160>(); using (ScriptBuilder sb = new ScriptBuilder()) { foreach (var output in cOutputs) { byte[] script; using (ScriptBuilder sb2 = new ScriptBuilder()) { foreach (UInt160 address in addresses) { sb2.EmitAppCall(output.AssetId, "balanceOf", address); } sb2.Emit(OpCode.DEPTH, OpCode.PACK); script = sb2.ToArray(); } ApplicationEngine engine = ApplicationEngine.Run(script); if (engine.State.HasFlag(VMState.FAULT)) { return(null); } var balances = ((VMArray)engine.ResultStack.Pop()).AsEnumerable().Reverse().Zip(addresses, (i, a) => new { Account = a, Value = i.GetBigInteger() }).Where(p => p.Value != 0).ToArray(); BigInteger sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); if (sum < output.Value) { return(null); } if (sum != output.Value) { balances = balances.OrderByDescending(p => p.Value).ToArray(); BigInteger amount = output.Value; int i = 0; while (balances[i].Value <= amount) { amount -= balances[i++].Value; } if (amount == BigInteger.Zero) { balances = balances.Take(i).ToArray(); } else { balances = balances.Take(i).Concat(new[] { balances.Last(p => p.Value >= amount) }).ToArray(); } sum = balances.Aggregate(BigInteger.Zero, (x, y) => x + y.Value); } sAttributes.UnionWith(balances.Select(p => p.Account)); for (int i = 0; i < balances.Length; i++) { BigInteger value = balances[i].Value; if (i == 0) { BigInteger change = sum - output.Value; if (change > 0) { value -= change; } } sb.EmitAppCall(output.AssetId, "transfer", balances[i].Account, output.Account, value); sb.Emit(OpCode.THROWIFNOT); } } tx = new InvocationTransaction { Version = 1, Script = sb.ToArray() }; } attributes.AddRange(sAttributes.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = p.ToArray() })); } if (!string.IsNullOrEmpty(remark)) { attributes.Add(new TransactionAttribute { Usage = TransactionAttributeUsage.Remark, Data = Encoding.UTF8.GetBytes(remark) }); } tx.Attributes = attributes.ToArray(); tx.Outputs = txOutListBox1.Items.Where(p => p.AssetId is UInt256).Select(p => p.ToTxOutput()).ToArray(); var tempOuts = tx.Outputs; if (tx is ContractTransaction copyTx) { copyTx.Witnesses = new Witness[0]; copyTx = Program.CurrentWallet.MakeTransaction(copyTx, FromAddress, change_address: ChangeAddress, fee: Fee); if (copyTx == null) { return(null); } ContractParametersContext transContext = new ContractParametersContext(copyTx); Program.CurrentWallet.Sign(transContext); if (transContext.Completed) { copyTx.Witnesses = transContext.GetWitnesses(); } if (copyTx.Size > 1024) { Fixed8 PriorityFee = Fixed8.FromDecimal(0.001m) + Fixed8.FromDecimal(copyTx.Size * 0.00001m); if (Fee > PriorityFee) { PriorityFee = Fee; } if (!Helper.CostRemind(Fixed8.Zero, PriorityFee)) { return(null); } tx = Program.CurrentWallet.MakeTransaction(new ContractTransaction { Outputs = tempOuts, Attributes = tx.Attributes }, FromAddress, change_address: ChangeAddress, fee: PriorityFee); } } return(tx); }
public bool CallContract(KeyPair key, string scriptHash, byte[] bytes, out byte[] txhash) { var inputs = new List <CoinReference>(); var outputs = new List <TransactionOutput>(); decimal gasCost = 0; if (key != null) { var unspent = GetUnspent(key.AsAddress(), UInt256.Zero); if (!unspent.ContainsKey("CRON")) { throw new RpcException(-3227, "No CRONs available"); } var sources = unspent["CRON"]; decimal selectedGas = 0; foreach (var src in sources) { selectedGas += src.value; var input = new CoinReference() { PrevHash = UInt256.Parse(src.txid), PrevIndex = (ushort)src.index, }; inputs.Add(input); if (selectedGas >= gasCost) { break; } } if (selectedGas < gasCost) { throw new RpcException(-3228, "Not enough CRONs available"); } var targetAssetID = reverseHex(ASSET_CRON).HexToBytes(); if (selectedGas > gasCost) { var left = selectedGas - gasCost; var change = new TransactionOutput() { AssetId = new UInt256(targetAssetID), ScriptHash = key.AsSignatureScript().HexToBytes().ToScriptHash(), Value = Fixed8.FromDecimal(left) }; outputs.Add(change); } } InvocationTransaction tx = new InvocationTransaction() { Attributes = new TransactionAttribute[0], Version = 0, Script = bytes, Gas = Fixed8.FromDecimal(gasCost), Inputs = inputs.ToArray(), Outputs = outputs.ToArray() }; if (key == null) { return(CallNoInvoke(tx, out txhash)); } txhash = tx.Hash.ToArray().Reverse().ToArray(); return(SignAndRelay(tx, new[] { key })); }
/** * run script through ApplicationEngine to determine gas price and bytecode validity */ private void btnTestScript_Click(object sender, EventArgs e) { if (tx == null) { tx = new InvocationTransaction(); } tx.Version = 1; tx.Script = txtCustomScriptCopy.Text.HexToBytes(); if (tx.Attributes == null) { tx.Attributes = new TransactionAttribute[0]; } if (tx.Inputs == null) { tx.Inputs = new CoinReference[0]; } if (tx.Outputs == null) { tx.Outputs = new TransactionOutput[0]; } if (tx.Scripts == null) { tx.Scripts = new Witness[0]; } LevelDBBlockchain blockchain = (LevelDBBlockchain)Blockchain.Default; DataCache <UInt160, AccountState> accounts = blockchain.GetTable <UInt160, AccountState>(); DataCache <ECPoint, ValidatorState> validators = blockchain.GetTable <ECPoint, ValidatorState>(); DataCache <UInt256, AssetState> assets = blockchain.GetTable <UInt256, AssetState>(); DataCache <UInt160, ContractState> contracts = blockchain.GetTable <UInt160, ContractState>(); DataCache <StorageKey, StorageItem> storages = blockchain.GetTable <StorageKey, StorageItem>(); CachedScriptTable script_table = new CachedScriptTable(contracts); StateMachine service = new StateMachine(accounts, validators, assets, contracts, storages); //////////////////////////////////////////////////////////// ////////////////////////EXPERIMENTAL//////////////////////// //testTx = tx; //testTx.Gas = Fixed8.Satoshi; //testTx = GetTransaction(); //ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, testTx, script_table, service, Fixed8.Zero, true); //engine.LoadScript(testTx.Script, false); ////////////////////////EXPERIMENTAL//////////////////////// //////////////////////////////////////////////////////////// ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, script_table, service, Fixed8.Zero, true); engine.LoadScript(tx.Script, false); if (engine.Execute()) { tx.Gas = engine.GasConsumed - Fixed8.FromDecimal(10); if (tx.Gas < Fixed8.One) { tx.Gas = Fixed8.One; } tx.Gas = tx.Gas.Ceiling(); label7.Text = tx.Gas + " gas"; btnInvoke.Enabled = true; if (engine.EvaluationStack.Count != 0) { if (engine.EvaluationStack.Peek().ToString() != "Neo.VM.Types.InteropInterface" && engine.EvaluationStack.Peek().ToString() != "Neo.VM.Types.Array") { MessageBox.Show( "Hex: " + engine.EvaluationStack.Peek().GetByteArray().ToHexString() + "\n" + "String: " + System.Text.Encoding.UTF8.GetString(engine.EvaluationStack.Peek().GetByteArray()) + "\n" + "BigInt: " + new BigInteger(engine.EvaluationStack.Peek().GetByteArray()), "Return from Test"); } } } else { MessageBox.Show(Strings.ExecutionFailed); } }
public void TestSetup() { uut = new InvocationTransaction(); }
public static async Task <string> SendInvocationTransaction(byte[] script, KeyPair keypair, string chainHash, Fixed8 gasLimit, Fixed8 gasPrice) { InvocationTransaction tx = MakeTransaction(script, keypair, gasLimit, gasPrice); return(await SendRawTransaction(tx.ToArray().ToHexString(), chainHash)); }
private void PreVerify(Transaction tx, Snapshot snapshot, IEnumerable <Transaction> mempool) { if (tx.Size > Transaction.MaxTransactionSize) { throw new RpcException(-500, "Size exceeds maximum transaction size"); } for (int i = 1; i < tx.Inputs.Length; i++) { for (int j = 0; j < i; j++) { if (tx.Inputs[i].PrevHash == tx.Inputs[j].PrevHash && tx.Inputs[i].PrevIndex == tx.Inputs[j].PrevIndex) { throw new RpcException(-500, "Duplicate inputs"); } } } if (mempool.Where(p => p != tx).SelectMany(p => p.Inputs).Intersect(tx.Inputs).Count() > 0) { throw new RpcException(-500, "Transaction already in mempool"); } CheckDoubleSpend(snapshot, tx); foreach (var group in tx.Outputs.GroupBy(p => p.AssetId)) { AssetState asset = snapshot.Assets.TryGet(group.Key); if (asset == null) { throw new RpcException(-500, "Asset value is null"); } if (asset.Expiration <= snapshot.Height + 1 && asset.AssetType != AssetType.GoverningToken && asset.AssetType != AssetType.UtilityToken) { throw new RpcException(-500, "Asset registration has expired"); } foreach (TransactionOutput output in group) { if (output.Value.GetData() % (long)Math.Pow(10, 8 - asset.Precision) != 0) { throw new RpcException(-500, "Invalid precision for asset output"); } } } TransactionResult[] results = tx.GetTransactionResults()?.ToArray(); if (results == null) { throw new RpcException(-500, "No TransactionResults found"); } TransactionResult[] results_destroy = results.Where(p => p.Amount > Fixed8.Zero).ToArray(); if (results_destroy.Length > 1) { throw new RpcException(-500, "TransactionResults destroy length > 1"); } if (results_destroy.Length == 1 && results_destroy[0].AssetId != Blockchain.UtilityToken.Hash) { throw new RpcException(-500, "TransactionResults destroy length equals 1 but asset is not GAS"); } if (tx.SystemFee > Fixed8.Zero && (results_destroy.Length == 0 || results_destroy[0].Amount < tx.SystemFee)) { throw new RpcException(-500, "System fee > 0 but TransactionResults destroy length is zero or amount is less than fee"); } TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray(); switch (tx.Type) { case TransactionType.MinerTransaction: case TransactionType.ClaimTransaction: if (results_issue.Any(p => p.AssetId != Blockchain.UtilityToken.Hash)) { throw new RpcException(-500, "ClaimTransaction but asset is not GAS"); } break; case TransactionType.IssueTransaction: if (results_issue.Any(p => p.AssetId == Blockchain.UtilityToken.Hash)) { throw new RpcException(-500, "IssueTransaction but asset hash matches GAS"); } break; case TransactionType.InvocationTransaction: InvocationTransaction it = (InvocationTransaction)tx; if (it.Gas.GetData() % 100000000 != 0) { throw new RpcException(-500, "Gas parameter is non-integer"); } break; default: if (results_issue.Length > 0) { throw new RpcException(-500, "No assets issued"); } break; } if (tx.Attributes.Count(p => p.Usage == TransactionAttributeUsage.ECDH02 || p.Usage == TransactionAttributeUsage.ECDH03) > 1) { throw new RpcException(-500, "Multiple occurances of ECDH02/ECDH03 transaction attribute usages"); } VerifyWitnesses(tx, snapshot); }
protected override JObject Process(string method, JArray _params) { TR.Enter(); switch (method) { case "getapplicationlog": { UInt256 hash = UInt256.Parse(_params[0].AsString()); string path = Path.Combine(Settings.Default.Paths.ApplicationLogs, $"{hash}.json"); return(TR.Exit( File.Exists(path) ? JObject.Parse(File.ReadAllText(path)) : throw new RpcException(-100, "Unknown transaction") )); } case "getbalance": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied."); } else { JObject json = new JObject(); switch (UIntBase.Parse(_params[0].AsString())) { case UInt160 asset_id_160: //NEP-5 balance json["balance"] = Program.Wallet.GetAvailable(asset_id_160).ToString(); break; case UInt256 asset_id_256: //Global Assets balance IEnumerable <Coin> coins = Program.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(TR.Exit(json)); } case "listaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied."); } else { return(TR.Exit(Program.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 (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UIntBase assetId = UIntBase.Parse(_params[0].AsString()); AssetDescriptor descriptor = new AssetDescriptor(assetId); UInt160 from = Wallet.ToScriptHash(_params[1].AsString()); UInt160 to = Wallet.ToScriptHash(_params[2].AsString()); 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 ? Wallet.ToScriptHash(_params[5].AsString()) : null; Transaction tx = Program.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); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(TR.Exit(tx.ToJson())); } else { return(TR.Exit(context.ToJson())); } } case "sendtoaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UIntBase assetId = UIntBase.Parse(_params[0].AsString()); AssetDescriptor descriptor = new AssetDescriptor(assetId); UInt160 scriptHash = Wallet.ToScriptHash(_params[1].AsString()); 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 ? Wallet.ToScriptHash(_params[4].AsString()) : null; Transaction tx = Program.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); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(TR.Exit(tx.ToJson())); } else { return(TR.Exit(context.ToJson())); } } case "sendmany": if (Program.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 = Wallet.ToScriptHash(to[i]["address"].AsString()) }; 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 ? Wallet.ToScriptHash(_params[2].AsString()) : null; Transaction tx = Program.Wallet.MakeTransaction(null, outputs, change_address: change_address, fee: fee); if (tx == null) { throw new RpcException(-300, "Insufficient funds"); } ContractParametersContext context = new ContractParametersContext(tx); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); Program.Wallet.ApplyTransaction(tx); LocalNode.Relay(tx); return(TR.Exit(tx.ToJson())); } else { return(TR.Exit(context.ToJson())); } } case "getnewaddress": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { WalletAccount account = Program.Wallet.CreateAccount(); if (Program.Wallet is NEP6Wallet wallet) { wallet.Save(); } return(TR.Exit(account.Address)); } case "dumpprivkey": if (Program.Wallet == null) { throw new RpcException(-400, "Access denied"); } else { UInt160 scriptHash = Wallet.ToScriptHash(_params[0].AsString()); WalletAccount account = Program.Wallet.GetAccount(scriptHash); return(TR.Exit(account.GetKey().Export())); } case "invoke": case "invokefunction": case "invokescript": JObject result = base.Process(method, _params); if (Program.Wallet != null) { InvocationTransaction tx = new InvocationTransaction { Version = 1, Script = result["script"].AsString().HexToBytes(), Gas = Fixed8.Parse(result["gas_consumed"].AsString()) }; tx.Gas -= Fixed8.FromDecimal(10); if (tx.Gas < Fixed8.Zero) { tx.Gas = Fixed8.Zero; } tx.Gas = tx.Gas.Ceiling(); tx = Program.Wallet.MakeTransaction(tx); if (tx != null) { ContractParametersContext context = new ContractParametersContext(tx); Program.Wallet.Sign(context); if (context.Completed) { tx.Scripts = context.GetScripts(); } else { tx = null; } } result["tx"] = tx?.ToArray().ToHexString(); } return(TR.Exit(result)); default: return(TR.Exit(base.Process(method, _params))); } }
/// <summary> /// Transfer NEP5 tokens. /// </summary> /// <param name="attributes"></param> /// <param name="outputs"></param> /// <param name="changeAddress"></param> /// <param name="fee"></param> /// <returns></returns> public override async Task <Transaction> TransferNep5(List <TransactionAttribute> attributes, IEnumerable <TransferOutput> outputs, UInt160 changeAddress = null, decimal fee = 0) { InvocationTransaction tx; var cOutputs = outputs.Where(p => !p.IsGlobalAsset).GroupBy(p => new { AssetId = (UInt160)p.AssetId, Account = p.ScriptHash }, (k, g) => new { k.AssetId, Value = g.Aggregate(BigInteger.Zero, (x, y) => x + y.Value.Value), k.Account }).ToArray(); if (cOutputs.Length == 0) { return(null); } var nep5Balances = await TransactionBuilderHelper.GetNep5Balances(AddressScriptHash.ToAddress(), _restService); using (ScriptBuilder sb = new ScriptBuilder()) { foreach (var output in cOutputs) { var nep5Balance = nep5Balances.SingleOrDefault(x => x.AssetHash == output.AssetId.ToString().Remove(0, 2)); if (nep5Balance == null) { throw new WalletException($"Not enough balance of: {output.AssetId} "); } sb.EmitAppCall(output.AssetId, Nep5Methods.transfer.ToString(), AddressScriptHash, output.Account, output.Value); sb.Emit(OpCode.THROWIFNOT); } byte[] nonce = GenerateNonce(8); sb.Emit(OpCode.RET, nonce); tx = new InvocationTransaction { Version = 1, Script = sb.ToArray() }; } if (attributes == null) { attributes = new List <TransactionAttribute>(); } attributes.Add(new TransactionAttribute { Usage = TransactionAttributeUsage.Script, Data = AddressScriptHash.ToArray() }); tx.Attributes = attributes.ToArray(); tx.Inputs = new CoinReference[0]; tx.Outputs = outputs.Where(p => p.IsGlobalAsset).Select(p => p.ToTxOutput()).ToArray(); tx.Witnesses = new Witness[0]; var gasConsumed = await EstimateGasAsync(tx.Script.ToHexString()); //todo add gas limit tx.Gas = InvocationTransaction.GetGas(Fixed8.FromDecimal(gasConsumed)); tx = MakeTransaction(tx, AddressScriptHash, changeAddress, Fixed8.FromDecimal(fee)); var success = await SignAndSendTransaction(tx); return(success ? tx : null); }
private bool Asset_Create(ExecutionEngine engine) { InvocationTransaction tx = (InvocationTransaction)engine.ScriptContainer; AssetType asset_type = (AssetType)(byte)engine.EvaluationStack.Pop().GetBigInteger(); if (!Enum.IsDefined(typeof(AssetType), asset_type) || asset_type == AssetType.CreditFlag || asset_type == AssetType.DutyFlag || asset_type == AssetType.GoverningToken || asset_type == AssetType.UtilityToken) { return(false); } if (engine.EvaluationStack.Peek().GetByteArray().Length > 1024) { return(false); } string name = Encoding.UTF8.GetString(engine.EvaluationStack.Pop().GetByteArray()); Fixed8 amount = new Fixed8((long)engine.EvaluationStack.Pop().GetBigInteger()); if (amount == Fixed8.Zero || amount < -Fixed8.Satoshi) { return(false); } if (asset_type == AssetType.Invoice && amount != -Fixed8.Satoshi) { return(false); } byte precision = (byte)engine.EvaluationStack.Pop().GetBigInteger(); if (precision > 8) { return(false); } if (asset_type == AssetType.Share && precision != 0) { return(false); } if (amount != -Fixed8.Satoshi && amount.GetData() % (long)Math.Pow(10, 8 - precision) != 0) { return(false); } ECPoint owner = ECPoint.DecodePoint(engine.EvaluationStack.Pop().GetByteArray(), ECCurve.Secp256r1); if (owner.IsInfinity) { return(false); } if (!CheckWitness(engine, owner)) { return(false); } UInt160 admin = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); UInt160 issuer = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); Fixed8 a_fee = new Fixed8((long)engine.EvaluationStack.Pop().GetBigInteger()); Fixed8 t_fee = new Fixed8((long)engine.EvaluationStack.Pop().GetBigInteger()); Fixed8 t_fee_min = new Fixed8((long)engine.EvaluationStack.Pop().GetBigInteger()); Fixed8 t_fee_max = new Fixed8((long)engine.EvaluationStack.Pop().GetBigInteger()); UInt160 feeAddress = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); AssetState asset = assets.GetOrAdd(tx.Hash, () => new AssetState { AssetId = tx.Hash, AssetType = asset_type, Name = name, Amount = amount, Available = Fixed8.Zero, Precision = precision, Fee = t_fee, FeeMin = t_fee_min, FeeMax = t_fee_max, FeeAddress = feeAddress, Owner = owner, Admin = admin, Issuer = issuer, Expiration = Blockchain.Default.Height + 1 + 4000000, // 2 Years IsFrozen = false, AFee = a_fee }); engine.EvaluationStack.Push(StackItem.FromInterface(asset)); return(true); }
public InvokeContractMessage(InvocationTransaction transaction) { this.Transaction = transaction; }