public bool Equals(PSBTInput other) => other != null && this.ToBytes().SequenceEqual(other.ToBytes());
internal PSBT(BitcoinStream stream, Network network) { Network = network; Inputs = new PSBTInputList(); Outputs = new PSBTOutputList(); var magicBytes = stream.Inner.ReadBytes(PSBT_MAGIC_BYTES.Length); if (!magicBytes.SequenceEqual(PSBT_MAGIC_BYTES)) { throw new FormatException("Invalid PSBT magic bytes"); } // It will be reassigned in `ReadWriteAsVarString` so no worry to assign 0 length array here. byte[] k = new byte[0]; byte[] v = new byte[0]; var txFound = false; stream.ReadWriteAsVarString(ref k); while (k.Length != 0) { switch (k[0]) { case PSBTConstants.PSBT_GLOBAL_UNSIGNED_TX: if (k.Length != 1) { throw new FormatException("Invalid PSBT. Contains illegal value in key global tx"); } if (tx != null) { throw new FormatException("Duplicate Key, unsigned tx already provided"); } tx = stream.ConsensusFactory.CreateTransaction(); uint size = 0; stream.ReadWriteAsVarInt(ref size); var pos = stream.Counter.ReadenBytes; tx.ReadWrite(stream); if (stream.Counter.ReadenBytes - pos != size) { throw new FormatException("Malformed global tx. Unexpected size."); } if (tx.Inputs.Any(txin => txin.ScriptSig != Script.Empty || txin.WitScript != WitScript.Empty)) { throw new FormatException("Malformed global tx. It should not contain any scriptsig or witness by itself"); } txFound = true; break; case PSBTConstants.PSBT_GLOBAL_XPUB when XPubVersionBytes != null: if (k.Length != 1 + XPubVersionBytes.Length + 74) { throw new FormatException("Malformed global xpub."); } for (int ii = 0; ii < XPubVersionBytes.Length; ii++) { if (k[1 + ii] != XPubVersionBytes[ii]) { throw new FormatException("Malformed global xpub."); } } stream.ReadWriteAsVarString(ref v); KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray()); var rootedKeyPath = new RootedKeyPath(new HDFingerprint(v.Take(4).ToArray()), path); GlobalXPubs.Add(new ExtPubKey(k, 1 + XPubVersionBytes.Length, 74).GetWif(Network), rootedKeyPath); break; default: if (unknown.ContainsKey(k)) { throw new FormatException("Invalid PSBTInput, duplicate key for unknown value"); } stream.ReadWriteAsVarString(ref v); unknown.Add(k, v); break; } stream.ReadWriteAsVarString(ref k); } if (!txFound) { throw new FormatException("Invalid PSBT. No global TX"); } int i = 0; while (stream.Inner.CanRead && i < tx.Inputs.Count) { var psbtin = new PSBTInput(stream, this, (uint)i, tx.Inputs[i]); Inputs.Add(psbtin); i++; } if (i != tx.Inputs.Count) { throw new FormatException("Invalid PSBT. Number of input does not match to the global tx"); } i = 0; while (stream.Inner.CanRead && i < tx.Outputs.Count) { var psbtout = new PSBTOutput(stream, this, (uint)i, tx.Outputs[i]); Outputs.Add(psbtout); i++; } if (i != tx.Outputs.Count) { throw new FormatException("Invalid PSBT. Number of outputs does not match to the global tx"); } }
internal void Combine(PSBTInput other) { if (this.IsFinalized()) { return; } foreach (var uk in other.unknown) { unknown.TryAdd(uk.Key, uk.Value); } if (other.final_script_sig != null) { final_script_sig = other.final_script_sig; } if (other.final_script_witness != null) { final_script_witness = other.final_script_witness; } if (IsFinalized()) { ClearForFinalize(); return; } if (non_witness_utxo == null && other.non_witness_utxo != null) { non_witness_utxo = other.non_witness_utxo; } if (witness_utxo == null && other.witness_utxo != null) { non_witness_utxo = other.non_witness_utxo; } if (sighash_type == 0 && other.sighash_type > 0) { sighash_type = other.sighash_type; } if (redeem_script == null && other.redeem_script != null) { redeem_script = other.redeem_script; } if (witness_script == null && other.witness_script != null) { witness_script = other.witness_script; } foreach (var sig in other.partial_sigs) { partial_sigs.TryAdd(sig.Key, sig.Value); } foreach (var keyPath in other.hd_keypaths) { hd_keypaths.TryAdd(keyPath.Key, keyPath.Value); } }