private static void CheckHasInputsAndOutputs(Transaction tx) { if (tx.Inputs.Length == 0) { throw new TransactionRuleException("Transaction must have at least one input"); } if (tx.Outputs.Length == 0) { throw new TransactionRuleException("Transaction must have at least one output"); } }
public static bool IsCoinbase(Transaction tx) { if (tx == null) throw new ArgumentNullException(nameof(tx)); if (tx.Inputs.Length != 1) return false; var previousOutput = tx.Inputs[0].PreviousOutpoint; return previousOutput.Index == uint.MaxValue && previousOutput.Hash.Equals(Blake256Hash.Zero); }
private static void CheckOutputValueSanity(Transaction tx) { Amount outputSum = 0; int outputIndex = 0; foreach (var output in tx.Outputs) { if (!IsSaneOutputValue(output.Amount)) { throw new TransactionRuleException($"Output value {output.Amount} for output {outputIndex} is outside valid range"); } if (outputSum - MaxOutputValue + output.Amount > 0) { throw new TransactionRuleException("Total output value exceeds maximum"); } outputSum += output.Amount; outputIndex++; } }
private static WalletTransaction.Output[] MarshalCombinedOutputs(Transaction transaction, Google.Protobuf.Collections.RepeatedField<TransactionDetails.Types.Output> credits) { var creditIndex = 0; return transaction.Outputs.Select((output, outputIndex) => { if (creditIndex < credits.Count && credits[creditIndex].Index == outputIndex) { var controlledOutput = MarshalControlledOutput(credits[creditIndex], output); creditIndex++; return controlledOutput; } else { return new WalletTransaction.Output.UncontrolledOutput(output.Amount, output.PkScript); } }).ToArray(); }
public static void CheckSanity(Transaction tx) { if (tx == null) throw new ArgumentNullException(nameof(tx)); CheckHasInputsAndOutputs(tx); // TODO: check serialize size CheckOutputValueSanity(tx); if (BlockChain.IsCoinbase(tx)) { CheckCoinbaseSignatureScript(tx.Inputs[0]); } else { CheckNonCoinbaseInputs(tx); } }
public async Task<Tuple<Transaction, bool>> SignTransactionAsync(string passphrase, Transaction tx) { var client = new WalletService.WalletServiceClient(_channel); var request = new SignTransactionRequest { Passphrase = ByteString.CopyFromUtf8(passphrase), SerializedTransaction = ByteString.CopyFrom(tx.Serialize()), }; var response = await client.SignTransactionAsync(request, cancellationToken: _tokenSource.Token); var signedTransaction = Transaction.Deserialize(response.Transaction.ToByteArray()); var complete = response.UnsignedInputIndexes.Count == 0; return Tuple.Create(signedTransaction, complete); }
private async Task<bool> SignTransactionWithPassphrase(string passphrase, Transaction tx, bool publishImmediately) { Tuple<Transaction, bool> signingResponse; var walletClient = App.Current.Synchronizer.WalletRpcClient; try { signingResponse = await walletClient.SignTransactionAsync(passphrase, tx); } catch (RpcException ex) when (ex.Status.StatusCode == StatusCode.InvalidArgument) { MessageBox.Show(ex.Status.Detail); return false; } var complete = signingResponse.Item2; if (!complete) { MessageBox.Show("Failed to create transaction input signatures."); return false; } var signedTransaction = signingResponse.Item1; if (!publishImmediately) { MessageBox.Show("Reviewing signed transaction before publishing is not implemented yet."); return false; } await walletClient.PublishTransactionAsync(signedTransaction.Serialize()); MessageBox.Show("Published transaction."); _pendingTransaction = null; _unusedChangeScripts.Remove(SelectedAccount.Account); PendingOutputs.Clear(); AddPendingOutput(); return true; }
private void SetPendingTransaction(Transaction unsignedTransaction, Amount inputAmount, Amount targetOutput) { var wallet = App.Current.Synchronizer.Wallet; if (wallet == null) return; var actualFee = TransactionFees.ActualFee(unsignedTransaction, inputAmount); var totalAccountBalance = wallet.LookupAccountProperties(SelectedAccount.Account).TotalBalance; _pendingTransaction = unsignedTransaction; EstimatedFee = actualFee; EstimatedRemainingBalance = totalAccountBalance - targetOutput - actualFee; FinishCreateTransaction.Executable = true; }
private void UnsetPendingTransaction() { _pendingTransaction = null; EstimatedFee = null; EstimatedRemainingBalance = null; FinishCreateTransaction.Executable = false; }
public static void SerializeSizes(Transaction tx, int expectedSerializeSize) { var serializeSize = tx.SerializeSize; Assert.Equal(expectedSerializeSize, serializeSize); }
private static void CheckNonCoinbaseInputs(Transaction tx) { var seenOutPoints = new HashSet<Transaction.OutPoint>(); foreach (var input in tx.Inputs) { if (seenOutPoints.Contains(input.PreviousOutpoint)) { throw new TransactionRuleException($"Transaction input contains duplicate previous output {input.PreviousOutpoint}"); } seenOutPoints.Add(input.PreviousOutpoint); if (input.PreviousOutpoint.IsNull()) { throw new TransactionRuleException("Non-coinbase transaction may not refer to a null previous output"); } } }
private static void CheckCoinbaseSignatureScript(Transaction.Input coinbaseInput) { var scriptLength = coinbaseInput.SignatureScript.Length; if (scriptLength < BlockChain.MinCoinbaseScriptLength || scriptLength > BlockChain.MaxCoinbaseScriptLength) { throw new TransactionRuleException($"Coinbase transaction input signature script length {scriptLength} is outside valid range"); } }
private static WalletTransaction.Output MarshalControlledOutput(TransactionDetails.Types.Output o, Transaction.Output txOutput) => new WalletTransaction.Output.ControlledOutput(txOutput.Amount, new Account(o.Account), o.Internal);
private void SetPendingTransaction(Amount totalAccountBalance, Transaction unsignedTransaction, Amount inputAmount, Amount targetOutput) { var actualFee = TransactionFees.ActualFee(unsignedTransaction, inputAmount); _pendingTransaction = unsignedTransaction; EstimatedFee = actualFee; EstimatedRemainingBalance = totalAccountBalance - targetOutput - actualFee; FinishCreateTransaction.Executable = true; }