public StealthPayment(BitcoinStealthAddress address, Key ephemKey, StealthMetadata metadata) { Metadata = metadata; ScriptPubKey = CreatePaymentScript(address.SignatureCount, address.SpendPubKeys, ephemKey, address.ScanPubKey); if(address.SignatureCount > 1) { Redeem = ScriptPubKey; ScriptPubKey = ScriptPubKey.Hash.ScriptPubKey; } SetStealthKeys(); }
public StealthPayment(BitcoinStealthAddress address, Key ephemKey, StealthMetadata metadata) { Metadata = metadata; ScriptPubKey = CreatePaymentScript(address.SignatureCount, address.SpendPubKeys, ephemKey, address.ScanPubKey); if (address.SignatureCount > 1) { Redeem = ScriptPubKey; ScriptPubKey = ScriptPubKey.Hash.ScriptPubKey; } SetStealthKeys(); }
public static StealthPayment[] GetPayments(Transaction transaction, BitcoinStealthAddress address, Key scan) { var result = new List <StealthPayment>(); for (var i = 0; i < transaction.Outputs.Count - 1; i++) { var metadata = StealthMetadata.TryParse(transaction.Outputs[i].ScriptPubKey); if (metadata != null && (address == null || address.Prefix.Match(metadata.BitField))) { var scriptPubKey = transaction.Outputs[i + 1].ScriptPubKey; var scriptId = PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey); var expectedScriptPubKey = address == null ? scriptPubKey : null; Script redeem = null; if (scriptId != null) { if (address == null) { throw new ArgumentNullException("address"); } redeem = CreatePaymentScript(address, metadata.EphemKey, scan); expectedScriptPubKey = redeem.Hash.ScriptPubKey; if (expectedScriptPubKey != scriptPubKey) { continue; } } var payment = new StealthPayment(scriptPubKey, redeem, metadata); if (scan != null) { if (address != null && payment.StealthKeys.Length != address.SpendPubKeys.Length) { continue; } if (expectedScriptPubKey == null) { expectedScriptPubKey = CreatePaymentScript(address, metadata.EphemKey, scan); } if (expectedScriptPubKey != scriptPubKey) { continue; } } result.Add(payment); } } return(result.ToArray()); }
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 CanBuildStealthTransaction() { var stealthKeys = Enumerable.Range(0, 3).Select(_ => new Key()).ToArray(); var scanKey = new Key(); var darkSatoshi = new BitcoinStealthAddress(scanKey.PubKey, stealthKeys.Select(k => k.PubKey).ToArray(), 2, new BitField(3, 5), Network.Main); var bob = new Key(); var coins = new Coin[] { new Coin() { Outpoint = RandOutpoint(), TxOut = new TxOut("1.00",bob.PubKey.Hash) } }; //Bob sends money to satoshi TransactionBuilder builder = new TransactionBuilder(); builder.StandardTransactionPolicy = EasyPolicy; var tx = builder .AddCoins(coins) .AddKeys(bob) .Send(darkSatoshi, "1.00") .BuildTransaction(true); Assert.True(builder.Verify(tx)); //Satoshi scans a StealthCoin in the transaction with his scan key var stealthCoin = StealthCoin.Find(tx, darkSatoshi, scanKey); Assert.NotNull(stealthCoin); //Satoshi sends back the money to Bob builder = new TransactionBuilder(); builder.StandardTransactionPolicy = EasyPolicy; tx = builder .AddCoins(stealthCoin) .AddKeys(stealthKeys) .AddKeys(scanKey) .Send(bob.PubKey.Hash, "1.00") .BuildTransaction(true); Assert.True(builder.Verify(tx)); //Signed ! //Same scenario, Satoshi wants to send money back to Bob //However, his keys are spread on two machines //He partially signs on the 1st machine builder = new TransactionBuilder(); builder.StandardTransactionPolicy = EasyPolicy; tx = builder .AddCoins(stealthCoin) .AddKeys(stealthKeys.Skip(2).ToArray()) //Only one Stealth Key .AddKeys(scanKey) .Send(bob.PubKey.Hash, "1.00") .BuildTransaction(true); Assert.False(builder.Verify(tx)); //Not fully signed //Then he partially signs on the 2nd machine builder = new TransactionBuilder(); builder.StandardTransactionPolicy = EasyPolicy; tx = builder .AddCoins(stealthCoin) .AddKeys(stealthKeys[0]) //Other key .AddKeys(scanKey) .SignTransaction(tx); Assert.True(builder.Verify(tx)); //Fully signed ! }
public static Script CreatePaymentScript(BitcoinStealthAddress address, PubKey ephemKey, Key scan) { return CreatePaymentScript(address.SignatureCount, address.SpendPubKeys.Select(p => p.UncoverReceiver(scan, ephemKey)).ToArray()); }
public static StealthPayment[] GetPayments(Transaction transaction, BitcoinStealthAddress address, Key scan) { List<StealthPayment> result = new List<StealthPayment>(); for(int i = 0; i < transaction.Outputs.Count - 1; i++) { var metadata = StealthMetadata.TryParse(transaction.Outputs[i].ScriptPubKey); if(metadata != null && (address == null || address.Prefix.Match(metadata.BitField))) { var scriptPubKey = transaction.Outputs[i + 1].ScriptPubKey; var scriptId = PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey); Script expectedScriptPubKey = address == null ? scriptPubKey : null; Script redeem = null; if(scriptId != null) { if(address == null) throw new ArgumentNullException("address"); redeem = CreatePaymentScript(address, metadata.EphemKey, scan); expectedScriptPubKey = redeem.Hash.ScriptPubKey; if(expectedScriptPubKey != scriptPubKey) continue; } var payment = new StealthPayment(scriptPubKey, redeem, metadata); if(scan != null) { if(address != null && payment.StealthKeys.Length != address.SpendPubKeys.Length) continue; if(expectedScriptPubKey == null) { expectedScriptPubKey = CreatePaymentScript(address, metadata.EphemKey, scan); } if(expectedScriptPubKey != scriptPubKey) continue; } result.Add(payment); } } return result.ToArray(); }
public static Script CreatePaymentScript(BitcoinStealthAddress address, PubKey ephemKey, Key scan) { return(CreatePaymentScript(address.SignatureCount, address.SpendPubKeys.Select(p => p.UncoverReceiver(scan, ephemKey)).ToArray())); }