/// <summary> /// Create the signing bag with matching scripts and private keys /// Then sign the inputs. /// </summary> public Transaction Sign(CoinParameters parameters, SignRawTransaction rawTransactions) { var transaction = this.Serializer.FromHex(rawTransactions.RawTransactionHex); var bag = new TransactionSigner.SignerBag { Items = new List <TransactionSigner.RedeemScript>() }; var keys = rawTransactions.PrivateKeys.Select(s => new BitcoinPrivateKey(parameters, s)).ToList(); // create a linked object between the pub key hash and its outpoint var inputs = rawTransactions.Inputs .Select(input => new { Input = input, PubKeyHash = new Script.Script(CryptoUtil.ConvertHex(input.ScriptPubKey)).TryGetPubKeyHash() }) .Where(p => p.PubKeyHash.IsNotNull()).ToList(); // compare private keys with redeem script pub key hash and add to the bag foreach (var key in keys) { // there should be at least one redeem script per private key var inserts = inputs.Where(f => f.PubKeyHash.SequenceEqual(key.Hash160)).ToList(); Thrower.If(inserts.None()).Throw <TransactionException>("Private key had no matching redeem script '{0}'".StringFormat(key.PublicKey.ToAddress(parameters).ToString())); inserts.ForEach(insert => bag.Add(insert.Input.TransactionId, insert.Input.Output, insert.Input.ScriptPubKey, key)); } this.Signer.SignInputs(transaction, bag); return(transaction); }
/// <summary> /// The create a pay to pub key hash transaction. /// </summary> public virtual Transaction CreatePubKeyHashTransaction(CoinParameters parameters, CreateRawTransaction rawTransaction) { var transaction = new Transaction { Version = parameters.TransactionVersion, Locktime = 0, Timestamp = (int)DateTime.UtcNow.UnixTimeStampFromDateTime() }; // create the inputs transaction.Inputs = rawTransaction.Inputs .Select(input => new TransactionInput { Outpoint = new TransactionOutPoint { Hash = input.TransactionId, Index = input.Output }, ScriptBytes = Enumerable.Empty <byte>().ToArray(), Sequence = 0xffffffff // uint.MaxValue }).ToList(); // create the output transaction.Outputs = rawTransaction.Outputs .Select((output, index) => new TransactionOutput { Index = index, Value = this.Serializer.ValueToNum(output.Value), ScriptBytes = ScriptBuilder.CreateOutputScript(Address.Create(parameters, output.Key)).GetProgram() }).ToList(); return(transaction); }
public void Given_a_signed_transaction_with_multipul_inputs_When_serialized_to_hex_Then_is_valid() { var param = new CoinParameters { CoinTag = "BTC", PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var builder = TransactionBuilder.Create(param); var key1 = new BitcoinPrivateKey(param, "KxSFAwxgg1kh2CKqmVGDJfQBKypAabiJ7UgDbJcvnRQ3ccbCm8oz"); var key2 = new BitcoinPrivateKey(param, "KwgcwLEWhzpcWvZtEJuYiPk4BJHNtGhp1tdqL2wKt1xe4Kv8kVd5"); var create = builder.CreateTransaction() .AddInput("2a99b070f457c8f2f6cb0f818b951c2038759af7abd98e57aecaf73989cd3474", 1) .AddInput("2a99b070f457c8f2f6cb0f818b951c2038759af7abd98e57aecaf73989cd3474", 0) .AddOutput("19vrUuKgwKvAhiXfwVBaeHDQo7wMJg4VjD", 0.00020000M) .AddOutput("1227pgTWYNsPNVN647NzsceFeXeLt18fTT", 0.00005000M); var trx = builder.CreatePubKeyHashTransaction(param, create); var unsigned = builder.Serializer.ToHex(trx); var sign = builder.CreateSignature(unsigned) .AddKey(key1.ToWifKey(param)) .AddKey(key2.ToWifKey(param)) .AddInput("2a99b070f457c8f2f6cb0f818b951c2038759af7abd98e57aecaf73989cd3474", 0, "76a914a92c9ee052cdcb7ac955b179d5d5553aa1aed28f88ac") .AddInput("2a99b070f457c8f2f6cb0f818b951c2038759af7abd98e57aecaf73989cd3474", 1, "76a914c789d0b3ec2bee3b2e057e2b759d1bd249095cd888ac"); trx = builder.Sign(param, sign); var signed = builder.Serializer.ToHex(trx); Assert.AreEqual("01000000027434cd8939f7caae578ed9abf79a7538201c958b810fcbf6f2c857f470b0992a010000006b483045022100b8642683528bce3b71911e970b045c05309035e410c7d7b3ff4def78958fe8670220148eb4bf6b07aa32b74c1041dc1c98b6646d68c3f4001b7e7683abd705ec4c3101210396c9b0b5664c28c4667e9931a9665a7d99222794b3a1d514e3dd198c2cc688e0ffffffff7434cd8939f7caae578ed9abf79a7538201c958b810fcbf6f2c857f470b0992a000000006b48304502210083099361f7feca8003687bb1a5454447846adec11b23e9760cc0c96273fcd39202207894c09e8744b266dfac87dcf0e6aedb848caf4aa451b285a14dc6fc041b6a1a012102b5d6110585425f5af0eaf84262e3962477d7974f97ed5ead5bd25a1eb415c40dffffffff02204e0000000000001976a91461f05b61eb47be1d96e417272d3d3d3c346e2e9188ac88130000000000001976a9140b2e3c8cfae581f5fa526070a705e617d3c61be488ac00000000", signed); }
public void Given_a_signed_transaction_When_serialized_to_hex_Then_is_valid() { var param = new CoinParameters { CoinTag = "BTC", PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var builder = TransactionBuilder.Create(param); var prv = "KxSFAwxgg1kh2CKqmVGDJfQBKypAabiJ7UgDbJcvnRQ3ccbCm8oz"; var key = new BitcoinPrivateKey(param, prv); var destination = Address.Create(param, "19vrUuKgwKvAhiXfwVBaeHDQo7wMJg4VjD"); var change = Address.Create(param, "1KC4ZkESbdjYVfdYWuAkmSscQfD1VEWb32"); var create = new CreateRawTransaction() .AddInput("9e779dbc84fe9c4082bc6fff332ddee81a6f56908720f25cb83ea9441cfa801b", 0) .AddOutput(destination.ToString(), 0.00010000M) .AddOutput(change.ToString(), 0.00030000M); var trx = builder.CreatePubKeyHashTransaction(param, create); var unsigned = builder.Serializer.ToHex(trx); var sign = new SignRawTransaction(unsigned) .AddKey(key.ToWifKey(param)) .AddInput("9e779dbc84fe9c4082bc6fff332ddee81a6f56908720f25cb83ea9441cfa801b", 0, "76a914c789d0b3ec2bee3b2e057e2b759d1bd249095cd888ac"); trx = builder.Sign(param, sign); var signed = builder.Serializer.ToHex(trx); Assert.AreEqual("01000000011b80fa1c44a93eb85cf2208790566f1ae8de2d33ff6fbc82409cfe84bc9d779e000000006b483045022100911e4ffada078f43c792b797b0d864a519cb088083b89003810aaf28325a6ecd02207f2e9790511710ae93309f4b3b30c0379b8917b6a0fb1cdcc9c711fc6f1831ef01210396c9b0b5664c28c4667e9931a9665a7d99222794b3a1d514e3dd198c2cc688e0ffffffff0210270000000000001976a91461f05b61eb47be1d96e417272d3d3d3c346e2e9188ac30750000000000001976a914c789d0b3ec2bee3b2e057e2b759d1bd249095cd888ac00000000", signed); }
/// <summary> /// Initializes a new instance of the <see cref="BitcoinPrivateKey"/> class. /// Parses the given private key as created by the BitCoin C++ RPC. /// </summary> /// <param name="coinParameters"> /// The expected header parameters of the key. If you don't care, provide null. /// </param> /// <param name="encoded"> /// The base58 encoded string. /// </param> public BitcoinPrivateKey(CoinParameters coinParameters, string encoded) { var decoded = Base58Encoding.DecodeWithCheckSum(encoded); var version = decoded.First(); Thrower.Condition <ArgumentException>(coinParameters.PrivateKeyVersion != version, string.Format("Mismatched version number, trying to cross networks? expected={0} found={1}", coinParameters.PrivateKeyVersion, version)); var bytes = decoded.Skip(1).ToArray(); this.Compressed = false; if (bytes.Length == 33) { // private key associated with a compressed public key Thrower.Condition <ArgumentException>(bytes.Last() != CompressedBytes.First(), string.Format("Invalid private key")); bytes = bytes.Take(32).ToArray(); this.Compressed = true; } // 256 bit keys Thrower.Condition <ArgumentException>(bytes.Length != Length, string.Format("Keys are 256 bits, so you must provide {0} bytes, got {1}", Length, bytes.Length)); this.Bytes = bytes; }
public CoinService(bool useTestNet, string daemonUrl, string rpcUsername, string rpcPassword, string walletPassword, short rpcRequestTimeoutInSeconds) { _rpcConnector = new RpcConnector(this); Parameters = new CoinParameters(this, daemonUrl, rpcUsername, rpcPassword, walletPassword, rpcRequestTimeoutInSeconds); Parameters.DaemonUrlTestnet = daemonUrl; Parameters.UseTestnet = useTestNet; }
/// <summary> /// Create a bitcoin address from a public key RIPEMD160 hash and specify if it is a Script or a Pub key /// </summary> public static Address Create(CoinParameters coinParameters, byte[] hash160, bool isScript) { return(new Address { Hash160 = hash160, CoinParameters = coinParameters, AddressVersion = isScript ? coinParameters.ScriptAddressVersion : coinParameters.PublicKeyAddressVersion }); }
public void Given_an_rpc_transaction_with_big_inputs_When_deserialized_to_hex_Then_is_successfully_compared() { // A json transaction as it's returned by the rpc method var param = new CoinParameters { CoinTag = "BTC", PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var builder = TransactionBuilder.Create(param); var BigTransaction = File.OpenText(@"..\..\Resources\BigTransaction.json").ReadToEnd(); var trx = JsonSerializer.DeSerialize <DecodedRawTransaction>(BigTransaction); var rawTransaction = builder.Serializer.ToHex(builder.Parse(trx)); Assert.AreEqual(trx.Hex, rawTransaction); }
public void Given_an_rpc_coinbase_transaction_When_deserialized_from_hex_Then_is_successfully_compared() { // A json transaction as it's returned by the rpc method var param = new CoinParameters { CoinTag = "BTC", PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var builder = TransactionBuilder.Create(param); var jsonCoinbaseTransaction = File.OpenText(@"..\..\Resources\CoinbaseTransaction.json").ReadToEnd(); var trx = JsonSerializer.DeSerialize <DecodedRawTransaction>(jsonCoinbaseTransaction); var decoded = builder.Serializer.FromHex(trx.Hex); CompareTransactions(decoded, builder.Parse(trx)); }
public void Given_an_unsigned_transaction_When_serialized_to_hex_Then_is_valid() { var param = new CoinParameters { CoinTag = "BTC", PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var builder = TransactionBuilder.Create(param); var create = new CreateRawTransaction() .AddInput("9e779dbc84fe9c4082bc6fff332ddee81a6f56908720f25cb83ea9441cfa801b", 0) .AddOutput("19vrUuKgwKvAhiXfwVBaeHDQo7wMJg4VjD", 0.00010000M) .AddOutput("1KC4ZkESbdjYVfdYWuAkmSscQfD1VEWb32", 0.00030000M); var trx = builder.CreatePubKeyHashTransaction(param, create); var unsigned = builder.Serializer.ToHex(trx); Assert.AreEqual("01000000011b80fa1c44a93eb85cf2208790566f1ae8de2d33ff6fbc82409cfe84bc9d779e0000000000ffffffff0210270000000000001976a91461f05b61eb47be1d96e417272d3d3d3c346e2e9188ac30750000000000001976a914c789d0b3ec2bee3b2e057e2b759d1bd249095cd888ac00000000", unsigned); }
/// <summary> /// Create a bitcoin address from an imported string. /// </summary> public static Address Create(CoinParameters coinParameters, string address) { var decoded = Base58Encoding.DecodeWithCheckSum(address); var version = decoded.First(); var hash = decoded.Skip(1).ToArray(); Thrower.If(hash.Length != Length).Throw <AddressException>("Invalid length? expected={0} found={1}", Length, hash.Length); Thrower.If( version != coinParameters.PublicKeyAddressVersion && version != coinParameters.ScriptAddressVersion) .Throw <AddressException>("Mismatched version number, trying to cross networks? expected={0},{1} found={2}", coinParameters.PublicKeyAddressVersion, coinParameters.ScriptAddressVersion, version); return(new Address { Hash160 = hash, CoinParameters = coinParameters, AddressVersion = version }); }
public void Given_an_unsigned_transaction_with_multipul_inputs_When_serialized_to_hex_Then_is_valid() { var param = new CoinParameters { CoinTag = "BTC", PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var builder = TransactionBuilder.Create(param); var create = new CreateRawTransaction() .AddInput("2a99b070f457c8f2f6cb0f818b951c2038759af7abd98e57aecaf73989cd3474", 1) .AddInput("2a99b070f457c8f2f6cb0f818b951c2038759af7abd98e57aecaf73989cd3474", 0) .AddOutput("19vrUuKgwKvAhiXfwVBaeHDQo7wMJg4VjD", 0.00020000M) .AddOutput("1227pgTWYNsPNVN647NzsceFeXeLt18fTT", 0.00005000M); var trx = builder.CreatePubKeyHashTransaction(param, create); var unsigned = builder.Serializer.ToHex(trx); Assert.AreEqual("01000000027434cd8939f7caae578ed9abf79a7538201c958b810fcbf6f2c857f470b0992a0100000000ffffffff7434cd8939f7caae578ed9abf79a7538201c958b810fcbf6f2c857f470b0992a0000000000ffffffff02204e0000000000001976a91461f05b61eb47be1d96e417272d3d3d3c346e2e9188ac88130000000000001976a9140b2e3c8cfae581f5fa526070a705e617d3c61be488ac00000000", unsigned); }
/// <summary> /// Import the private key to a wallet import format. /// </summary> /// <param name="coinParameters"> /// The coin network to import to. /// </param> public string ToWifKey(CoinParameters coinParameters) { Guard.Require(coinParameters.PrivateKeyVersion < 256 && coinParameters.PrivateKeyVersion >= 0, "PrivateKeyVersion"); Guard.Require(this.HasPrivateKey, "HasPrivateKey"); var key = this.Bytes; var version = new[] { (byte)coinParameters.PrivateKeyVersion }.ToArray(); key = version.Concat(key).ToArray(); if (this.PrivateKey.Key.Compressed) { // compressed public keys so we append the zero to the private key key = key.Concat(CompressedBytes).ToArray(); } return(Base58Encoding.EncodeWithCheckSum(key)); }
/// <summary> /// </summary> /// <param name="test"> /// </param> private static void RunTest(TestVector test) { var constHash = "Bitcoin seed".Select(Convert.ToByte).ToArray(); var headers = new CoinParameters { PublicKeyAddressVersion = 0, PrivateKeyVersion = 128 }; var seed = CryptoUtil.ConvertHex(test.strHexMaster); var key = ExtendedKey.Create(constHash, seed); var pubkey = key.GetPublicKey(); foreach (var derive in test.vDerive) { var skey = key.Serialize(); var spubkey = pubkey.Serialize(); Guard.Require(skey == derive.prv); Guard.Require(spubkey == derive.pub); var pkey = ExtendedKey.Parse(derive.prv); var ppubkey = ExtendedKey.Parse(derive.pub); var wif1 = pkey.GetKey(1).PrivateKey.ToWifKey(headers); var wif2 = key.GetKey(1).PrivateKey.ToWifKey(headers); Guard.Require(wif1 == wif2); var addr1 = ppubkey.GetKey(1).PublicKey.ToAddress(headers); var addr2 = pubkey.GetKey(1).ToAddress(headers); Guard.Require(addr1.ToString() == addr2.ToString()); key = key.GetChild(derive.nChild); if ((derive.nChild & 0x80000000) == 0) { var pubkeyn = pubkey.GetChild(derive.nChild); pubkey = key.GetPublicKey(); Guard.Require(pubkey.Master.ToAddress(headers).ToString() == pubkeyn.Master.ToAddress(headers).ToString()); } else { pubkey = key.GetPublicKey(); } } }
public Address GetToAddress(CoinParameters param, bool forcePayToPubKey) { if (this.IsSentToAddress()) { return(Address.Create(param, this.GetPubKeyHash())); } else if (this.IsPayToScriptHash()) { return(Address.Create(param, this.GetPubKeyHash())); } else if (forcePayToPubKey && this.IsSentToRawPubKey()) { return(new BitcoinPublicKey(this.GetPubKey()).ToAddress(param)); } else { throw new ScriptException("Cannot cast this script to a pay-to-address type"); } }
/// <summary> /// This method does some final operation on the context /// </summary> /// <param name="coinParameters">The coinParameters.</param> /// <param name="previousRawTransactions">The transaction to modify.</param> private static void GridcoinHack(CoinParameters coinParameters, List <DecodedRawTransaction> previousRawTransactions) { if (coinParameters.CoinTag == "GRC") { //// for some reason the GRC client will not return the HEX of an output //// pars the raw transaction and populate the HEX from there //// this will be fixed in the next release of the Gridcoin client //// see git issue and pull request https://github.com/gridcoin/Gridcoin-Research/issues/86#issuecomment-218024574 previousRawTransactions.ForEach(trx => { var cloned = TransactionBuilder.Create(coinParameters).Serializer.FromHex(trx.Hex); trx.VOut.ForEach(output => { output.ScriptPubKey.Hex = CryptoUtil.ToHex(cloned.Outputs.ElementAt(output.N).ScriptBytes); }); }); } }
public override Transaction CreatePubKeyHashTransaction(CoinParameters parameters, CreateRawTransaction rawTransaction) { var transaction = base.CreatePubKeyHashTransaction(parameters, rawTransaction); var changePointer = transaction.Inputs.SingleOrDefault(p => p.Outpoint.Index == -1); if (changePointer == null) { return(transaction); } var rawChangeOutput = rawTransaction.Outputs.Single(p => p.Key == changePointer.Outpoint.Hash); var changeOutputScriptBytes = ScriptBuilder .CreateOutputScript(Address.Create(parameters, rawChangeOutput.Key)).GetProgram(); changePointer.Outpoint.Index = transaction.Outputs.Single(p => p.ScriptBytes == changeOutputScriptBytes).Index; changePointer.Outpoint.Hash = "0"; return(transaction); }
public void Given_an_unsigned_timestamped_transaction_with_multipul_inputs_When_serialized_to_hex_Then_is_valid() { var param = new CoinParameters { CoinTag = "PPC", PublicKeyAddressVersion = 55, PrivateKeyVersion = 183 }; var builder = TransactionBuilder.Create(param); var create = new CreateRawTransaction() .AddInput("c41e47d99557b2c25e72ee3c6bacf9796b09791cda5ea654225621701a2bc87d", 1) .AddInput("32b137d72a26681fa4ac1de3cab1cdcf05e0fd592b0a1cd81a3bd35000dfddf4", 1) .AddInput("69a3b97a4207eb7da766b9a29862acae00d3f3e00965d3b31f879abae1a58a50", 0) .AddOutput("PXCfHLaR2RCMnHsYCAtSoyjwBkhaniC9Ty", 1.10000000M) .AddOutput("PXDHGy2j11atyPoqyNZfccwS46gv6K4BkL", 20.82399100M) .AddOutput("PEBAEAM6R7SZkhQmvHa7eBfCNfTCE5CGYT", 1.40000000M) .AddOutput("PFj9YzkCRiihArX46KM9kZappnWa5svFhe", 2.53000000M); var trx = builder.CreatePubKeyHashTransaction(param, create); trx.Timestamp = 1461838729; // specific to this transaction var unsigned = builder.Serializer.ToHex(trx); Assert.AreEqual("0100000089e32157037dc82b1a7021562254a65eda1c79096b79f9ac6b3cee725ec2b25795d9471ec40100000000fffffffff4dddf0050d33b1ad81c0a2b59fde005cfcdb1cae31daca41f68262ad737b1320100000000ffffffff508aa5e1ba9a871fb3d36509e0f3d300aeac6298a2b966a77deb07427ab9a3690000000000ffffffff04e0c81000000000001976a914f808ce1d1a2c111581237f65931e8ae56d6cbfd088acb7bf3d01000000001976a914f826d9dfd3b0dfdacf09ea6cc7d48c9569ace1b288acc05c1500000000001976a9143d45ff4529a9326804aa1fe2800e55418c00604c88acd09a2600000000001976a9144e4adeb9f14731d0370b10cff700e12c36571b7e88ac00000000", unsigned); }
public void Given_a_signed_timestamped_transaction_with_multipul_inputs_When_serialized_to_hex_Then_is_valid() { var param = new CoinParameters { CoinTag = "PPC", PublicKeyAddressVersion = 55, PrivateKeyVersion = 183 }; var builder = TransactionBuilder.Create(param); var key1 = new BitcoinPrivateKey(param, "79rzXNEtHfWJQxPUHJpMLnuuLKD9NnJAFeYVYtus2JjyXcKkCQ1"); var key2 = new BitcoinPrivateKey(param, "7Ab1bFhaW5oC1LN7AgaHKr9ZZGumLSxThq8qMP3iP1oJYwAW2Kq"); var create = new CreateRawTransaction() .AddInput("c41e47d99557b2c25e72ee3c6bacf9796b09791cda5ea654225621701a2bc87d", 1) .AddInput("32b137d72a26681fa4ac1de3cab1cdcf05e0fd592b0a1cd81a3bd35000dfddf4", 1) .AddInput("69a3b97a4207eb7da766b9a29862acae00d3f3e00965d3b31f879abae1a58a50", 0) .AddOutput("PXCfHLaR2RCMnHsYCAtSoyjwBkhaniC9Ty", 1.10000000M) .AddOutput("PXDHGy2j11atyPoqyNZfccwS46gv6K4BkL", 20.82399100M) .AddOutput("PEBAEAM6R7SZkhQmvHa7eBfCNfTCE5CGYT", 1.40000000M) .AddOutput("PFj9YzkCRiihArX46KM9kZappnWa5svFhe", 2.53000000M); var trx = builder.CreatePubKeyHashTransaction(param, create); trx.Timestamp = 1461838729; // specific to this transaction var unsigned = builder.Serializer.ToHex(trx); var sign = builder.CreateSignature(unsigned) .AddKey(key1.ToWifKey(param)) .AddKey(key2.ToWifKey(param)) .AddInput("c41e47d99557b2c25e72ee3c6bacf9796b09791cda5ea654225621701a2bc87d", 1, "76a914f826d9dfd3b0dfdacf09ea6cc7d48c9569ace1b288ac") .AddInput("32b137d72a26681fa4ac1de3cab1cdcf05e0fd592b0a1cd81a3bd35000dfddf4", 1, "76a9144e4adeb9f14731d0370b10cff700e12c36571b7e88ac") .AddInput("69a3b97a4207eb7da766b9a29862acae00d3f3e00965d3b31f879abae1a58a50", 0, "76a9144e4adeb9f14731d0370b10cff700e12c36571b7e88ac"); trx = builder.Sign(param, sign); var signed = builder.Serializer.ToHex(trx); Assert.AreEqual("0100000089e32157037dc82b1a7021562254a65eda1c79096b79f9ac6b3cee725ec2b25795d9471ec4010000008a47304402200cf6263646697a3e90114ead2d461c56a26cb22c3758c1aa3bdd4d1e2d8461ba022048d1c17cc00ab21177a14bcac61fcc990969e1b855c1cdc9ede503af56d924410141042cfdbfaee53942b2c8d9c67631e099e604e6e4e01594394942f33fa237eca310ac0aa79189d96c79e9e764826243614ebdfd400680421dff54314d9eabb87daafffffffff4dddf0050d33b1ad81c0a2b59fde005cfcdb1cae31daca41f68262ad737b132010000008b483045022100c867962f5c584897e5231e45d06065e4bc9dbf26ea4bcbc6de3ceebe87791ade02205c8b016d84a38a25275d89da8f6435016313e1d108f15638609e791570fbd8a10141040c65817203cc55de4c2ab32413497fd9e917a5c1d4711e7772a7277f491d3dc50879852e697a2551547c5d8e28a0a97280e8e63d4590a9c722ce2a84c6d71f3affffffff508aa5e1ba9a871fb3d36509e0f3d300aeac6298a2b966a77deb07427ab9a369000000008b4830450221008d55d7eb528622f4d803c0756f11e49fd6b3206789d6603e9f3221a3473cda18022002663afafc52cc4c3a5bc9c16401d13a853f2e0b4b258c923ec3c18e85aeab100141040c65817203cc55de4c2ab32413497fd9e917a5c1d4711e7772a7277f491d3dc50879852e697a2551547c5d8e28a0a97280e8e63d4590a9c722ce2a84c6d71f3affffffff04e0c81000000000001976a914f808ce1d1a2c111581237f65931e8ae56d6cbfd088acb7bf3d01000000001976a914f826d9dfd3b0dfdacf09ea6cc7d48c9569ace1b288acc05c1500000000001976a9143d45ff4529a9326804aa1fe2800e55418c00604c88acd09a2600000000001976a9144e4adeb9f14731d0370b10cff700e12c36571b7e88ac00000000", signed); }
public CoinService() { _rpcConnector = new RpcConnector(this); Parameters = new CoinParameters(this, null, null, null, null, 0); }
/// <summary> /// Initializes a new instance of the <see cref="TransactionSigner"/> class. /// </summary> public TransactionSigner(CoinParameters coinParameters, TransactionSerializer serializer) { this.CoinParameters = coinParameters; this.Serializer = serializer; }
/// <summary> /// Create a transaction builder for a given coin. /// Some other Blockchain coins have different serializes of network parameters, /// this is calculated when creating the builder. /// </summary> public static TransactionBuilder Create(CoinParameters param) { AddressUtil.PopulateCoinParameters(param); // todo: move factory selector in a configuration file var lst = new List <string> { "BTC", "TRC", "GRC", "DOGE", "DASH", "RDD", "XPM", "LTC", "NMC", "QRK", "PPC", "MTR", "GB", "SHM", "CRX", "UBIQ", "ARG", "ZYD", "DLC", "STRAT", "SH" }; if (lst.Contains(param.CoinTag)) { var ser = new TransactionSerializer(param); // coin scale can be found in util.h (static const int64_t COIN = 100000000) param.CoinScale = 100000000; param.TransactionVersion = 1; if (param.CoinTag == "QRK") { param.CoinScale = 100000; } if (param.CoinTag == "PPC") { param.CoinScale = 1000000; ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "GRC") { ser = new TransactionSerializerGridcoin(param); } if (param.CoinTag == "RDD") { param.TransactionVersion = 2; ser = new TransactionSerializerReddcoin(param); } if (param.CoinTag == "MTR") { ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "GB") { ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "SHM") { ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "CRX") { ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "UBIQ") { ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "STRAT") { ser = new TransactionSerializerTimeStamped(param); } if (param.CoinTag == "DST") { ser = new TransactionSerializerTimeStamped(param); return(new DeStreamTransactionBuilder(param, ser, new TransactionSigner(param, ser))); } var builder = new TransactionBuilder(param, ser, new TransactionSigner(param, ser)); return(builder); } return(new TransactionBuilderClient()); }
protected TransactionBuilder(CoinParameters param, TransactionSerializer serializer, TransactionSigner signer) { this.CoinParameters = param; this.Serializer = serializer; this.Signer = signer; }
public DeStreamTransactionBuilder(CoinParameters param, TransactionSerializer serializer, TransactionSigner signer) : base(param, serializer, signer) { }
public CoinService(string daemonUrl, string rpcUsername, string rpcPassword, string walletPassword) { _rpcConnector = new RpcConnector(this); Parameters = new CoinParameters(this, daemonUrl, rpcUsername, rpcPassword, walletPassword, 0); }
// this provides support for cases where *.config files are not an option public CoinService(String daemonUrl, String rpcUsername, String rpcPassword, String walletPassword, Int16 rpcRequestTimeoutInSeconds) { _rpcConnector = new RpcConnector(this); Parameters = new CoinParameters(this, daemonUrl, rpcUsername, rpcPassword, walletPassword, rpcRequestTimeoutInSeconds); }
/// <summary> /// The create. /// </summary> /// <param name="headers"> /// The headers. /// </param> /// <returns> /// The <see cref="ReceiveInfo"/>. /// </returns> public BitcoinPrivateKey GetKey(CoinParameters headers) { return(new BitcoinPrivateKey(headers, this.PrivateKey)); }
/// <summary> /// Create a transaction. /// This allows to create a transaction using the private keys, /// This means we don't need to open the wallet to send the transaction and all the handling of open wallet synchronization is not required. /// </summary> public static async Task <TransactionContext> CreateAsync(CoinParameters param, IBitcoinClient client, IEnumerable <TransactionInfo> items) { if (string.Equals(param.CoinTag, "DST")) { return(await CreateDeStreamAsync(param, client, items)); } var context = new TransactionContext { CoinParameters = param, PreviousRawTransactions = new List <DecodedRawTransaction>(), CreateRawTransaction = new CreateRawTransaction(), SendItems = new List <TransactionInfo>() }; foreach (var item in items) { context.SendItems.Add(item); // check if receiving address is already in the transaction, this is allowed but not supported by the client. if (item.SpendToAddresses.Select(s => s.PublicKey) .Intersect(item.SpendFromAddresses.Select(s => s.PublicKey)).Any()) { item.Fail(false, FailedReason.CannotSendToSelfUnlessChange); continue; } if (item.SpendFromAddresses.SelectMany(s => s.Transactions).None()) { item.Fail(false, FailedReason.NoTransactionsFound); continue; } // check if a transaction is already in use if (context.CreateRawTransaction.Inputs. Any(source => item.SpendFromAddresses.SelectMany(s => s.Transactions) .Any(target => source.TransactionId == target.Hash && source.Output == target.Index))) { item.Fail(true, FailedReason.TransactionInputAlreadyInUse); continue; } // check if receiving address is already in the transaction, this is allowed but not supported by the client. if (context.CreateRawTransaction.Outputs .Any(source => item.SpendToAddresses.Any(target => source.Key == target.PublicKey))) { item.Fail(true, FailedReason.ReceiveAddressAlreadyInOutput); continue; } if (item.RequestedFee <= 0) { item.Fail(false, FailedReason.FeeCannotBeZero); continue; } ////try ////{ //// var rawTrxListAsync = item.SpendFromAddresses //// .SelectMany(s => s.Transactions) //// .DistinctBy(t => t.Hash) //// .Where(a => context.PreviousRawTransactions.None(b => b.TxId == a.Hash)) //// .Select(async trx => await client.GetRawTransactionAsync(trx.Hash, 1)); //// var previousRawTransactions = (await Task.WhenAll(rawTrxListAsync)).ToList(); //// context.PreviousRawTransactions.AddRange(previousRawTransactions); //// // the Gridcoin hack //// GridcoinHack(context.CoinParameters, previousRawTransactions); ////} ////catch (Exception) ////{ //// item.Fail(false, FailedReason.FailedReadingTransactionsFromClient); //// continue; ////} // validate all the transaction outpoints have the ScriptPubKey Hex ////item.SpendFromAddresses.ForEach(spend => //// spend.Transactions.ForEach(strx => //// { //// var output = context.PreviousRawTransactions //// .Where(t => t.TxId == strx.Hash) //// .SelectMany(s => s.VOut) //// .SingleOrDefault(v => v.N == strx.Index); //// if (output.IsNotNull() && output.Value == strx.Value) //// { //// if (output.ScriptPubKey.IsNotNull()) //// { //// if (output.ScriptPubKey.Addresses.Any(addr => addr == spend.PublicKey)) //// { //// strx.ScriptPubKeyHex = output.ScriptPubKey.Hex; //// } //// } //// } //// })); if (item.SpendFromAddresses.SelectMany(s => s.Transactions).Any(t => t.ScriptPubKeyHex.IsNullOrEmpty())) { item.Fail(false, FailedReason.ScriptPubKeyHexNotFound); continue; } //// get the outputs associated with the sender address var spendToEnumerated = item.SpendToAddresses.ToList(); var spendFromEnumerated = item.SpendFromAddresses.SelectMany(s => s.Transactions).ToList(); ////var outputs = previousRawTransactions //// .ToDictionary(rawtrx => rawtrx.TxId, rawtrx => rawtrx.VOut //// .Where(vout => vout.ScriptPubKey.Addresses //// .Any(add => item.SpendFromAddresses.Any(a => a.PublicKey == add)))); //// calculate the sum of inputs and outputs. var inputSum = spendFromEnumerated.Select(t => t.Value).Sum(); var outputSum = spendToEnumerated.Select(spt => spt.Amount).Sum(); var change = inputSum - outputSum; if (change < 0) { item.Fail(false, FailedReason.InvalidSum); continue; } if (item.SpendToAddresses.Any(s => s.TakeFee)) { //// take the fee from a receiver address. var receiver = item.SpendToAddresses.First(s => s.TakeFee); if (receiver.Amount < item.RequestedFee) { item.Fail(false, FailedReason.InsufficientFeeOnReceiver); continue; } receiver.Amount -= item.RequestedFee; } else { //// take the fee from the sender if (change < item.RequestedFee) { item.Fail(false, FailedReason.InsufficientFeeOnSender); continue; } change -= item.RequestedFee; } if (change > 0) { if (item.ChangeAddress.IsNullOrEmpty()) { item.Fail(false, FailedReason.NoChangeAddressFound); continue; } } // try to calculate the fee if trx is too big the default fee may not be enough // http://bitcoin.stackexchange.com/questions/7537/calculator-for-estimated-tx-fees // https://en.bitcoin.it/wiki/Transaction_fees // http://bitcoin.stackexchange.com/questions/3400/what-is-the-exact-formula-for-calculating-transaction-fees // http://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending // formula fee = [Normal fee * (500 KB) / (500KB - Block size)] - [Normal fee / (1 - Block size / 500KB)] // trx size = [in*148 + out*34 + 10 plus or minus 'in'] //// TODO: move this code to a facotry to allow calculation of fee per coin //// coins have different block sizes sand this needs to be acocunted for as well var ins = spendFromEnumerated.Count(); var outs = spendToEnumerated.Count(); var sizeByte = Convert.ToDecimal((ins * 148) + (outs * 34) + 10 + (ins / 2)); // we'll calculate roughly the coin fee times size of transaction const decimal AvgSizeBytes = 1000; if (sizeByte >= AvgSizeBytes) { var feeRatio = sizeByte / AvgSizeBytes; var expectedFee = feeRatio * item.CoinFee; if (expectedFee > item.RequestedFee) { item.Fail(false, FailedReason.InsufficientFeeForTransactionSize); continue; } } spendFromEnumerated.ForEach(vout => context.CreateRawTransaction.AddInput(vout.Hash, (int)vout.Index)); spendToEnumerated.ForEach(spt => context.CreateRawTransaction.AddOutput(spt.PublicKey, spt.Amount)); if (change > 0) { context.CreateRawTransaction.AddOutput(item.ChangeAddress, change); } } if (context.SendItems.All(w => w.Failed)) { return(context); } //// create the builder and build the transaction (either a client using the coins client or locally if supported). await TransactionBuilder.Create(param).Build(client, context); // seems we are done here return(context); }
private static async Task <TransactionContext> CreateDeStreamAsync(CoinParameters param, IBitcoinClient client, IEnumerable <TransactionInfo> items) { const decimal feeRate = (decimal)0.0077; var context = new TransactionContext { CoinParameters = param, PreviousRawTransactions = new List <DecodedRawTransaction>(), CreateRawTransaction = new CreateRawTransaction(), SendItems = new List <TransactionInfo>() }; foreach (var item in items) { context.SendItems.Add(item); // check if receiving address is already in the transaction, this is allowed but not supported by the client. if (item.SpendToAddresses.Select(s => s.PublicKey) .Intersect(item.SpendFromAddresses.Select(s => s.PublicKey)).Any()) { item.Fail(false, FailedReason.CannotSendToSelfUnlessChange); continue; } if (item.SpendFromAddresses.SelectMany(s => s.Transactions).None()) { item.Fail(false, FailedReason.NoTransactionsFound); continue; } // check if a transaction is already in use if (context.CreateRawTransaction.Inputs.Any(source => item.SpendFromAddresses .SelectMany(s => s.Transactions) .Any(target => source.TransactionId == target.Hash && source.Output == target.Index))) { item.Fail(true, FailedReason.TransactionInputAlreadyInUse); continue; } // check if receiving address is already in the transaction, this is allowed but not supported by the client. if (context.CreateRawTransaction.Outputs .Any(source => item.SpendToAddresses.Any(target => source.Key == target.PublicKey))) { item.Fail(true, FailedReason.ReceiveAddressAlreadyInOutput); continue; } if (item.SpendFromAddresses.SelectMany(s => s.Transactions).Any(t => t.ScriptPubKeyHex.IsNullOrEmpty())) { item.Fail(false, FailedReason.ScriptPubKeyHexNotFound); continue; } //// get the outputs associated with the sender address var spendToEnumerated = item.SpendToAddresses.ToList(); var spendFromEnumerated = item.SpendFromAddresses.SelectMany(s => s.Transactions).ToList(); //// calculate the sum of inputs and outputs. var inputSum = spendFromEnumerated.Select(t => t.Value).Sum(); var outputSum = spendToEnumerated.Select(spt => spt.Amount).Sum(); var change = inputSum - outputSum; if (change < 0) { item.Fail(false, FailedReason.InvalidSum); continue; } if (item.SpendToAddresses.Any(s => s.TakeFee)) { item.SpendToAddresses.First(s => s.TakeFee).Amount /= decimal.One + feeRate; } else { change -= outputSum * feeRate; } if (change > 0) { if (item.ChangeAddress.IsNullOrEmpty()) { item.Fail(false, FailedReason.NoChangeAddressFound); continue; } } spendFromEnumerated.ForEach(vout => context.CreateRawTransaction.AddInput(vout.Hash, (int)vout.Index)); spendToEnumerated.ForEach(spt => context.CreateRawTransaction.AddOutput(spt.PublicKey, spt.Amount)); if (change > 0) { context.CreateRawTransaction.AddOutput(item.ChangeAddress, change); context.CreateRawTransaction.AddInput(item.ChangeAddress, -1); } } if (context.SendItems.All(w => w.Failed)) { return(context); } //// create the builder and build the transaction (either a client using the coins client or locally if supported). await TransactionBuilder.Create(param).Build(client, context); // seems we are done here return(context); }
public CoinService() { _rpcConnector = new RpcConnector(this); Parameters = new CoinParameters(this); }
public CoinService(string daemonUrl, string rpcUsername, string rpcPassword, string walletPassword) { Parameters = new CoinParameters(this, daemonUrl, rpcUsername, rpcPassword, walletPassword, 0); _rpcConnector = new RpcConnector(this); }
// this provides support for cases where *.config files are not an option public CoinService(string daemonUrl, string rpcUsername, string rpcPassword, string walletPassword, short rpcRequestTimeoutInSeconds) { Parameters = new CoinParameters(this, daemonUrl, rpcUsername, rpcPassword, walletPassword, rpcRequestTimeoutInSeconds); _rpcConnector = new RpcConnector(this); }
// this provides support for cases where *.config files are not an option public CoinService(string daemonUrl, string rpcUsername, string rpcPassword, string walletPassword, short rpcRequestTimeoutInSeconds) { _rpcConnector = new RpcConnector(this); Parameters = new CoinParameters(this, daemonUrl, rpcUsername, rpcPassword, walletPassword, rpcRequestTimeoutInSeconds); }