private string GetAddressFromScript(string script)
        {
            if (string.IsNullOrEmpty(script))
            {
                return(string.Empty);
            }
            string firstOP = script.Substring(0, 2);
            string remove  = string.Empty;

            if (firstOP == NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_DUP, 1))
            {
                remove =
                    NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_DUP, 1) +
                    NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_HASH160, 1) +
                    NumberConversions.IntToHex(40 / 2, 1);
            }
            else if (firstOP == NumberConversions.IntToHex(0x01, 1))
            {
                remove =
                    NumberConversions.IntToHex(0x01, 1) +
                    NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_INVALIDOPCODE, 1) +
                    NumberConversions.IntToHex(0x16, 1) +
                    NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_PUBKEYHASH, 1) +
                    NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_0, 1);
            }

            string addressHash = script.Substring(remove.Length, 40);

            return(BitcoinConversions.Hash160ToBase58(addressHash));
        }
        public override async Task <Response <List <UTXO> > > GetUTXO(List <SendingAddress> addrList)
        {
            Response <List <UTXO> > resp = new Response <List <UTXO> >();
            string             addresses = string.Join("|", addrList.Select(b => b.Address).ToArray());
            string             url       = "https://blockchain.info/unspent?active=" + addresses;
            Response <JObject> apiResp   = await SendApiRequestAsync(url);

            if (apiResp.Errors.Any())
            {
                resp.Errors.AddRange(apiResp.Errors);
                return(resp);
            }

            resp.Result = new List <UTXO>();
            foreach (var item in apiResp.Result["unspent_outputs"])
            {
                UTXO   u      = new UTXO();
                string script = item["script"].ToString();
                u.AddressHash160 = script.Substring("76a914".Length, script.Length - "76a91488ac".Length);
                u.Address        = BitcoinConversions.Hash160ToBase58(u.AddressHash160);
                u.TxHash         = item["tx_hash_big_endian"].ToString();
                u.Amount         = (ulong)item["value"];
                u.Confirmation   = (int)item["confirmations"];
                u.OutIndex       = (uint)item["tx_output_n"];
                resp.Result.Add(u);
            }

            return(resp);
        }
Esempio n. 3
0
        private static string BuildScriptPub(string addr)
        {
            string hash160 = BitcoinConversions.Base58ToHash160(addr);

            return(ByteArray.ConcatArrays(
                       new byte[] { (byte)OPCodes.OP_DUP, (byte)OPCodes.OP_HASH160 },
                       new byte[] { (byte)(hash160.Length / 2) },
                       Base16.ToByteArray(hash160),
                       new byte[] { (byte)OPCodes.OP_EQUALVERIFY, (byte)OPCodes.OP_CHECKSIG }).ToBase16());
        }
        private void MakeTx()
        {
            List <UTXO> uList = new List <UTXO>();

            foreach (var item in TxInList)
            {
                UTXO u = new UTXO();
                u.TxHash   = item.TxId;
                u.OutIndex = item.OutIndex;
                if (BTx.Status == BitcoinTransaction.TxStatus.Signed)
                {
                    int    pubKeyLength = 65;
                    string pubKey       = item.ScriptSig.Substring((item.ScriptSigLength * 2) - (pubKeyLength + 1));
                    u.Address        = BitcoinConversions.PubKeyToBase58(pubKey);
                    u.AddressHash160 = BitcoinConversions.ByteArrayToHex(BitcoinConversions.PubKeyToHash160(pubKey));
                }
                else
                {
                    var addr = GetAddressFromScript(item.ScriptSig);
                    if (string.IsNullOrEmpty(addr))
                    {
                        u.Address        = string.Empty;
                        u.AddressHash160 = string.Empty;
                    }
                    else
                    {
                        u.Address        = addr;
                        u.AddressHash160 = BitcoinConversions.Base58ToHash160(addr);
                    }
                }

                uList.Add(u);
            }

            try
            {
                UInt32 lockTime = 0;
                RawTx = Transaction.CreateRawTx(uList, ReceiveList.ToList(), lockTime, SelectedWalletType);
            }
            catch (Exception ex)
            {
                RawTx = string.Empty;
                MessageBox.Show(ex.Message);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Makes ScriptSig based on wallet type.
        /// <para/> INCOMPLETE!
        /// </summary>
        /// <param name="addr">Hes string to put inside of ScriptSig.</param>
        /// <param name="type">Type of wallet that is supposed to sign this transaction.</param>
        /// <returns>ScriptSig</returns>
        public static string BuildScriptSig(string addr, WalletType type)
        {
            string hash160 = BitcoinConversions.Base58ToHash160(addr);

            switch (type)
            {
            case WalletType.Electrum:
                //01 ff 16 fd 00
                return(ByteArray.ConcatArrays(
                           new byte[] { 1, (byte)OPCodes.OP_INVALIDOPCODE, 16, (byte)OPCodes.OP_PUBKEYHASH, 0 },
                           Base16.ToByteArray(hash160)
                           ).ToBase16());

            case WalletType.Normal:
            case WalletType.Core:
            default:
                return(BuildScriptPub(addr));
            }
        }
        public async Task <List <UTXO> > GetUTXO(List <SendingAddress> addrList)
        {
            using (var client = new HttpClient())
            {
                // Make sure all balances are 0
                foreach (var ad in addrList)
                {
                    ad.BalanceSatoshi = 0;
                }
                var    addresses = string.Join("|", addrList.Select(b => b.Address).ToArray());
                string url       = "https://blockchain.info/unspent?active=" + addresses;
                string result    = await client.GetStringAsync(url);

                JObject     jResult = JObject.Parse(result);
                List <UTXO> utxList = new List <UTXO>();
                foreach (var item in jResult["unspent_outputs"])
                {
                    UTXO   u      = new UTXO();
                    string script = item["script"].ToString();
                    u.AddressHash160 = script.Substring("76a914".Length, script.Length - "76a91488ac".Length);
                    u.Address        = BitcoinConversions.Hash160ToBase58(u.AddressHash160);
                    u.TxHash         = item["tx_hash_big_endian"].ToString();
                    u.Amount         = (int)item["value"];
                    u.Confirmation   = (int)item["confirmations"];
                    u.OutIndex       = (UInt32)item["tx_output_n"];
                    utxList.Add(u);
                    // Update address balance
                    foreach (var ad in addrList)
                    {
                        if (ad.Address == u.Address)
                        {
                            ad.BalanceSatoshi += u.Amount;
                        }
                    }
                }

                return(utxList);
            }
        }
        /// <summary>
        /// Creates a Raw Unsigned bitcoin transaction Transaction.
        /// <para/> Sources:
        /// <para/> 1. https://bitcoin.stackexchange.com/questions/32628/redeeming-a-raw-transaction-step-by-step-example-required
        /// <para/> 2. https://bitcoin.org/en/developer-reference#raw-transaction-format
        /// <para/> 3. https://bitcoin.org/en/developer-examples#simple-raw-transaction
        /// </summary>
        /// <param name="txToSpend">List of UTXOs to spend.</param>
        /// <param name="receiveAddr">List of receiving addresses and the amount to be paid to each.</param>
        /// <param name="wallet">Type of the wallet (Electrum does not recognize normal type of scriptSig placeholder).</param>
        /// <returns>Rat unsigned transaction string.</returns>
        public static string CreateRawTx(List <UTXO> txToSpend, List <ReceivingAddress> receiveAddr, UInt32 lockTime, WalletType wallet)
        {
            StringBuilder rawTx = new StringBuilder();

            // 1) 4 byte - version
            UInt32 version = 1;

            rawTx.Append(NumberConversions.IntToHex(version, 4));

            // 2) ? byte - tx_in count (compactSize uint)
            int txInCount = txToSpend.Count;

            rawTx.Append(NumberConversions.MakeCompactSize((UInt64)txInCount));

            for (int i = 0; i < txInCount; i++)
            {
                // 3) 32 byte - TX hash (reverse)
                string txToSend = txToSpend[i].TxHash;
                rawTx.Append(ReverseTx(txToSend));

                // 4) 4 byte - output Index
                UInt32 outputIndex = txToSpend[i].OutIndex;
                rawTx.Append(NumberConversions.IntToHex(outputIndex, 4));

                // 5) ? byte - scriptSig length (compactSize uint) (Maximum value is 10,000 bytes)
                //    Can be from 0 and up. Will be replaced by actual scriptSig with another length.
                //    Format can be different based on the client which will be used for signing.
                if (string.IsNullOrEmpty(txToSpend[i].AddressHash160) && wallet != WalletType.Core)
                {
                    throw new Exception(string.Format("No address was found. Set wallet type to Core."));
                }
                string scriptSig = BuildScript(txToSpend[i].AddressHash160, wallet);
                rawTx.Append(NumberConversions.IntToHex((UInt64)(scriptSig.Length / 2), 1));

                // 6) ? byte - scriptSig which is filled with scriptPubkey temporarily (20 is half the length of address hash160)
                rawTx.Append(scriptSig);

                //7) 4 byte - sequence - max is 0xffffffff - can change for RBF transactions
                // Ref:
                // https://bitcoin.stackexchange.com/questions/2025/what-is-txins-sequence
                UInt32 sequence = UInt32.MaxValue;
                rawTx.Append(NumberConversions.IntToHex(sequence, 4));
            }

            //8) ? byte - tx_out count (compactSize uint)
            int txOutCount = receiveAddr.Count;

            rawTx.Append(NumberConversions.MakeCompactSize((UInt64)txOutCount));

            foreach (var item in receiveAddr)
            {
                //9) 8 byte - amout to transfer
                UInt64 amount = item.PaymentSatoshi;
                rawTx.Append(NumberConversions.IntToHex(amount, 8));

                //10) ? byte - pk_script length (compactSize uint)
                string itemHash     = BitcoinConversions.Base58ToHash160(item.Address);
                string outputScript = BuildScript(itemHash, WalletType.Normal);
                rawTx.Append(NumberConversions.MakeCompactSize((UInt64)outputScript.Length / 2));

                //11) ? byte - pk_script
                rawTx.Append(outputScript);
            }

            //12) 4 byte - lock time
            // * If less than 500 million, locktime is parsed as a block height. The transaction can be added to any block which has this height or higher.
            // * If greater than or equal to 500 million, locktime is parsed using the Unix epoch time format (the number of seconds elapsed since 1970-01-01T00:00 UTC—currently over 1.395 billion). The transaction can be added to any block whose block time is greater than the locktime.
            rawTx.Append(NumberConversions.IntToHex(lockTime, 4));

            return(rawTx.ToString());
        }
        /// <summary>
        /// Deserialize the transaction hex string.
        /// </summary>
        /// <param name="tx">Transaction hex string</param>
        /// <returns>Bitcoin Transaction</returns>
        public static BitcoinTransaction DecodeRawTx(string tx)
        {
            BitcoinTransaction btx = new BitcoinTransaction();
            int index = 0;

            // 1) 4 byte - version
            string version = tx.Substring(index, 8);

            btx.Version = (UInt32)NumberConversions.HexToUInt(version);
            index      += 8;

            // 2) ? byte - tx_in count (CompactSize uint)
            btx.TxInCount = NumberConversions.ReadCompactSize(tx, ref index);

            bool isSigned = true;
            bool isRbf    = false;

            // Initialize the array
            btx.TxInList = new TxIn[btx.TxInCount];
            for (UInt64 i = 0; i < btx.TxInCount; i++)
            {
                TxIn temp = new TxIn();
                // 3) 32 byte - TX hash (reverse)
                temp.TxId = tx.Substring(index, 64);
                temp.TxId = ReverseTx(temp.TxId);
                index    += 64;

                // 4) 4 byte - output Index
                string outIndex = tx.Substring(index, 8);
                temp.OutIndex = (UInt32)NumberConversions.HexToUInt(outIndex);
                index        += 8;

                // 5) ? byte - scriptSig length (CompactSize uint) (Maximum value is 10,000 bytes)
                string scriptSigLength = tx.Substring(index, 2);
                temp.ScriptSigLength = (int)NumberConversions.ReadCompactSize(tx, ref index);

                // 6) ? byte - scriptSig or a placeholder for unsigned (can be empty too)
                temp.ScriptSig = tx.Substring(index, temp.ScriptSigLength * 2);
                index         += temp.ScriptSigLength * 2;

                //7) 4 byte - sequence - max is 0xffffffff - can change for RBF transactions
                string sequence = tx.Substring(index, 8);
                temp.Sequence = (UInt32)NumberConversions.HexToUInt(sequence);
                index        += 8;

                btx.TxInList[i] = temp;

                // Check to see if all the inputs are signed
                if (temp.ScriptSigLength <= 25)
                {
                    isSigned = false;
                }

                // Check for opt-in Replace By Fee
                if (temp.Sequence != UInt32.MaxValue)
                {
                    isRbf = true;
                }
            }
            // Set transaction sign and RBF status.
            btx.Status = (isSigned) ? BitcoinTransaction.TxStatus.Signed : BitcoinTransaction.TxStatus.Unsigned;
            btx.IsRbf  = isRbf;

            //8) ? byte - tx_out count (compactSize uint)
            btx.TxOutCount = NumberConversions.ReadCompactSize(tx, ref index);

            // Initialize the array
            btx.TxOutList = new TxOut[btx.TxOutCount];
            for (UInt64 i = 0; i < btx.TxOutCount; i++)
            {
                TxOut temp = new TxOut();

                //9) 8 byte - amout to transfer
                string amount = tx.Substring(index, 16);
                temp.Amount = NumberConversions.HexToUInt(amount);
                index      += 16;

                //10) ? byte - pk_script length (compactSize uint)
                string pkScriptLength = tx.Substring(index, 2);
                temp.PkScriptLength = (Int32)NumberConversions.HexToUInt(pkScriptLength);
                index += 2;

                //11) ? byte - pk_script
                temp.PkScript = tx.Substring(index, temp.PkScriptLength * 2);
                index        += temp.PkScriptLength * 2;

                btx.TxOutList[i] = temp;
            }

            //12) 4 byte - lock time
            string lockTime = tx.Substring(index, 8);

            btx.LockTime = (UInt32)NumberConversions.HexToUInt(lockTime);
            index       += 8;

            // If the transaction is signed, then it has a TxId
            if (isSigned)
            {
                btx.TxId = BitcoinConversions.GetTxId(tx);
            }

            return(btx);
        }