예제 #1
0
        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);
            }
        }
예제 #2
0
 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);
            }
        }
예제 #4
0
 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);
     }
 }
예제 #5
0
        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);
        }
예제 #6
0
        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();
        }
예제 #7
0
        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());
        }
예제 #8
0
        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);
        }
예제 #9
0
        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");
            }
        }
예제 #10
0
        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");
            }
        }
예제 #11
0
        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();
            }
        }
예제 #12
0
        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);
        }