static CoinPicker() { Transaction baseTx = Transaction.Create(new List <Transaction.Output>(), new Dictionary <Address, Money>(), new Dictionary <Address, ECKey>(), allowDust: true); MinBytesForTx = (long)(baseTx.ToByteArray().LongCount() * (1 + PercentError)); var input = new Transaction.Input("23e90c875e2ed7a1ec01f5a80643879625b8aeb48b67db64c0f9edb8259240b6", 0, 0) { ScriptSig = Script.FromString("3045022100f65c5e8c5d3b2386547a876db4ddb7bba1e57f9dbeaec9f3010516e453577fda02206f9df1a9262997263ac01be4342d0ade7057f5cceab4375fa1020ac7bfc5054b01 04ef96e3bccc8fff6b21d28e81f61c4a93cfe0f133214c9547c0d683a9fc12f529229c8d1ab20004c0f7f13961566b65492c6267fa452784c0724b4f542e4001f1") }; using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) { input.Write(writer); ApproxBytesPerInput = (long)(stream.ToArray().LongCount() * (1 + PercentError)); } } var output = new Transaction.Output(Script.FromString("OP_DUP OP_HASH160 4da1b9632e160406693b961ff321402b22ce5452 OP_EQUALVERIFY OP_CHECKSIG"), Money.Create(0.002m, "BTC"), baseTx, 0); using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) { output.Write(writer); ApproxBytesPerOutput = (long)(stream.ToArray().LongCount() * (1 + PercentError)); } } }
public void Withdraw(byte[] asset, BigInteger amount, Transaction.Input input) { var balance = balances.ContainsKey(asset) ? balances[asset] : 0; balance -= amount; balances[asset] = balance; unspent.RemoveAll(x => x.prevHash == input.prevHash && x.prevIndex == input.prevIndex); }
public void Deposit(byte[] asset, BigInteger amount, Transaction.Input input) { var balance = balances.ContainsKey(asset) ? balances[asset] : 0; balance += amount; balances[asset] = balance; unspent.Add(input); }
private void ExecuteTransaction(Transaction tx) { foreach (var input in tx.inputs) { var input_tx = GetTransaction(input.prevHash); var output = input_tx.outputs[input.prevIndex]; var account = GetAccount(output.scriptHash); account.Withdraw(output.assetID, output.value.ToBigInteger(), input); var asset = GetAsset(output.assetID); if (asset != null) { var address = output.scriptHash.ToAddress(); Logger($"Withdrawing {output.value} {asset.name} from {address}"); } } uint index = 0; foreach (var output in tx.outputs) { var account = GetAccount(output.scriptHash); var unspent = new Transaction.Input() { prevIndex = index, prevHash = tx.Hash }; account.Deposit(output.assetID, output.value.ToBigInteger(), unspent); var asset = GetAsset(output.assetID); var address = output.scriptHash.ToAddress(); Logger($"Depositing {output.value} {asset.name} to {address}"); index++; } switch (tx.type) { case TransactionType.PublishTransaction: { var contract_hash = tx.contractRegistration.script.ToScriptHash(); var account = GetAccount(contract_hash); account.contract = tx.contractRegistration; account.storage = new Storage(); break; } case TransactionType.InvocationTransaction: { ExecuteVM(tx, TriggerType.Application); break; } } }
private static string SerializeTransactionInput(Transaction.Input input) { return(reverseHex(input.prevHash) + reverseHex(num2hexstring(input.prevIndex, 4))); }
public void GenerateInputsOutputs(UInt160 from_script_hash, string symbol, IEnumerable <Transaction.Output> targets, out List <Transaction.Input> inputs, out List <Transaction.Output> outputs, decimal system_fee = 0) { var unspent = GetUnspent(from_script_hash); // filter any asset lists with zero unspent inputs unspent = unspent.Where(pair => pair.Value.Count > 0).ToDictionary(pair => pair.Key, pair => pair.Value); inputs = new List <Transaction.Input>(); outputs = new List <Transaction.Output>(); string assetID; var info = GetAssetsInfo(); if (info.ContainsKey(symbol)) { assetID = info[symbol]; } else { throw new NeoException($"{symbol} is not a valid blockchain asset."); } var from_address = from_script_hash.ToAddress(); if (!unspent.ContainsKey(symbol)) { throw new NeoException($"Not enough {symbol} in address {from_address}"); } decimal cost = 0; if (targets != null) { foreach (var target in targets) { if (target.scriptHash.Equals(from_script_hash)) { throw new NeoException("Target can't be same as input"); } cost += target.value; } } var targetAssetID = LuxUtils.ReverseHex(assetID).HexToBytes(); var sources = unspent[symbol]; decimal selected = 0; if (lastTransactions.ContainsKey(from_address)) { var lastTx = lastTransactions[from_address]; uint index = 0; foreach (var output in lastTx.outputs) { if (output.assetID.SequenceEqual(targetAssetID) && output.scriptHash.Equals(from_script_hash)) { selected += output.value; var input = new Transaction.Input() { prevHash = lastTx.Hash, prevIndex = index, }; inputs.Add(input); break; } index++; } } foreach (var src in sources) { if (selected >= cost && inputs.Count > 0) { break; } selected += src.value; var input = new Transaction.Input() { prevHash = src.hash, prevIndex = src.index, }; inputs.Add(input); } if (selected < cost) { throw new NeoException($"Not enough {symbol}"); } if (cost > 0 && targets != null) { foreach (var target in targets) { var output = new Transaction.Output() { assetID = targetAssetID, scriptHash = target.scriptHash, value = target.value }; outputs.Add(output); } } if (selected > cost || cost == 0) { var left = selected - cost; var change = new Transaction.Output() { assetID = targetAssetID, scriptHash = from_script_hash, value = left }; outputs.Add(change); } }
private async Task RecalculatePendingTransaction() { if (PendingOutputs.Count == 0 || PendingOutputs.Any(x => !x.IsValid)) { UnsetPendingTransaction(); return; } var walletClient = App.Current.Synchronizer?.WalletRpcClient; var outputs = PendingOutputs.Select(po => { var amount = po.OutputAmount; var script = po.BuildOutputScript().Script; return(new Transaction.Output(amount, Transaction.Output.LatestPkScriptVersion, script)); }).ToArray(); TransactionAuthor.InputSource inputSource = async targetAmount => { var inputs = new Transaction.Input[0]; // TODO: don't hardcode confs var funding = await walletClient.FundTransactionAsync(SelectedAccount.Account, targetAmount, 1); if (funding.Item2 >= targetAmount) { inputs = funding.Item1.Select(o => Transaction.Input.CreateFromPrefix(new Transaction.OutPoint(o.TransactionHash, o.OutputIndex, o.Tree), TransactionRules.MaxInputSequence)).ToArray(); } return(Tuple.Create(funding.Item2, inputs)); }; TransactionAuthor.ChangeSource changeSource = async() => { // Use cached change script if one has already been generated for this account. var selectedAccount = SelectedAccount.Account; OutputScript cachedScript; if (_unusedChangeScripts.TryGetValue(selectedAccount, out cachedScript)) { return(cachedScript); } var changeAddress = await walletClient.NextInternalAddressAsync(SelectedAccount.Account); var changeScript = Address.Decode(changeAddress).BuildScript(); _unusedChangeScripts[selectedAccount] = changeScript; return(changeScript); }; try { var r = await TransactionAuthor.BuildUnsignedTransaction(outputs, TransactionFees.DefaultFeePerKb, inputSource, changeSource); Amount totalAccountBalance; using (var walletGuard = await App.Current.Synchronizer.WalletMutex.LockAsync()) { totalAccountBalance = walletGuard.Instance.LookupAccountProperties(SelectedAccount.Account).TotalBalance; } SetPendingTransaction(totalAccountBalance, r.Item1, r.Item2, outputs.Sum(o => o.Amount)); } catch (Exception ex) { UnsetPendingTransaction(); // Insufficient funds will need a nicer error displayed somehow. For now, hide it // while disabling the UI to create the transaction. All other errors are unexpected. if (!(ex is InsufficientFundsException)) { throw; } } }