public int EstimateSize(Transaction tx) { var clone = tx.Clone(); clone.Inputs.Clear(); var baseSize = clone.ToBytes().Length; int inputSize = 0; for (int i = 0; i < tx.Inputs.Count; i++) { var txin = tx.Inputs[i]; var coin = FindCoin(txin.PrevOut); if (coin == null) { throw CoinNotFound(txin); } inputSize += EstimateScriptSigSize(coin) + 41; } return(baseSize + inputSize); }
private PSBT(Transaction transaction) { if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); } tx = transaction.Clone(); Inputs = new PSBTInputList(); Outputs = new PSBTOutputList(); for (var i = 0; i < tx.Inputs.Count; i++) { this.Inputs.Add(new PSBTInput(this, (uint)i, tx.Inputs[i])); } for (var i = 0; i < tx.Outputs.Count; i++) { this.Outputs.Add(new PSBTOutput(this, (uint)i, tx.Outputs[i])); } foreach (var input in tx.Inputs) { input.ScriptSig = Script.Empty; input.WitScript = WitScript.Empty; } }
public bool TryFinalizeInput(out IList <PSBTError> errors) { errors = null; if (IsFinalized()) { return(true); } var isSane = this.CheckSanity(); if (isSane.Count != 0) { errors = isSane; return(false); } if (witness_utxo == null && non_witness_utxo == null) { errors = new List <PSBTError>() { new PSBTError(Index, "Neither witness_utxo nor non_witness_output is set") }; return(false); } var coin = this.GetSignableCoin(out var getSignableCoinError) ?? this.GetCoin(); // GetCoin can't be null at this stage. TransactionBuilder transactionBuilder = Parent.CreateTransactionBuilder(); transactionBuilder.AddCoins(coin); foreach (var sig in PartialSigs) { transactionBuilder.AddKnownSignature(sig.Key, sig.Value, coin.Outpoint); } Transaction signed = null; try { var signedTx = Parent.Settings.IsSmart ? Parent.GetOriginalTransaction() : Transaction.Clone(); signed = transactionBuilder.SignTransaction(signedTx, SigHash.All); } catch (Exception ex) { errors = new List <PSBTError>() { new PSBTError(Index, $"Error while finalizing the input \"{getSignableCoinError ?? ex.Message}\"") }; return(false); } var indexedInput = signed.Inputs.FindIndexedInput(coin.Outpoint); if (!indexedInput.VerifyScript(coin, out var error)) { errors = new List <PSBTError>() { new PSBTError(Index, $"The finalized input script does not properly validate \"{error}\"") }; return(false); } FinalScriptSig = indexedInput.ScriptSig is Script oo && oo != Script.Empty ? oo : null; FinalScriptWitness = indexedInput.WitScript is WitScript o && o != WitScript.Empty ? o : null; if (transactionBuilder.FindSignableCoin(indexedInput) is ScriptCoin scriptCoin) { if (scriptCoin.IsP2SH) { RedeemScript = scriptCoin.GetP2SHRedeem(); } if (scriptCoin.RedeemType == RedeemType.WitnessV0) { WitnessScript = scriptCoin.Redeem; } } ClearForFinalize(); errors = null; return(true); }
public Transaction SignTransaction(KeyPath keyPath, ICoin[] signedCoins, Transaction[] parents, Transaction transaction) { using(Transport.Lock()) { var pubkey = GetWalletPubKey(keyPath).UncompressedPublicKey.Compress(); var parentsById = parents.ToDictionary(p => p.GetHash()); var coinsByPrevout = signedCoins.ToDictionary(c => c.Outpoint); List<TrustedInput> trustedInputs = new List<TrustedInput>(); foreach(var input in transaction.Inputs) { Transaction parent; parentsById.TryGetValue(input.PrevOut.Hash, out parent); if(parent == null) throw new KeyNotFoundException("Parent transaction " + input.PrevOut.Hash + " not found"); trustedInputs.Add(GetTrustedInput(parent, (int)input.PrevOut.N)); } var inputs = trustedInputs.ToArray(); transaction = transaction.Clone(); foreach(var input in transaction.Inputs) { ICoin previousCoin = null; coinsByPrevout.TryGetValue(input.PrevOut, out previousCoin); if(previousCoin != null) input.ScriptSig = previousCoin.GetScriptCode(); } bool newTransaction = true; foreach(var input in transaction.Inputs.AsIndexedInputs()) { ICoin coin = null; if(!coinsByPrevout.TryGetValue(input.PrevOut, out coin)) continue; UntrustedHashTransactionInputStart(newTransaction, input, inputs); newTransaction = false; UntrustedHashTransactionInputFinalizeFull(transaction.Outputs); var sig = UntrustedHashSign(keyPath, null, transaction.LockTime, SigHash.All); input.ScriptSig = PayToPubkeyHashTemplate.Instance.GenerateScriptSig(sig, pubkey); ScriptError error; if(!Script.VerifyScript(coin.TxOut.ScriptPubKey, transaction, (int)input.Index, Money.Zero, out error)) return null; } return transaction; } }
private Transaction CombineSignaturesCore(Transaction signed1, Transaction signed2) { if(signed1 == null) return signed2; if(signed2 == null) return signed1; var tx = signed1.Clone(); for(int i = 0; i < tx.Inputs.Count; i++) { if(i >= signed2.Inputs.Count) break; var txIn = tx.Inputs[i]; var coin = FindCoin(txIn.PrevOut); var scriptPubKey = coin == null ? (DeduceScriptPubKey(txIn.ScriptSig) ?? DeduceScriptPubKey(signed2.Inputs[i].ScriptSig)) : coin.TxOut.ScriptPubKey; Money amount = null; if(coin != null) amount = coin is IColoredCoin ? ((IColoredCoin)coin).Bearer.Amount : ((Coin)coin).Amount; var result = Script.CombineSignatures( scriptPubKey, new TransactionChecker(tx, i, amount), GetScriptSigs(signed1.Inputs.AsIndexedInputs().Skip(i).First()), GetScriptSigs(signed2.Inputs.AsIndexedInputs().Skip(i).First())); var input = tx.Inputs.AsIndexedInputs().Skip(i).First(); input.WitScript = result.WitSig; input.ScriptSig = result.ScriptSig; } return tx; }
/// <summary> /// Allows to keep building on the top of a partially built transaction /// </summary> /// <param name="transaction">Transaction to complete</param> /// <returns></returns> public TransactionBuilder ContinueToBuild(Transaction transaction) { if(_CompletedTransaction != null) throw new InvalidOperationException("Transaction to complete already set"); _CompletedTransaction = transaction.Clone(); return this; }
public int EstimateSize(Transaction tx) { if(tx == null) throw new ArgumentNullException("tx"); var clone = tx.Clone(); clone.Inputs.Clear(); var baseSize = clone.ToBytes().Length; int inputSize = 0; foreach(var txin in tx.Inputs.AsIndexedInputs()) { var coin = FindSignableCoin(txin) ?? FindCoin(txin.PrevOut); if(coin == null) throw CoinNotFound(txin); inputSize += EstimateScriptSigSize(coin) + 41; } return baseSize + inputSize; }
public Transaction SignTransaction(Transaction transaction, SigHash sigHash) { var tx = transaction.Clone(); SignTransactionInPlace(tx, sigHash); return tx; }