Example #1
0
        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 P2PTransaction();
            }
            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();
                        }
                        using (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 P2PTransaction 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 P2PTransaction
                    {
                        Outputs    = tempOuts,
                        Attributes = tx.Attributes
                    }, FromAddress, change_address: ChangeAddress, fee: PriorityFee);
                }
            }
            return(tx);
        }
Example #2
0
        private bool OnSendCommand(string[] args)
        {
            if (args.Length < 4 || args.Length > 5)
            {
                Console.WriteLine("error");
                return(true);
            }
            if (NoWallet())
            {
                return(true);
            }
            string password = ReadUserInput("password", true);

            if (password.Length == 0)
            {
                Console.WriteLine("cancelled");
                return(true);
            }
            if (!Program.Wallet.VerifyPassword(password))
            {
                Console.WriteLine("Incorrect password");
                return(true);
            }
            UIntBase assetId;

            switch (args[1].ToLower())
            {
            case "krona":
            case "krn":
                assetId = Blockchain.GoverningToken.Hash;
                break;

            default:
                assetId = UIntBase.Parse(args[1]);
                break;
            }
            UInt160     scriptHash = args[2].ToScriptHash();
            bool        isSendAll  = string.Equals(args[3], "all", StringComparison.OrdinalIgnoreCase);
            Transaction tx;

            if (isSendAll)
            {
                Coin[] coins = Program.Wallet.FindUnspentCoins().Where(p => p.Output.AssetId.Equals(assetId)).ToArray();
                tx = new P2PTransaction
                {
                    Attributes = new TransactionAttribute[0],
                    Inputs     = coins.Select(p => p.Reference).ToArray(),
                    Outputs    = new[]
                    {
                        new TransactionOutput
                        {
                            AssetId    = (UInt256)assetId,
                            Value      = coins.Sum(p => p.Output.Value),
                            ScriptHash = scriptHash
                        }
                    }
                };
                ContractParametersContext context = new ContractParametersContext(tx);
                Program.Wallet.Sign(context);
                if (context.Completed)
                {
                    tx.Witnesses = context.GetWitnesses();
                    Program.Wallet.ApplyTransaction(tx);
                    system.LocalNode.Tell(new LocalNode.Relay {
                        Inventory = tx
                    });
                    Console.WriteLine($"TXID: {tx.Hash}");
                }
                else
                {
                    Console.WriteLine("SignatureContext:");
                    Console.WriteLine(context.ToString());
                }
            }
            else
            {
                AssetDescriptor descriptor = new AssetDescriptor(assetId);
                if (!BigDecimal.TryParse(args[3], descriptor.Decimals, out BigDecimal amount) || amount.Sign <= 0)
                {
                    Console.WriteLine("Incorrect Amount Format");
                    return(true);
                }
                Fixed8 fee = Fixed8.Zero;

                if (args.Length >= 5)
                {
                    if (!Fixed8.TryParse(args[4], out fee) || fee < Fixed8.Zero)
                    {
                        Console.WriteLine("Incorrect Fee Format");
                        return(true);
                    }
                }

                tx = Program.Wallet.MakeTransaction(null, new[]
                {
                    new TransferOutput
                    {
                        AssetId    = assetId,
                        Value      = amount,
                        ScriptHash = scriptHash
                    }
                }, fee: fee);

                if (tx == null)
                {
                    Console.WriteLine("Insufficient funds");
                    return(true);
                }

                ContractParametersContext context = new ContractParametersContext(tx);
                Program.Wallet.Sign(context);
                if (context.Completed)
                {
                    tx.Witnesses = context.GetWitnesses();
                    if (tx.Size > 1024)
                    {
                        Fixed8 calFee = Fixed8.FromDecimal(tx.Size * 0.00001m + 0.001m);
                        if (fee < calFee)
                        {
                            fee = calFee;
                            tx  = Program.Wallet.MakeTransaction(null, new[]
                            {
                                new TransferOutput
                                {
                                    AssetId    = assetId,
                                    Value      = amount,
                                    ScriptHash = scriptHash
                                }
                            }, fee: fee);
                            if (tx == null)
                            {
                                Console.WriteLine("Insufficient funds");
                                return(true);
                            }
                            context = new ContractParametersContext(tx);
                            Program.Wallet.Sign(context);
                            tx.Witnesses = context.GetWitnesses();
                        }
                    }
                    Program.Wallet.ApplyTransaction(tx);
                    system.LocalNode.Tell(new LocalNode.Relay {
                        Inventory = tx
                    });
                    Console.WriteLine($"TXID: {tx.Hash}");
                }
                else
                {
                    Console.WriteLine("SignatureContext:");
                    Console.WriteLine(context.ToString());
                }
            }

            return(true);
        }
Example #3
0
        public Transaction MakeTransaction(List <TransactionAttribute> attributes, IEnumerable <TransferOutput> outputs, UInt160 from = null, UInt160 change_address = null, Fixed8 fee = default(Fixed8))
        {
            Random rand     = new Random();
            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>();
            }

            // Generate nonce
            var nonce = new byte[8];

            rand.NextBytes(nonce);
            attributes.Add(new TransactionAttribute()
            {
                Usage = TransactionAttributeUsage.Remark,
                Data  = nonce
            });

            if (cOutputs.Length == 0)
            {
                tx = new P2PTransaction();
            }
            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)
                    {
                        var balances = new List <(UInt160 Account, BigInteger Value)>();
                        foreach (UInt160 account in accounts)
                        {
                            byte[] script;
                            using (ScriptBuilder sb2 = new ScriptBuilder())
                            {
                                sb2.EmitAppCall(output.AssetId, "balanceOf", account);
                                script = sb2.ToArray();
                            }
                            using (ApplicationEngine engine = ApplicationEngine.Run(script))
                            {
                                if (engine.State.HasFlag(VMState.FAULT))
                                {
                                    return(null);
                                }
                                var result = engine.ResultStack.Pop().GetBigInteger();
                                if (result == 0)
                                {
                                    continue;
                                }
                                balances.Add((account, result));
                            }
                        }
                        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);
                        }
                    }
                    sb.Emit(OpCode.RET);
                    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.Witnesses  = new Witness[0];
            if (tx is InvocationTransaction itx)
            {
                using (ApplicationEngine engine = ApplicationEngine.Run(itx.Script, itx))
                {
                    if (engine.State.HasFlag(VMState.FAULT))
                    {
                        return(null);
                    }
                    Fixed8 freeGas;
                    if (WalletHeight < Blockchain.FreeGasChangeHeight)
                    {
                        freeGas = Fixed8.FromDecimal(10);
                    }
                    else
                    {
                        freeGas = Fixed8.FromDecimal(50);
                    }
                    tx = new InvocationTransaction
                    {
                        Version    = itx.Version,
                        Script     = itx.Script,
                        Gas        = InvocationTransaction.GetGas(engine.GasConsumed, freeGas),
                        Attributes = itx.Attributes,
                        Inputs     = itx.Inputs,
                        Outputs    = itx.Outputs
                    };
                }
            }
            tx = MakeTransaction(tx, from, change_address, fee);
            return(tx);
        }