//https://github.com/libbitcoin/libbitcoin/blob/master/test/stealth.cpp public void BitFieldCorrectlyMatchData() { var bit = (uint)1358086238; var o = new BitField(bit, 32); var tests = new[] { new { Encoded = (uint)0x691cf48b, BitCount = 27, Data = "8bf42c79", Match = false }, new { Encoded = (uint)0x691cf48b, BitCount = 27, //Data miss match (1 instead of 2) Data = "8bf41c79", Match = true } }; foreach(var test in tests) { BitField field = new BitField(test.Encoded, test.BitCount); Assert.Equal(test.Match, field.Match(Utils.ToUInt32(TestUtils.ParseHex(test.Data), true))); } }
public void CanVerifySignature() { var tests = new[] { new { Address = "15jZVzLc9cXz5PUFFda5A4Z7kZDYPg2NnL", PrivateKey = "L3TiCqmvPkXJpzCCZJuhy6wQtJZWDkR1AuqFY4Utib5J5XLuvLdZ", Message = "This is an example of a signed message.", Signature = "H6sliOnVrD9r+J8boZAKHZwBIW2zLiD72IfTIF94bfZhBI0JdMu9AM9rrF7P6eH+866YvM4H9xWGVN4jMJZycFU=" }, new { Address = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ", PrivateKey = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj", Message = "hello world", Signature = "G+dnSEywl3v1ijlWXvpY6zpu+AKNNXJcVmrdE35m0mMlzwFzXDiNg+uZrG9k8mpQL6sjHKrlBoDNSA+yaPW7PEA=" }, new { Address = "1Q1wVsNNiUo68caU7BfyFFQ8fVBqxC2DSc", PrivateKey = null as string, Message = "Localbitcoins.com will change the world", Signature = "IJ/17TjGGUqmEppAliYBUesKHoHzfY4gR4DW0Yg7QzrHUB5FwX1uTJ/H21CF8ncY8HHNB5/lh8kPAOeD5QxV8Xc=" }, new { Address = "1GvPJp7H8UYsYDvE4GFoV4f2gSCNZzGF48", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "This is an example of a signed message2", Signature = "G8YNwlo+I36Ct+hZKGSBFl3q8Kbx1pxPpwQmwdsG85io76+DUOHXqh/DfBq+Cn2R3C3dI//g3koSjxy7yNxJ9m8=" }, new { Address = "1GvPJp7H8UYsYDvE4GFoV4f2gSCNZzGF48", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "this is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long message", Signature = "HFKBHewleUsotk6fWG0OvWS/E2pP4o5hixdD6ui60in/x4376FBI4DvtJYrljXLNJTG1pBOZG+qRT/7S9WiIBfQ=" }, }; foreach(var test in tests) { if(test.PrivateKey != null) { var secret = Network.Main.CreateBitcoinSecret(test.PrivateKey); var signature = secret.PrivateKey.SignMessage(test.Message); Assert.True(((BitcoinPubKeyAddress)Network.Main.CreateBitcoinAddress(test.Address)).VerifyMessage(test.Message, signature)); Assert.True(secret.PubKey.VerifyMessage(test.Message, signature)); } BitcoinPubKeyAddress address = (BitcoinPubKeyAddress)Network.Main.CreateBitcoinAddress(test.Address); Assert.True(address.VerifyMessage(test.Message, test.Signature)); Assert.True(!address.VerifyMessage("bad message", test.Signature)); } }
//https://wiki.unsystem.net/index.php/DarkWallet/Stealth#Bitfield_value public void BitFieldCorrectlyInterpreted() { var tests = new[] { new { Encoded = (uint)1763505291, BitCount = 32, Raw = "8bf41c69" }, new { Encoded = (uint)1, BitCount = 8, Raw = "0100" }, new { Encoded = (uint)1, BitCount = 7, Raw = "01" } }; foreach(var test in tests) { var bitField = new BitField(test.Encoded, test.BitCount); Assert.Equal(TestUtils.ParseHex(test.Raw), bitField.GetRawForm()); Assert.Equal(test.Encoded, bitField.GetEncodedForm()); bitField = new BitField(TestUtils.ParseHex(test.Raw), test.BitCount); Assert.Equal(test.Encoded, bitField.GetEncodedForm()); Assert.Equal(TestUtils.ParseHex(test.Raw), bitField.GetRawForm()); } }
//https://github.com/libbitcoin/libbitcoin/blob/master/test/stealth.cpp public void BitFieldCanFetchTransaction() { var tests = new[] { new { Encoded = "deadbeef", BitCount = 5, Transaction = "0100000001bd365d65c6eeee2d3ae29c6e1b0fb22f70f0135220eac1273c92a13b6039ecdf000000006b48304502202551626a52a088ea585f2aaa8afe1c1b7bc52ea8e577e149f20f0f5f2fc54485022100913643c6a1e54b5284d1c108519d9243213373256ff95ab6c7e3d162175c9e28012102881c1427e826f230246197f7f693f1c923ad2afea1dad901d1c870f7e310c895ffffffff020000000000000000286a2606ab3099ae0359222b4a2dd9a21ebd70331794fb05b0bd605bd9660fdf895906b1a227edcbec306c3402000000001976a9142f62432f367dc3cf67e68f814cc2a3ed5b2e8cec88ac00000000", } ,new { Encoded = "deadbeef", BitCount = 5, Transaction = "01000000028d8d968818e464db9dc876eb4edd59e62913d849603ba10afe033159b55652dc010000006b483045022100992e7ce4516cdabffae823557f5854c6ca7a424cc2fe7a9b94f439ecca839f84022025b871cf499f7b0eebcf751a83e7ffe9473b6c5227f95d9989ef8e98937112760121038795ff1d07ef7092e03d612f187d1281705b36d0c11ecf4b11167beb7efc3437ffffffff723372d920d8eaa6bd1fe6ec5adaf913b6d8a48655acded49a3f09191cdf9f3a010000006a47304402206f6ca054242fe5b410d743ae238049799eccdd88637d555ad48d8da1a854bd0802201b13217d0a6a7cf3467074aa8964f3f365541304142503d91813baa0117d1a8101210258cedc6e590493838c612a394d53ffbb283638f9d1d49e1c571b15a885f405d9ffffffff05f0874b00000000001976a914cd08dcd94ba88488b4c02c82fc11fc63fee4e30c88acc0e1e400000000001976a9148784cbfe99e86135d40d276c58fcbbf37af1b48e88ac0000000000000000286a260689d0267d021cc402cf764f2c88138028b6d5c0f06d5caa34edffc1d2286048f0162756ebf1f0874b00000000001976a914923d77bbdd3d29dd4844a51629514bac9e8a2c4d88aca30c0000000000001976a9143aaa9db7a3fbc5dcc9d121a157bf63aaaee9d8f588ac00000000", }, new { Encoded = "deadbeef", BitCount = 5, Transaction = "010000000288b6952f8357b5a8271f6c0608ea779549bf0a9ae72213ce433f7a9c90190be6000000006b4830450221008b1b7369d7bf8a1dd99e6a260ef969840d633ba81fc379bf4f5469afd3b288e2022077e21dd12c1d8b2a4ab2e58fb0113b9e3bb77db4439483f7a8311cac3ad6ce9a012102b0c2eb0cc505a4c9fb62df8a7fcdcc00ddab43ff5752ebc51a0dae11fbfb0648ffffffff4ca2f8960e17b56e690739158994ed23427ffc08445ba3223edacb322db12d4b000000006b483045022100f94d50d846d85a545693574f9e1a680a858f085257c827cd8b211df8d27c558202201bca0407ad67960937999a88c95e52417442703a16036bbe55f071cdff804f69012102884d0c845bff3158e88033bce236cdbfeadba0a7e7381258379612f355681d6dffffffff052f750000000000001976a914a34a179dc97cbb84992d2960ee240b56f1b2ae3088acbca15400000000001976a914139e314f94a8e4fc2864ef2544e7640ba033725b88ac0000000000000000286a26061d6b629c024c7daf682da4afd61aadf401fae316a265c3244d912b375a75b55551d52a583f2f750000000000001976a9142888cb9f0b0f489106bc13f56fdd2ceec1b6883788ac51c30000000000001976a91404316e7db781481d1419feda2c8d7fa9f7b68a6e88ac00000000", }, }; foreach(var test in tests) { var field = new BitField(TestUtils.ParseHex(test.Encoded), test.BitCount); Transaction transaction = new Transaction(); transaction.FromBytes(TestUtils.ParseHex(test.Transaction)); var stealthOutput = field.GetPayments(transaction).FirstOrDefault(); Assert.NotNull(stealthOutput); Assert.True(field.Match(stealthOutput.Metadata.BitField)); } }
public void CanCreatePayment() { var tests = new[] { new CanCreatePaymentData { //sx stealth-newkey StealthAddress = "vJmtjxSDxNPXL4RNapp9ARdqKz3uJyf1EDGjr1Fgqs9c8mYsVH82h8wvnA4i5rtJ57mr3kor1EVJrd4e5upACJd588xe52yXtzumxj", ScanSecret = "3e49e7257cb31db997edb1cf8299af0f37e2663e2260e4b8033e49d39a6d02f2", ScanPubKey = "025e58a31122b38c86abc119b9379fe247410aee87a533f9c07b189aef6c3c1f52", SpendSecret = "aa3db0cfb3edc94de4d10f873f8190843f2a17484f6021a95a7742302c744748", SpendPubKey = "03616562c98e7d7b74be409a787cec3a912122f3fb331a9bee9b0b73ce7b9f50af", //sx newkey | sx wif-to-secret EphemSecret = "9e63abaf8dcd5ea3919e6de0b6c544e00bf51bf92496113a01d6e369944dc091", EphemPubKey = "03403d306ec35238384c7e340393335f9bc9bb4a2e574eb4e419452c4ea19f14b0", //sx steatlh-uncover-secret [EphemPubKey] [ScanSecret] [SpendSecret] StealthSecret = "4e422fb1e5e1db6c1f6ab32a7706d368ceb385e7fab098e633c5c5949c3b97cd", //sx stealth-initiate [EphemSecret] [ScanPubKey] [SpendPubKey] (for sender) //or //sx stealth-uncover [EphemPubKey] [ScanSecret] [SpendPubKey] (for receiver) StealthPubKey = "02726112ad39cb6bf848b1b1ef30b88e35286bf99f746c2be575f96c0e02a9357c", }, //Need padding for to find the stealth secret new CanCreatePaymentData{ StealthAddress = "vJmyTEybwCKz7W8y6vP62jo7RoyfLneiANcPLBBNYwn98EXzQRStMKqKGRiZhqscuQ6WKy2J3U3zfx72V3b2J6YvxxBcxUj4XMDsw7", ScanSecret = "2f517d81cf30e47dbf4809321275bbfd92192af81a6141a17aa53e40bd28fe36", ScanPubKey = "039d91ae0eebea6dc500fb57b704abce3d3fa700cc762a52bc5dcaee27770a8402", SpendSecret = "71e33219884fc27011f8da9adcc730f0c2e940759bdb1b615764492bce04fcea", SpendPubKey = "021a3d5b40ec83fc58b5a23207eb9c99b741d8f0e9f8b80f04f49cec915b540c40", EphemSecret = "578ffe42c0fbfb324a31f41dbbcd8b1f910ce2f4d803444a83b18ae9f8ccd97e", EphemPubKey = "03c190be0a1c6e50577b3dd637b1fff9344de31c2544ff3d815535c0515711150f", StealthSecret = "006d138b4bcef0f09c8784c0cc68f2be4497a1a822d8d7b0519c5c0378b5cb45", StealthPubKey = "0223a99278a5279ea93718503a42377067e72960eb808d8bff6defdd95d4feff76" } }; foreach(var test in tests) { var scan = AssertKeys(test.ScanSecret, test.ScanPubKey); var spend = AssertKeys(test.SpendSecret, test.SpendPubKey); var ephem = AssertKeys(test.EphemSecret, test.EphemPubKey); var stealth = AssertKeys(test.StealthSecret, test.StealthPubKey); var address = spend.PubKey.CreateStealthAddress(scan.PubKey, Network.Main); Assert.Equal(test.StealthAddress, address.ToString()); //Try roundtrip address = new BitcoinStealthAddress(address.ToBytes(), Network.Main); Assert.Equal(test.StealthAddress, address.ToString()); var payment = address.CreatePayment(ephem); var generatedKey = spend.Uncover(scan, payment.Metadata.EphemKey); if(stealth != null) { Assert.Equal(stealth.PubKey.Hash, payment.StealthKeys[0].ID); Assert.Equal(stealth.ToBytes(), generatedKey.ToBytes()); } var uncoveredSender = spend.PubKey.UncoverSender(ephem, scan.PubKey); var uncovertedReceiver = spend.PubKey.UncoverReceiver(scan, ephem.PubKey); AssertEx.CollectionEquals(uncoveredSender.ToBytes(), uncovertedReceiver.ToBytes()); AssertEx.CollectionEquals(generatedKey.PubKey.ToBytes(), uncovertedReceiver.ToBytes()); var transaction = new Transaction(); payment.AddToTransaction(transaction, 100); } }
public void CanParseStealthAddress() { var tests = new[] { //Test vector created with sx //sx stealth-newkey -> ScanSecret,SpendSecret,StealthAddress //sx stealth-show-addr StealthAddress -> ScanPubKey,SpendPubKey,RequiredSignature... new { ScanSecret = "9ac9fdee7c2c19611bcbed8959e1c61d00cdc27cf17bb50a1f4d29db7f953632", SpendSecrets = new[]{ "4e2fa767cc241c3fa4c512d572b2758a3960a06d374f2c819fe409b161d72ad4" }, StealthAddress = "vJmsmwE8cVt9ytJxBuY2jayh8RAfvpG42CyNVYpeVZAkHaiwASobUEzskpXMwbH1TZNBLoxWWYem5WuZewTL8xz3upJ75zKcdVmTfg", ScanPubKey = "021ce89be99a229d123e8bc13ffbcb66722d6200bbeb1d90ddddbf97df82ed2672", SpendPubKeys = new[] { "03c197525241d3d70bbf33bb2b54d41e6b9595a92a2c6b7bf7157727c017f0154a" }, RequiredSignature = 1, Options = 0, PrefixLength = 0, PrefixValue = "", } }; foreach(var test in tests) { var scanSecret = new Key(TestUtils.ParseHex(test.ScanSecret)); AssertEx.CollectionEquals(scanSecret.PubKey.ToBytes(), TestUtils.ParseHex(test.ScanPubKey)); var stealth = new BitcoinStealthAddress(test.StealthAddress, Network.Main); Assert.Equal(test.RequiredSignature, stealth.SignatureCount); Assert.Equal(test.PrefixLength, stealth.Prefix.BitCount); AssertEx.CollectionEquals(stealth.Prefix.GetRawForm(), TestUtils.ParseHex(test.PrefixValue)); Assert.Equal(test.Options, stealth.Options); AssertEx.CollectionEquals(stealth.ScanPubKey.ToBytes(), TestUtils.ParseHex(test.ScanPubKey)); for(int i = 0 ; i < test.SpendPubKeys.Length ; i++) { AssertEx.CollectionEquals(stealth.SpendPubKeys[i].ToBytes(), TestUtils.ParseHex(test.SpendPubKeys[i])); var spendSecret = new Key(TestUtils.ParseHex(test.SpendSecrets[i])); AssertEx.CollectionEquals(stealth.SpendPubKeys[i].ToBytes(), TestUtils.ParseHex(test.SpendPubKeys[i])); } } }
public void script_combineSigs() { Key[] keys = new[] { new Key(), new Key(), new Key() }; var txFrom = CreateCreditingTransaction(keys[0].PubKey.Hash.ScriptPubKey); var txTo = CreateSpendingTransaction(new Script(), txFrom); Script scriptPubKey = txFrom.Outputs[0].ScriptPubKey; Script scriptSig = txTo.Inputs[0].ScriptSig; Script empty = new Script(); Script combined = Script.CombineSignatures(scriptPubKey, txTo, 0, empty, empty); Assert.True(combined.ToBytes().Length == 0); // Single signature case: SignSignature(keys, txFrom, txTo, 0); // changes scriptSig scriptSig = txTo.Inputs[0].ScriptSig; combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); Assert.True(combined == scriptSig); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); Assert.True(combined == scriptSig); Script scriptSigCopy = scriptSig.Clone(); // Signing again will give a different, valid signature: SignSignature(keys, txFrom, txTo, 0); scriptSig = txTo.Inputs[0].ScriptSig; combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); Assert.True(combined == scriptSigCopy || combined == scriptSig); // P2SH, single-signature case: Script pkSingle = PayToPubkeyTemplate.Instance.GenerateScriptPubKey(keys[0].PubKey); scriptPubKey = pkSingle.Hash.ScriptPubKey; txFrom.Outputs[0].ScriptPubKey = scriptPubKey; txTo.Inputs[0].PrevOut = new OutPoint(txFrom, 0); SignSignature(keys, txFrom, txTo, 0, pkSingle); scriptSig = txTo.Inputs[0].ScriptSig; combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); Assert.True(combined == scriptSig); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); scriptSig = txTo.Inputs[0].ScriptSig; Assert.True(combined == scriptSig); scriptSigCopy = scriptSig.Clone(); SignSignature(keys, txFrom, txTo, 0); scriptSig = txTo.Inputs[0].ScriptSig; combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); Assert.True(combined == scriptSigCopy || combined == scriptSig); // dummy scriptSigCopy with placeholder, should always choose non-placeholder: scriptSigCopy = new Script(OpcodeType.OP_0, Op.GetPushOp(pkSingle.ToBytes())); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); Assert.True(combined == scriptSig); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSig, scriptSigCopy); Assert.True(combined == scriptSig); // Hardest case: Multisig 2-of-3 scriptPubKey = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, keys.Select(k => k.PubKey).ToArray()); txFrom.Outputs[0].ScriptPubKey = scriptPubKey; txTo.Inputs[0].PrevOut = new OutPoint(txFrom, 0); SignSignature(keys, txFrom, txTo, 0); scriptSig = txTo.Inputs[0].ScriptSig; combined = Script.CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); Assert.True(combined == scriptSig); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); Assert.True(combined == scriptSig); // A couple of partially-signed versions: uint256 hash1 = scriptPubKey.SignatureHash(txTo, 0, SigHash.All); var sig1 = new TransactionSignature(keys[0].Sign(hash1), SigHash.All); uint256 hash2 = scriptPubKey.SignatureHash(txTo, 0, SigHash.None); var sig2 = new TransactionSignature(keys[1].Sign(hash2), SigHash.None); uint256 hash3 = scriptPubKey.SignatureHash(txTo, 0, SigHash.Single); var sig3 = new TransactionSignature(keys[2].Sign(hash3), SigHash.Single); // Not fussy about order (or even existence) of placeholders or signatures: Script partial1a = new Script() + OpcodeType.OP_0 + Op.GetPushOp(sig1.ToBytes()) + OpcodeType.OP_0; Script partial1b = new Script() + OpcodeType.OP_0 + OpcodeType.OP_0 + Op.GetPushOp(sig1.ToBytes()); Script partial2a = new Script() + OpcodeType.OP_0 + Op.GetPushOp(sig2.ToBytes()); Script partial2b = new Script() + Op.GetPushOp(sig2.ToBytes()) + OpcodeType.OP_0; Script partial3a = new Script() + Op.GetPushOp(sig3.ToBytes()); Script partial3b = new Script() + OpcodeType.OP_0 + OpcodeType.OP_0 + Op.GetPushOp(sig3.ToBytes()); Script partial3c = new Script() + OpcodeType.OP_0 + Op.GetPushOp(sig3.ToBytes()) + OpcodeType.OP_0; Script complete12 = new Script() + OpcodeType.OP_0 + Op.GetPushOp(sig1.ToBytes()) + Op.GetPushOp(sig2.ToBytes()); Script complete13 = new Script() + OpcodeType.OP_0 + Op.GetPushOp(sig1.ToBytes()) + Op.GetPushOp(sig3.ToBytes()); Script complete23 = new Script() + OpcodeType.OP_0 + Op.GetPushOp(sig2.ToBytes()) + Op.GetPushOp(sig3.ToBytes()); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial1b); Assert.True(combined == partial1a); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial2a); Assert.True(combined == complete12); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial1a); Assert.True(combined == complete12); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial1b, partial2b); Assert.True(combined == complete12); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial1b); Assert.True(combined == complete13); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial3a); Assert.True(combined == complete23); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial2b); Assert.True(combined == complete23); combined = Script.CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial3a); Assert.True(combined == partial3c); }
public void CanUseCompactVarInt() { var tests = new[]{ new object[]{0UL, new byte[]{0}}, new object[]{1UL, new byte[]{1}}, new object[]{127UL, new byte[]{0x7F}}, new object[]{128UL, new byte[]{0x80, 0x00}}, new object[]{255UL, new byte[]{0x80, 0x7F}}, new object[]{256UL, new byte[]{0x81, 0x00}}, new object[]{16383UL, new byte[]{0xFE, 0x7F}}, //new object[]{16384UL, new byte[]{0xFF, 0x00}}, //new object[]{16511UL, new byte[]{0x80, 0xFF, 0x7F}}, //new object[]{65535UL, new byte[]{0x82, 0xFD, 0x7F}}, new object[]{(ulong)1 << 32, new byte[]{0x8E, 0xFE, 0xFE, 0xFF, 0x00}}, }; foreach(var test in tests) { ulong val = (ulong)test[0]; byte[] expectedBytes = (byte[])test[1]; AssertEx.CollectionEquals(new CompactVarInt(val, sizeof(ulong)).ToBytes(), expectedBytes); AssertEx.CollectionEquals(new CompactVarInt(val, sizeof(uint)).ToBytes(), expectedBytes); var compact = new CompactVarInt(sizeof(ulong)); compact.ReadWrite(expectedBytes); Assert.Equal(val, compact.ToLong()); compact = new CompactVarInt(sizeof(uint)); compact.ReadWrite(expectedBytes); Assert.Equal(val, compact.ToLong()); } foreach(var i in Enumerable.Range(0, 65535 * 4)) { var compact = new CompactVarInt((ulong)i, sizeof(ulong)); var bytes = compact.ToBytes(); compact = new CompactVarInt(sizeof(ulong)); compact.ReadWrite(bytes); Assert.Equal((ulong)i, compact.ToLong()); } }
//https://en.bitcoin.it/wiki/List_of_address_prefixes public void CanDetectBase58NetworkAndType() { var tests = new[] { new { Base58 = "bWqaKUZETiECYgmJNbNZUoanBxnAzoVjCNx", ExpectedType = typeof(BitcoinColoredAddress), Network = Network.TestNet }, new { Base58 = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem", ExpectedType = typeof(BitcoinPubKeyAddress), Network = Network.Main }, new { Base58 = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem", ExpectedType = typeof(BitcoinPubKeyAddress), Network = Network.Main }, new { Base58 = "T7nYdHtL34xLZ2S5KwqgySNNzGxovhszhtDM3wQRUEfUbUVvRZzTW", ExpectedType = typeof(BitcoinWitScriptAddress), Network = Network.SegNet }, new { Base58 = "p2y9AsuyBkVj4fpF6capRJKhVCPno6QFRfrx", ExpectedType = typeof(BitcoinWitPubKeyAddress), Network = Network.SegNet }, new { Base58 = "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX", ExpectedType = typeof(BitcoinScriptAddress), Network = Network.Main }, new { Base58 = "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn", ExpectedType = typeof(BitcoinPubKeyAddress), Network = Network.TestNet }, new { Base58 = "5Hwgr3u458GLafKBgxtssHSPqJnYoGrSzgQsPwLFhLNYskDPyyA", ExpectedType = typeof(BitcoinSecret), Network = Network.Main }, new { Base58 = "92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc", ExpectedType = typeof(BitcoinSecret), Network = Network.TestNet }, new { Base58 = "3qdi7TXgRo1qR", ExpectedType = (Type)null, Network = (Network)null }, new { Base58 = "6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7", ExpectedType = typeof(BitcoinEncryptedSecretNoEC), Network = (Network)null }, new { Base58 = "6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX", ExpectedType = typeof(BitcoinEncryptedSecretEC), Network = (Network)null }, new { Base58 = "passphrasepxFy57B9v8HtUsszJYKReoNDV6VHjUSGt8EVJmux9n1J3Ltf1gRxyDGXqnf9qm", ExpectedType = typeof(BitcoinPassphraseCode), Network = (Network)null }, new { Base58 = "cfrm38V8aXBn7JWA1ESmFMUn6erxeBGZGAxJPY4e36S9QWkzZKtaVqLNMgnifETYw7BPwWC9aPD", ExpectedType = typeof(BitcoinConfirmationCode), Network = (Network)null }, new { Base58 = "xprv9s21ZrQH143K3Gx1VAAD1ueDmwoPQUApekxWYSJ1f4W4m1nUPpRGdV5sTVhixZJT5cP2NqtEMZ2mrwHdW5RWpohCwspWidCpcLALvioXDyz", ExpectedType = typeof(BitcoinExtKey), Network = Network.Main }, new { Base58 = "xpub661MyMwAqRbcEhHavVcryjNF2uA5woK6JCNRNJB8Z3dxPU8VNBd9E8GP7fusw2bhgYe7BXt6izr5iUaYo483919jjdtfEpG8j97djnEgJqo", ExpectedType = typeof(BitcoinExtPubKey), Network = Network.Main }, new { Base58 = "akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy", ExpectedType = typeof(BitcoinColoredAddress), Network = Network.Main } }; foreach(var test in tests) { if(test.ExpectedType == null) { Assert.Throws<FormatException>(() => Network.CreateFromBase58Data(test.Base58)); } else { var result = Network.CreateFromBase58Data(test.Base58); Assert.True(test.ExpectedType == result.GetType()); if(test.Network != null) Assert.True(test.Network == result.Network); Network.CreateFromBase58Data(test.Base58, test.Network); if(test.Network != null) foreach(var network in Network.GetNetworks()) { if(network == test.Network) break; Assert.Throws<FormatException>(() => Network.CreateFromBase58Data(test.Base58, network)); } } } }
public void CanConvertMoney() { var tests = new[] { new object[]{ 1.23456789m, MoneyUnit.BTC, 123456789m, MoneyUnit.Satoshi }, new object[]{ 1.23456789m, MoneyUnit.BTC, 1234.56789m, MoneyUnit.MilliBTC }, new object[]{ 1.23456789m, MoneyUnit.BTC, 1234567.89m, MoneyUnit.Bit }, new object[]{ 1.23456789m, MoneyUnit.BTC, 1.23456789m, MoneyUnit.BTC }, }; foreach(var test in tests) { var inputAmount = (decimal)test[0]; var inputUnit = (MoneyUnit)test[1]; var outputAmount = (decimal)test[2]; var outputUnit = (MoneyUnit)test[3]; var result = new Money(inputAmount, inputUnit); var actual = result.ToUnit(outputUnit); Assert.Equal(outputAmount, actual); result = new Money(outputAmount, outputUnit); actual = result.ToUnit(inputUnit); Assert.Equal(inputAmount, actual); } }
public void CanGeneratePubKeysAndAddress() { //Took from http://brainwallet.org/ and http://procbits.com/2013/08/27/generating-a-bitcoin-address-with-javascript var tests = new[] { new { PrivateKeyWIF = "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD", CompressedPrivateKeyWIF = "KwomKti1X3tYJUUMb1TGSM2mrZk1wb1aHisUNHCQXTZq5auC2qc3", PubKey = "04d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6fbdd594388756a7beaf73b4822bc22d36e9bda7db82df2b8b623673eefc0b7495", CompressedPubKey = "03d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6f", Address = "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS", CompressedAddress = "1FkKMsKNJqWSDvTvETqcCeHcUQQ64kSC6s", Hash160 = "3c176e659bea0f29a3e9bf7880c112b1b31b4dc8", CompressedHash160 = "a1c2f92a9dacbd2991c3897724a93f338e44bdc1", DER = "3082011302010104201184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fda081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a14403420004d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6fbdd594388756a7beaf73b4822bc22d36e9bda7db82df2b8b623673eefc0b7495", CompressedDER = "3081d302010104201184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fda08185308182020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a12403220003d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6f" }, new { PrivateKeyWIF = "5J7WTMRn1vjZ9udUxNCLq7F9DYEJiqRCjstiBrY6mDjnaomd6kZ", CompressedPrivateKeyWIF = "KxXj1KAMh6ApvKJ2PNZ4XLZRGLqjDehppFdEnueGSBDrC2Hfe7vt", PubKey = "0493e5d305cad2588d5fb254065fe48ce446028ba380e6ee663baea9cd105500897eb030c033cdab160f31c36df0ea38330fdd69677df49cd14826902022d17f3f", CompressedPubKey = "0393e5d305cad2588d5fb254065fe48ce446028ba380e6ee663baea9cd10550089", Address = "1MZmwgyMyjM11uA6ZSpgn1uK3LBWCzvV6e", CompressedAddress = "1AECNr2TDye8dpC1TeDH3eJpGoZ7dNPy4g", Hash160 = "e19557c8f8fb53a964c5dc7bfde86d806709f7c5", CompressedHash160 = "6538094af65453ea279f14d1a04b408e3adfebd7", DER = "308201130201010420271ac4d7056937c156abd828850d05df0697dd662d3c1b0107f53a387b4c176ca081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a1440342000493e5d305cad2588d5fb254065fe48ce446028ba380e6ee663baea9cd105500897eb030c033cdab160f31c36df0ea38330fdd69677df49cd14826902022d17f3f", CompressedDER = "3081d30201010420271ac4d7056937c156abd828850d05df0697dd662d3c1b0107f53a387b4c176ca08185308182020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a1240322000393e5d305cad2588d5fb254065fe48ce446028ba380e6ee663baea9cd10550089" } }; foreach(var test in tests) { BitcoinSecret secret = Network.Main.CreateBitcoinSecret(test.PrivateKeyWIF); Assert.Equal(test.PubKey, secret.PrivateKey.PubKey.ToHex()); TestDERCoherence(secret); TestDEREqual(test.DER, secret); var address = Network.Main.CreateBitcoinAddress(test.Address); Assert.Equal(new KeyId(test.Hash160), address.Hash); Assert.Equal(new KeyId(test.Hash160), secret.PrivateKey.PubKey.Hash); Assert.Equal(address.Hash, secret.PrivateKey.PubKey.GetAddress(Network.Main).Hash); var compressedSec = secret.Copy(true); TestDERCoherence(compressedSec); TestDEREqual(test.CompressedDER, compressedSec); var a = secret.PrivateKey.PubKey; var b = compressedSec.PrivateKey.PubKey; Assert.Equal(test.CompressedPrivateKeyWIF, compressedSec.ToWif()); Assert.Equal(test.CompressedPubKey, compressedSec.PrivateKey.PubKey.ToHex()); Assert.True(compressedSec.PrivateKey.PubKey.IsCompressed); var compressedAddr = Network.Main.CreateBitcoinAddress(test.CompressedAddress); Assert.Equal(new KeyId(test.CompressedHash160), compressedAddr.Hash); Assert.Equal(new KeyId(test.CompressedHash160), compressedSec.PrivateKey.PubKey.Hash); } }
public void CanGeneratePubKeysAndAddress() { //Took from http://brainwallet.org/ and http://procbits.com/2013/08/27/generating-a-bitcoin-address-with-javascript var tests = new[] { new { PrivateKeyWIF = "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD", CompressedPrivateKeyWIF = "KwomKti1X3tYJUUMb1TGSM2mrZk1wb1aHisUNHCQXTZq5auC2qc3", PubKey = "04d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6fbdd594388756a7beaf73b4822bc22d36e9bda7db82df2b8b623673eefc0b7495", CompressedPubKey = "03d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6f", Address = "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS", CompressedAddress = "1FkKMsKNJqWSDvTvETqcCeHcUQQ64kSC6s", Hash160 = "3c176e659bea0f29a3e9bf7880c112b1b31b4dc8", CompressedHash160 = "a1c2f92a9dacbd2991c3897724a93f338e44bdc1" }, new { PrivateKeyWIF = "5J7WTMRn1vjZ9udUxNCLq7F9DYEJiqRCjstiBrY6mDjnaomd6kZ", CompressedPrivateKeyWIF = "KxXj1KAMh6ApvKJ2PNZ4XLZRGLqjDehppFdEnueGSBDrC2Hfe7vt", PubKey = "0493e5d305cad2588d5fb254065fe48ce446028ba380e6ee663baea9cd105500897eb030c033cdab160f31c36df0ea38330fdd69677df49cd14826902022d17f3f", CompressedPubKey = "0393e5d305cad2588d5fb254065fe48ce446028ba380e6ee663baea9cd10550089", Address = "1MZmwgyMyjM11uA6ZSpgn1uK3LBWCzvV6e", CompressedAddress = "1AECNr2TDye8dpC1TeDH3eJpGoZ7dNPy4g", Hash160 = "e19557c8f8fb53a964c5dc7bfde86d806709f7c5", CompressedHash160 = "6538094af65453ea279f14d1a04b408e3adfebd7" } }; foreach(var test in tests) { BitcoinSecret secret = Network.Main.CreateBitcoinSecret(test.PrivateKeyWIF); Assert.Equal(test.PubKey, secret.PrivateKey.PubKey.ToHex()); var address = (BitcoinPubKeyAddress)Network.Main.CreateBitcoinAddress(test.Address); Assert.Equal(new KeyId(test.Hash160), address.Hash); Assert.Equal(new KeyId(test.Hash160), secret.PrivateKey.PubKey.Hash); Assert.Equal(address.Hash, secret.PrivateKey.PubKey.GetAddress(Network.Main).Hash); var compressedSec = secret.Copy(true); var a = secret.PrivateKey.PubKey; var b = compressedSec.PrivateKey.PubKey; Assert.Equal(test.CompressedPrivateKeyWIF, compressedSec.ToWif()); Assert.Equal(test.CompressedPubKey, compressedSec.PrivateKey.PubKey.ToHex()); Assert.True(compressedSec.PrivateKey.PubKey.IsCompressed); var compressedAddr = (BitcoinPubKeyAddress)Network.Main.CreateBitcoinAddress(test.CompressedAddress); Assert.Equal(new KeyId(test.CompressedHash160), compressedAddr.Hash); Assert.Equal(new KeyId(test.CompressedHash160), compressedSec.PrivateKey.PubKey.Hash); } }
public EncryptedKeyResult GenerateEncryptedSecret(bool isCompressed = true, byte[] seedb = null) { //Set flagbyte. byte flagByte = 0; //Turn on bit 0x20 if the Bitcoin address will be formed by hashing the compressed public key flagByte |= isCompressed ? (byte)0x20 : (byte)0x00; flagByte |= LotSequence != null ? (byte)0x04 : (byte)0x00; //Generate 24 random bytes, call this seedb. Take SHA256(SHA256(seedb)) to yield 32 bytes, call this factorb. seedb = seedb ?? RandomUtils.GetBytes(24); var factorb = Hashes.Hash256(seedb).ToBytes(); //ECMultiply passpoint by factorb. var curve = ECKey.Secp256k1; var passpoint = curve.Curve.DecodePoint(Passpoint); var pubPoint = passpoint.Multiply(new BigInteger(1, factorb)); //Use the resulting EC point as a public key var pubKey = new PubKey(pubPoint.GetEncoded()); //and hash it into a Bitcoin address using either compressed or uncompressed public key //This is the generated Bitcoin address, call it generatedaddress. pubKey = isCompressed ? pubKey.Compress() : pubKey.Decompress(); //call it generatedaddress. var generatedaddress = pubKey.GetAddress(Network); //Take the first four bytes of SHA256(SHA256(generatedaddress)) and call it addresshash. var addresshash = BitcoinEncryptedSecretEC.HashAddress(generatedaddress); //Derive a second key from passpoint using scrypt //salt is addresshash + ownerentropy var derived = BitcoinEncryptedSecretEC.CalculateDecryptionKey(Passpoint, addresshash, OwnerEntropy); //Now we will encrypt seedb. var encrypted = BitcoinEncryptedSecret.EncryptSeed (seedb, derived); //0x01 0x43 + flagbyte + addresshash + ownerentropy + encryptedpart1[0...7] + encryptedpart2 which totals 39 bytes var bytes = new[] { flagByte } .Concat(addresshash) .Concat(this.OwnerEntropy) .Concat(encrypted.Take(8).ToArray()) .Concat(encrypted.Skip(16).ToArray()) .ToArray(); var encryptedSecret = new BitcoinEncryptedSecretEC(bytes, Network); return new EncryptedKeyResult(encryptedSecret, generatedaddress, seedb, () => { //ECMultiply factorb by G, call the result pointb. The result is 33 bytes. var pointb = new Key(factorb).PubKey.ToBytes(); //The first byte is 0x02 or 0x03. XOR it by (derivedhalf2[31] & 0x01), call the resulting byte pointbprefix. var pointbprefix = (byte)(pointb[0] ^ (byte)(derived[63] & 0x01)); var pointbx = BitcoinEncryptedSecret.EncryptKey(pointb.Skip(1).ToArray(), derived); var encryptedpointb = new byte[] { pointbprefix }.Concat(pointbx).ToArray(); var confirmBytes = Network.GetVersionBytes(Base58Type.CONFIRMATION_CODE) .Concat(new[] { flagByte }) .Concat(addresshash) .Concat(OwnerEntropy) .Concat(encryptedpointb) .ToArray(); return new BitcoinConfirmationCode(Encoders.Base58Check.EncodeData(confirmBytes), Network); }); }