示例#1
0
 private static WalletTransaction.Output MarshalWalletTransactionOutput(TransactionDetails.Types.Output o, Transaction.Output txOutput)
 {
     if (o.Mine)
         return new WalletTransaction.Output.ControlledOutput(txOutput.Amount, new Account(o.Account), o.Internal);
     else
         return new WalletTransaction.Output.UncontrolledOutput(txOutput.Amount, txOutput.PkScript);
 }
示例#2
0
 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");
     }
 }
示例#3
0
 private static WalletTransaction.Output[] MarshalCombinedOutputs(Transaction transaction, IEnumerator<TransactionDetails.Types.Output> credits)
 {
     return transaction.Outputs.Select((output, index) =>
     {
         while (credits.Current?.Index < index)
             credits.MoveNext();
         return credits.Current?.Index == index
             ? MarshalControlledOutput(credits.Current, output)
             : new WalletTransaction.Output.UncontrolledOutput(output.Amount, output.PkScript);
     }).ToArray();
 }
示例#4
0
        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(Sha256Hash.Zero);
        }
示例#5
0
 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");
     }
 }
示例#6
0
 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++;
     }
 }
示例#7
0
        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);
            }
        }
示例#8
0
 public async Task<Tuple<Transaction, bool>> SignTransactionAsync(string passphrase, Transaction tx)
 {
     var client = WalletService.NewClient(_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);
 }
示例#9
0
 private static WalletTransaction.Output MarshalControlledOutput(TransactionDetails.Types.Output o, Transaction.Output txOutput) =>
     new WalletTransaction.Output.ControlledOutput(txOutput.Amount, new Account(o.Account), o.Internal);
示例#10
0
        private async Task SignTransactionWithPassphrase(string passphrase, Transaction.Output[] outputs, bool publishImmediately)
        {
            var walletClient = App.Current.WalletRpcClient;
            var requiredConfirmations = 1; // TODO: Don't hardcode confs.
            var targetAmount = outputs.Sum(o => o.Amount);
            var targetFee = (Amount)1e4; // TODO: Don't hardcode fee/kB.
            var funding = await walletClient.FundTransactionAsync(_account, targetAmount + targetFee, requiredConfirmations);
            var fundingAmount = funding.Item2;
            if (fundingAmount < targetAmount + targetFee)
            {
                MessageBox.Show($"Transaction requires {targetAmount + targetFee} but only {fundingAmount} is spendable.",
                    "Insufficient funds to create transaction.");
                return;
            }

            var selectedOutputs = funding.Item1;
            var inputs = selectedOutputs
                .Select(o =>
                {
                    var prevOutPoint = new Transaction.OutPoint(o.TransactionHash, o.OutputIndex);
                    return new Transaction.Input(prevOutPoint, new byte[0], TransactionRules.MaxInputSequence);
                })
                .ToArray();

            // TODO: Port the fee estimation logic from btcwallet.  Using a hardcoded fee is unacceptable.
            var estimatedFee = targetFee;

            var changePkScript = funding.Item3;
            if (changePkScript != null)
            {
                // Change output amount is calculated by solving for changeAmount with the equation:
                //   estimatedFee = fundingAmount - (targetAmount + changeAmount)
                var changeOutput = new Transaction.Output(fundingAmount - targetAmount - estimatedFee, changePkScript.Script);
                var outputsList = outputs.ToList();
                // TODO: Randomize change output position.
                outputsList.Add(changeOutput);
                outputs = outputsList.ToArray();
            }

            // TODO: User may want to set the locktime.
            var unsignedTransaction = new Transaction(Transaction.LatestVersion, inputs, outputs, 0);

            var signingResponse = await walletClient.SignTransactionAsync(passphrase, unsignedTransaction);
            var complete = signingResponse.Item2;
            if (!complete)
            {
                MessageBox.Show("Failed to create transaction input signatures.");
                return;
            }
            var signedTransaction = signingResponse.Item1;

            MessageBox.Show($"Created tx with {estimatedFee} fee.");

            if (!publishImmediately)
            {
                MessageBox.Show("Reviewing signed transaction before publishing is not implemented yet.");
                return;
            }

            // TODO: The client just deserialized the transaction, so serializing it is a
            // little silly.  This could be optimized.
            await walletClient.PublishTransactionAsync(signedTransaction.Serialize());
            MessageBox.Show("Published transaction.");
        }
示例#11
0
        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");
                }
            }
        }
示例#12
0
 public static void SerializeSizes(Transaction tx, int expectedSerializeSize)
 {
     var serializeSize = tx.SerializeSize;
     Assert.Equal(expectedSerializeSize, serializeSize);
 }