internal PSBTInput(BitcoinStream stream, PSBT parent, uint index, TxIn input) : base(parent) { TxIn = input; Index = index; originalScriptSig = TxIn.ScriptSig ?? Script.Empty; originalWitScript = TxIn.WitScript ?? WitScript.Empty; byte[] k = new byte[0]; byte[] v = new byte[0]; try { stream.ReadWriteAsVarString(ref k); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTInput. Failed to Parse key.", e); } while (k.Length != 0) { try { stream.ReadWriteAsVarString(ref v); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTInput. Failed to parse key.", e); } switch (k.First()) { case PSBTConstants.PSBT_IN_NON_WITNESS_UTXO: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for NonWitnessUTXO"); } if (non_witness_utxo != null) { throw new FormatException("Invalid PSBTInput. Duplicate non_witness_utxo"); } non_witness_utxo = this.GetConsensusFactory().CreateTransaction(); non_witness_utxo.FromBytes(v); break; case PSBTConstants.PSBT_IN_WITNESS_UTXO: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for WitnessUTXO"); } if (witness_utxo != null) { throw new FormatException("Invalid PSBTInput. Duplicate witness_utxo"); } if (this.GetConsensusFactory().TryCreateNew <TxOut>(out var txout)) { witness_utxo = txout; } else { witness_utxo = new TxOut(); } witness_utxo.FromBytes(v); break; case PSBTConstants.PSBT_IN_PARTIAL_SIG: var pubkey = new PubKey(k.Skip(1).ToArray()); if (partial_sigs.ContainsKey(pubkey)) { throw new FormatException("Invalid PSBTInput. Duplicate key for partial_sigs"); } partial_sigs.Add(pubkey, new TransactionSignature(v)); break; case PSBTConstants.PSBT_IN_SIGHASH: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for SigHash type"); } if (!(sighash_type is null)) { throw new FormatException("Invalid PSBTInput. Duplicate key for sighash_type"); } if (v.Length != 4) { throw new FormatException("Invalid PSBTInput. SigHash Type is not 4 byte"); } var value = Utils.ToUInt32(v, 0, true); if (!Enum.IsDefined(typeof(SigHash), value)) { throw new FormatException($"Invalid PSBTInput Unknown SigHash Type {value}"); } sighash_type = (SigHash)value; break; case PSBTConstants.PSBT_IN_REDEEMSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for redeem script"); } if (redeem_script != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for redeem_script"); } redeem_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_IN_WITNESSSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for witness script"); } if (witness_script != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for redeem_script"); } witness_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_IN_BIP32_DERIVATION: var pubkey2 = new PubKey(k.Skip(1).ToArray()); if (hd_keypaths.ContainsKey(pubkey2)) { throw new FormatException("Invalid PSBTInput. Duplicate key for hd_keypaths"); } var masterFingerPrint = new HDFingerprint(v.Take(4).ToArray()); KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray()); hd_keypaths.Add(pubkey2, new RootedKeyPath(masterFingerPrint, path)); break; case PSBTConstants.PSBT_IN_SCRIPTSIG: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for final scriptsig"); } if (final_script_sig != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for final_script_sig"); } final_script_sig = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_IN_SCRIPTWITNESS: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for final script witness"); } if (final_script_witness != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for final_script_witness"); } final_script_witness = new WitScript(v); break; default: if (unknown.ContainsKey(k)) { throw new FormatException("Invalid PSBTInput. Duplicate key for unknown value"); } unknown.Add(k, v); break; } stream.ReadWriteAsVarString(ref k); } }
public void Serialize(BitcoinStream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } // Write the utxo // If there is a non-witness utxo, then don't serialize the witness one. if (witness_utxo != null) { // key stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_WITNESS_UTXO; stream.ReadWrite(ref key); // value var data = witness_utxo.ToBytes(); stream.ReadWriteAsVarString(ref data); } if (non_witness_utxo != null) { // key stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_NON_WITNESS_UTXO; stream.ReadWrite(ref key); // value byte[] data = non_witness_utxo.ToBytes(); stream.ReadWriteAsVarString(ref data); } // Write the sighash type if (sighash_type > 0) { stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_SIGHASH; stream.ReadWrite(ref key); var tmp = Utils.ToBytes((uint)sighash_type, true); stream.ReadWriteAsVarString(ref tmp); } // Write the redeem script if (redeem_script != null) { stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_REDEEMSCRIPT; stream.ReadWrite(ref key); var value = redeem_script.ToBytes(); stream.ReadWriteAsVarString(ref value); } // Write the witness script if (witness_script != null) { stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_WITNESSSCRIPT; stream.ReadWrite(ref key); var value = witness_script.ToBytes(); stream.ReadWriteAsVarString(ref value); } // Write any partial signatures foreach (var sig_pair in partial_sigs) { var key = new byte[] { PSBTConstants.PSBT_IN_PARTIAL_SIG }.Concat(sig_pair.Key.ToBytes()); stream.ReadWriteAsVarString(ref key); var sig = sig_pair.Value.ToBytes(); stream.ReadWriteAsVarString(ref sig); } // Write any hd keypaths foreach (var pathPair in hd_keypaths) { var key = new byte[] { PSBTConstants.PSBT_IN_BIP32_DERIVATION }.Concat(pathPair.Key.ToBytes()); stream.ReadWriteAsVarString(ref key); var masterFingerPrint = pathPair.Value.MasterFingerprint; var path = pathPair.Value.KeyPath.ToBytes(); var pathInfo = masterFingerPrint.ToBytes().Concat(path); stream.ReadWriteAsVarString(ref pathInfo); } // Write script sig if (final_script_sig != null) { stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_SCRIPTSIG; stream.ReadWrite(ref key); byte[] value = final_script_sig.ToBytes(); stream.ReadWriteAsVarString(ref value); } // write script witness if (final_script_witness != null) { stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_IN_SCRIPTWITNESS; stream.ReadWrite(ref key); var stack = final_script_witness.ToBytes(); stream.ReadWriteAsVarString(ref stack); } // Write unknown things foreach (var entry in unknown) { var k = entry.Key; var v = entry.Value; stream.ReadWriteAsVarString(ref k); stream.ReadWriteAsVarString(ref v); } var sep = PSBTConstants.PSBT_SEPARATOR; stream.ReadWrite(ref sep); }
internal PSBTOutput(BitcoinStream stream, PSBT parent, uint index, TxOut txOut) { if (txOut == null) { throw new ArgumentNullException(nameof(txOut)); } if (parent == null) { throw new ArgumentNullException(nameof(parent)); } hd_keypaths = new HDKeyPathKVMap(new PubKeyComparer()); unknown = new UnKnownKVMap(BytesComparer.Instance); Parent = parent; TxOut = txOut; Index = index; byte[] k = new byte[0]; byte[] v = new byte[0]; try { stream.ReadWriteAsVarString(ref k); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTOutput. Could not read key", e); } while (k.Length != 0) { try { stream.ReadWriteAsVarString(ref v); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTOutput. Could not read value", e); } switch (k.First()) { case PSBTConstants.PSBT_OUT_REDEEMSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTOutput. Contains illegal value in key for redeem script"); } if (redeem_script != null) { throw new FormatException("Invalid PSBTOutput, duplicate key for redeem_script"); } redeem_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_OUT_WITNESSSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTOutput. Contains illegal value in key for witness script"); } if (witness_script != null) { throw new FormatException("Invalid PSBTOutput, duplicate key for redeem_script"); } witness_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_OUT_BIP32_DERIVATION: var pubkey2 = new PubKey(k.Skip(1).ToArray()); if (hd_keypaths.ContainsKey(pubkey2)) { throw new FormatException("Invalid PSBTOutput, duplicate key for hd_keypaths"); } KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray()); hd_keypaths.Add(pubkey2, Tuple.Create(new HDFingerprint(v.Take(4).ToArray()), path)); break; default: if (unknown.ContainsKey(k)) { throw new FormatException("Invalid PSBTInput, duplicate key for unknown value"); } unknown.Add(k, v); break; } stream.ReadWriteAsVarString(ref k); } }
public void ReadWrite(BitcoinStream stream) { stream.ReadWriteAsVarString(ref _Data); }
internal PSBT(BitcoinStream stream) { 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.First()) { 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; 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 PSBT(BitcoinStream stream, Network network) { if (network == null) { throw new ArgumentNullException(nameof(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 PSBTOutput(BitcoinStream stream, PSBT parent, uint index, TxOut txOut) : base(parent) { if (txOut == null) { throw new ArgumentNullException(nameof(txOut)); } if (parent == null) { throw new ArgumentNullException(nameof(parent)); } TxOut = txOut; Index = index; byte[] k = new byte[0]; byte[] v = new byte[0]; try { stream.ReadWriteAsVarString(ref k); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTOutput. Could not read key", e); } while (k.Length != 0) { try { stream.ReadWriteAsVarString(ref v); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTOutput. Could not read value", e); } switch (k.First()) { case PSBTConstants.PSBT_OUT_REDEEMSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTOutput. Contains illegal value in key for redeem script"); } if (redeem_script != null) { throw new FormatException("Invalid PSBTOutput, duplicate key for redeem_script"); } redeem_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_OUT_WITNESSSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTOutput. Unexpected key length for PSBT_OUT_BIP32_DERIVATION"); } if (witness_script != null) { throw new FormatException("Invalid PSBTOutput, duplicate key for redeem_script"); } witness_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_OUT_BIP32_DERIVATION: var pubkey2 = new PubKey(k.Skip(1).ToArray()); if (hd_keypaths.ContainsKey(pubkey2)) { throw new FormatException("Invalid PSBTOutput, duplicate key for hd_keypaths"); } KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray()); hd_keypaths.Add(pubkey2, new RootedKeyPath(new HDFingerprint(v.Take(4).ToArray()), path)); break; case PSBTConstants.PSBT_OUT_TAP_BIP32_DERIVATION: var pubkey3 = new TaprootPubKey(k.Skip(1).ToArray()); if (hd_taprootkeypaths.ContainsKey(pubkey3)) { throw new FormatException("Invalid PSBTOutput, duplicate key for hd_taproot_keypaths"); } var bs = new BitcoinStream(v); List <uint256> hashes = null !; bs.ReadWrite(ref hashes); var pos = (int)bs.Inner.Position; KeyPath path2 = KeyPath.FromBytes(v.Skip(pos + 4).ToArray()); hd_taprootkeypaths.Add(pubkey3, new TaprootKeyPath( new RootedKeyPath(new HDFingerprint(v.Skip(pos).Take(4).ToArray()), path2), hashes.ToArray())); break; case PSBTConstants.PSBT_OUT_TAP_INTERNAL_KEY: if (k.Length != 1) { throw new FormatException("Invalid PSBTOutput. Contains illegal value in key for internal taproot pubkey"); } if (!TaprootInternalPubKey.TryCreate(v, out var tpk)) { throw new FormatException("Invalid PSBTOutput. Contains invalid internal taproot pubkey"); } TaprootInternalKey = tpk; break; default: if (unknown.ContainsKey(k)) { throw new FormatException("Invalid PSBTInput, duplicate key for unknown value"); } unknown.Add(k, v); break; } stream.ReadWriteAsVarString(ref k); } }
public void Serialize(BitcoinStream stream) { if (redeem_script != null) { stream.ReadWriteAsVarInt(ref defaultKeyLen); stream.ReadWrite(PSBTConstants.PSBT_OUT_REDEEMSCRIPT); var value = redeem_script.ToBytes(); stream.ReadWriteAsVarString(ref value); } if (witness_script != null) { stream.ReadWriteAsVarInt(ref defaultKeyLen); stream.ReadWrite(PSBTConstants.PSBT_OUT_WITNESSSCRIPT); var value = witness_script.ToBytes(); stream.ReadWriteAsVarString(ref value); } if (this.TaprootInternalKey is TaprootInternalPubKey tp) { stream.ReadWriteAsVarInt(ref defaultKeyLen); var key = PSBTConstants.PSBT_OUT_TAP_INTERNAL_KEY; stream.ReadWrite(ref key); var b = tp.ToBytes(); stream.ReadWriteAsVarString(ref b); } foreach (var pathPair in hd_keypaths) { var key = new byte[] { PSBTConstants.PSBT_OUT_BIP32_DERIVATION }.Concat(pathPair.Key.ToBytes()); stream.ReadWriteAsVarString(ref key); var path = pathPair.Value.KeyPath.ToBytes(); var pathInfo = pathPair.Value.MasterFingerprint.ToBytes().Concat(path); stream.ReadWriteAsVarString(ref pathInfo); } foreach (var pathPair in hd_taprootkeypaths) { var key = new byte[] { PSBTConstants.PSBT_OUT_TAP_BIP32_DERIVATION }.Concat(pathPair.Key.ToBytes()); stream.ReadWriteAsVarString(ref key); uint leafCount = (uint)pathPair.Value.LeafHashes.Length; BitcoinStream bs = new BitcoinStream(new MemoryStream(), true); bs.ReadWriteAsVarInt(ref leafCount); foreach (var hash in pathPair.Value.LeafHashes) { bs.ReadWrite(hash); } var b = pathPair.Value.RootedKeyPath.MasterFingerprint.ToBytes(); bs.ReadWrite(ref b); b = pathPair.Value.RootedKeyPath.KeyPath.ToBytes(); bs.ReadWrite(ref b); b = ((MemoryStream)bs.Inner).ToArrayEfficient(); stream.ReadWriteAsVarString(ref b); } foreach (var entry in unknown) { var k = entry.Key; var v = entry.Value; stream.ReadWriteAsVarString(ref k); stream.ReadWriteAsVarString(ref v); } var sep = PSBTConstants.PSBT_SEPARATOR; stream.ReadWrite(ref sep); }
private static byte[] ReadPush(BitcoinStream stream) { byte[] push = null; stream.ReadWriteAsVarString(ref push); return(push); }