public void Serialize(BitcoinStream stream) { // magic bytes stream.Inner.Write(PSBT_MAGIC_BYTES, 0, PSBT_MAGIC_BYTES.Length); // unsigned tx flag stream.ReadWriteAsVarInt(ref defaultKeyLen); stream.ReadWrite(PSBTConstants.PSBT_GLOBAL_UNSIGNED_TX); // Write serialized tx to a stream stream.TransactionOptions &= TransactionOptions.None; uint txLength = (uint)tx.GetSerializedSize(TransactionOptions.None); stream.ReadWriteAsVarInt(ref txLength); stream.ReadWrite(tx); foreach (var xpub in GlobalXPubs) { if (xpub.Key.Network != Network) { throw new InvalidOperationException("Invalid key inside the global xpub collection"); } var len = (uint)(1 + XPubVersionBytes.Length + 74); stream.ReadWriteAsVarInt(ref len); stream.ReadWrite(PSBTConstants.PSBT_GLOBAL_XPUB); var vb = XPubVersionBytes; stream.ReadWrite(ref vb); xpub.Key.ExtPubKey.ReadWrite(stream); var path = xpub.Value.KeyPath.ToBytes(); var pathInfo = xpub.Value.MasterFingerprint.ToBytes().Concat(path); stream.ReadWriteAsVarString(ref pathInfo); } // Write the unknown things foreach (var kv in unknown) { byte[] k = kv.Key; byte[] v = kv.Value; stream.ReadWriteAsVarString(ref k); stream.ReadWriteAsVarString(ref v); } // Separator var sep = PSBTConstants.PSBT_SEPARATOR; stream.ReadWrite(ref sep); // Write inputs foreach (var psbtin in Inputs) { psbtin.Serialize(stream); } // Write outputs foreach (var psbtout in Outputs) { psbtout.Serialize(stream); } }
public static void ReadWriteC(this BitcoinStream bs, ref PoupardSternProof proof) { if (bs.Serializing) { if (proof == null) { uint o = 0; bs.ReadWriteAsVarInt(ref o); return; } var len = (uint)proof.XValues.Length; bs.ReadWriteAsVarInt(ref len); for (int i = 0; i < len; i++) { var n = proof.XValues[i]; bs.ReadWriteC(ref n); } var yvalue = proof.YValue; bs.ReadWriteC(ref yvalue); } else { uint len = 0; bs.ReadWriteAsVarInt(ref len); if (len == 0) { proof = null; return; } if (len > bs.MaxArraySize) { throw new ArgumentOutOfRangeException("Array is too big"); } var xValues = new BigInteger[len]; for (int i = 0; i < len; i++) { BigInteger b = null; bs.ReadWriteC(ref b); xValues[i] = b; } BigInteger yValue = null; bs.ReadWriteC(ref yValue); proof = new PoupardSternProof(Tuple.Create(xValues, yValue)); } }
public void Serialize(BitcoinStream stream) { AssertSanity(); // magic bytes stream.Inner.Write(PSBT_MAGIC_BYTES, 0, PSBT_MAGIC_BYTES.Length); // unsigned tx flag stream.ReadWriteAsVarInt(ref defaultKeyLen); stream.ReadWrite(PSBTConstants.PSBT_GLOBAL_UNSIGNED_TX); // Write serialized tx to a stream stream.TransactionOptions &= TransactionOptions.None; uint txLength = (uint)tx.GetSerializedSize(TransactionOptions.None); stream.ReadWriteAsVarInt(ref txLength); stream.ReadWrite(tx); // Write the unknown things foreach (var kv in unknown) { byte[] k = kv.Key; byte[] v = kv.Value; stream.ReadWriteAsVarString(ref k); stream.ReadWriteAsVarString(ref v); } // Separator var sep = PSBTConstants.PSBT_SEPARATOR; stream.ReadWrite(ref sep); // Write inputs foreach (var psbtin in Inputs) { psbtin.Serialize(stream); } // Write outputs foreach (var psbtout in Outputs) { psbtout.Serialize(stream); } }
public static void ReadWriteC(this BitcoinStream bs, ref PermutationTestProof proof) { if (bs.Serializing) { if (proof == null) { uint o = 0; bs.ReadWriteAsVarInt(ref o); return; } var len = (uint)proof.Signatures.Length; bs.ReadWriteAsVarInt(ref len); for (int i = 0; i < len; i++) { var sig = proof.Signatures[i]; bs.ReadWriteAsVarString(ref sig); } } else { uint len = 0; bs.ReadWriteAsVarInt(ref len); if (len == 0) { proof = null; return; } if (len > bs.MaxArraySize) { throw new ArgumentOutOfRangeException("Array is too big"); } var signatures = new byte[len][]; for (int i = 0; i < len; i++) { byte[] sig = null; bs.ReadWriteAsVarString(ref sig); signatures[i] = sig; } proof = new PermutationTestProof(signatures); } }
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); } 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.Item2.ToBytes(); var pathInfo = pathPair.Value.Item1.ToBytes().Concat(path); stream.ReadWriteAsVarString(ref pathInfo); } 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); }
void ReadCore(BitcoinStream stream) { List <byte[]> pushes = new List <byte[]>(); uint pushCount = 0; stream.ReadWriteAsVarInt(ref pushCount); for (int i = 0; i < (int)pushCount; i++) { byte[] push = ReadPush(stream); pushes.Add(push); } _Pushes = pushes.ToArray(); }
public byte[] ToBytes() { var ms = new MemoryStream(); BitcoinStream stream = new BitcoinStream(ms, true); uint pushCount = (uint)_Pushes.Length; stream.ReadWriteAsVarInt(ref pushCount); foreach (var push in Pushes) { var localpush = push; stream.ReadWriteAsVarString(ref localpush); } return(ms.ToArrayEfficient()); }
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 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 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"); } }
public void ReadWrite(BitcoinStream stream) { if (stream.Serializing) { uint nMaskSize = 0, nMaskCode = 0; CalcMaskSize(ref nMaskSize, ref nMaskCode); bool fFirst = vout.Count > 0 && !vout[0].IsNull; bool fSecond = vout.Count > 1 && !vout[1].IsNull; uint nCode = unchecked ((uint)(8 * (nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0))); // version stream.ReadWriteAsVarInt(ref nVersion); // size of header code stream.ReadWriteAsVarInt(ref nCode); // spentness bitmask for (uint b = 0; b < nMaskSize; b++) { byte chAvail = 0; for (uint i = 0; i < 8 && 2 + b * 8 + i < vout.Count; i++) { if (!vout[2 + (int)b * 8 + (int)i].IsNull) { chAvail |= (byte)(1 << (int)i); } } stream.ReadWrite(ref chAvail); } // txouts themself for (uint i = 0; i < vout.Count; i++) { if (!vout[(int)i].IsNull) { var compressedTx = new TxOutCompressor(vout[(int)i]); stream.ReadWrite(ref compressedTx); } } // coinbase height stream.ReadWriteAsVarInt(ref nHeight); } else { uint nCode = 0; // version stream.ReadWriteAsVarInt(ref nVersion); //// header code stream.ReadWriteAsVarInt(ref nCode); fCoinBase = (nCode & 1) != 0; List <bool> vAvail = new List <bool>() { false, false }; vAvail[0] = (nCode & 2) != 0; vAvail[1] = (nCode & 4) != 0; uint nMaskCode = unchecked ((uint)((nCode / 8) + ((nCode & 6) != 0 ? 0 : 1))); //// spentness bitmask while (nMaskCode > 0) { byte chAvail = 0; stream.ReadWrite(ref chAvail); for (uint p = 0; p < 8; p++) { bool f = (chAvail & (1 << (int)p)) != 0; vAvail.Add(f); } if (chAvail != 0) { nMaskCode--; } } // txouts themself vout = Enumerable.Range(0, vAvail.Count).Select(_ => new TxOut()).ToList(); for (uint i = 0; i < vAvail.Count; i++) { if (vAvail[(int)i]) { TxOutCompressor compressed = new TxOutCompressor(); stream.ReadWrite(ref compressed); vout[(int)i] = compressed.TxOut; } } //// coinbase height stream.ReadWriteAsVarInt(ref nHeight); Cleanup(); UpdateValue(); } }
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); }