|
| 1 | +#if HAS_SPAN |
| 2 | +using NBitcoin.BIP322; |
| 3 | +using System; |
| 4 | +using System.Threading.Tasks; |
| 5 | +using Xunit; |
| 6 | + |
| 7 | +namespace NBitcoin.Tests |
| 8 | +{ |
| 9 | + [Trait("UnitTest", "UnitTest")] |
| 10 | + public class BIP322Tests |
| 11 | + { |
| 12 | + //from https://github.com/ACken2/bip322-js/tree/main/test && https://github.com/bitcoin/bitcoin/pull/24058/files#diff-2bd57d7fbec4bb262834d155c304ebe15d26f73fea87c75ff273df3529a15510 |
| 13 | + [Fact] |
| 14 | + public void CanHandleLegacyBIP322Message() |
| 15 | + { |
| 16 | + var address = BitcoinAddress.Create("1F3sAm6ZtwLAUnj7d38pGFxtP3RVEvtsbV", Network.Main); |
| 17 | + var addressTestnet = BitcoinAddress.Create("muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi", Network.TestNet); |
| 18 | + var addressRegtest = BitcoinAddress.Create("muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi", Network.RegTest); |
| 19 | + var addressWrong = BitcoinAddress.Create("1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", Network.Main); |
| 20 | + var addressWrongTestnet = BitcoinAddress.Create("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn", Network.TestNet); |
| 21 | + var addressWrongRegtest = BitcoinAddress.Create("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn", Network.RegTest); |
| 22 | + var message = "This is an example of a signed message."; |
| 23 | + var messageWrong = ""; |
| 24 | + var signature = BIP322.BIP322Signature.Parse("H9L5yLFjti0QTHhPyFrZCT1V/MMnBtXKmoiKDZ78NDBjERki6ZTQZdSMCtkgoNmp17By9ItJr8o7ChX0XxY91nk=", address.Network); |
| 25 | + Assert.True(address.VerifyBIP322(message, signature)); |
| 26 | + Assert.True(addressTestnet.VerifyBIP322(message, signature)); |
| 27 | + Assert.True(addressRegtest.VerifyBIP322(message, signature)); |
| 28 | + Assert.False(address.VerifyBIP322(messageWrong, signature)); |
| 29 | + Assert.False(addressTestnet.VerifyBIP322(messageWrong, signature)); |
| 30 | + Assert.False(addressRegtest.VerifyBIP322(messageWrong, signature)); |
| 31 | + Assert.False(addressWrong.VerifyBIP322(message, signature)); |
| 32 | + Assert.False(addressWrongTestnet.VerifyBIP322(message, signature)); |
| 33 | + Assert.False(addressWrongRegtest.VerifyBIP322(message, signature)); |
| 34 | + |
| 35 | + |
| 36 | + var privateKey = new BitcoinSecret("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k", Network.Main) |
| 37 | + .PrivateKey; |
| 38 | + address = privateKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main); |
| 39 | + addressTestnet = privateKey.GetAddress(ScriptPubKeyType.Legacy, Network.TestNet); |
| 40 | + addressRegtest = privateKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest); |
| 41 | + message = "Hello World"; |
| 42 | + signature = privateKey.SignBIP322(address, message, SignatureType.Legacy); |
| 43 | + var signatureTestnet = |
| 44 | + privateKey.SignBIP322(addressTestnet, message, SignatureType.Legacy); |
| 45 | + var signatureRegtest = |
| 46 | + privateKey.SignBIP322(addressRegtest, message, SignatureType.Legacy); |
| 47 | + |
| 48 | + Assert.True(address.VerifyBIP322(message, signature)); |
| 49 | + Assert.True(addressTestnet.VerifyBIP322(message, signatureTestnet)); |
| 50 | + Assert.True(addressRegtest.VerifyBIP322(message, signatureRegtest)); |
| 51 | + } |
| 52 | + |
| 53 | + [Fact] |
| 54 | + public void CanSign() |
| 55 | + { |
| 56 | + var k = new Key(); |
| 57 | + var p = k.GetAddress(ScriptPubKeyType.SegwitP2SH, Network.Main); |
| 58 | + |
| 59 | + var privateKey = new BitcoinSecret("KwTbAxmBXjoZM3bzbXixEr9nxLhyYSM4vp2swet58i19bw9sqk5z", Network.Main) |
| 60 | + .PrivateKey; // Private key of "3HSVzEhCFuH9Z3wvoWTexy7BMVVp3PjS6f" |
| 61 | + var privateKeyTestnet = |
| 62 | + new BitcoinSecret("cMpadsm2xoVpWV5FywY5cAeraa1PCtSkzrBM45Ladpf9rgDu6cMz", Network.TestNet) |
| 63 | + .PrivateKey; // Equivalent to "KwTbAxmBXjoZM3bzbXixEr9nxLhyYSM4vp2swet58i19bw9sqk5z" |
| 64 | + var address = BitcoinAddress.Create("3HSVzEhCFuH9Z3wvoWTexy7BMVVp3PjS6f", Network.Main); |
| 65 | + var addressTestnet = BitcoinAddress.Create("2N8zi3ydDsMnVkqaUUe5Xav6SZqhyqEduap", Network.TestNet); |
| 66 | + var addressRegtest = BitcoinAddress.Create("2N8zi3ydDsMnVkqaUUe5Xav6SZqhyqEduap", Network.RegTest); |
| 67 | + var message = "Hello World"; |
| 68 | + |
| 69 | + var signature = privateKey.SignBIP322(address, message, SignatureType.Simple); |
| 70 | + var signatureTestnet = |
| 71 | + privateKeyTestnet.SignBIP322(addressTestnet, message, SignatureType.Simple); |
| 72 | + var signatureRegtest = |
| 73 | + privateKeyTestnet.SignBIP322(addressRegtest, message, SignatureType.Simple); |
| 74 | + |
| 75 | + Assert.Equal(signatureTestnet, signature); |
| 76 | + Assert.Equal(signatureRegtest, signature); |
| 77 | + |
| 78 | + var k1 = new BitcoinSecret("L4DksdGZ4KQJfcLHD5Dv25fu8Rxyv7hHi2RjZR4TYzr8c6h9VNrp", Network.Main).PrivateKey; |
| 79 | + var k2 = new BitcoinSecret("KzSRqnCVwjzY8id2X5oHEJWXkSHwKUYaAXusjwgkES8BuQPJnPNu", Network.Main).PrivateKey; |
| 80 | + var k3 = new BitcoinSecret("L1zt9Rw7HrU7jaguMbVzhiX8ffuVkmMis5wLHddXYuHWYf8u8uRj", Network.Main).PrivateKey; |
| 81 | + |
| 82 | + |
| 83 | + var redeem = |
| 84 | + PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, true, |
| 85 | + new PubKey[] {k1.PubKey, k2.PubKey, k3.PubKey}); |
| 86 | + var p2wshScriptPubKey = redeem.WitHash.ScriptPubKey; |
| 87 | + var p2ShAddressScriptPubKey = redeem.Hash.ScriptPubKey; |
| 88 | + var p2shAddress = p2ShAddressScriptPubKey.GetDestinationAddress(Network.Main); |
| 89 | + var p2wshAddress = p2wshScriptPubKey.GetDestinationAddress(Network.Main); |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | + var message2of3 = "This will be a 2-of-3 multisig BIP 322 signed message"; |
| 94 | + |
| 95 | + var toSign = p2shAddress.CreateBIP322PSBT(message2of3); |
| 96 | + toSign.AddScripts(redeem); |
| 97 | + toSign.SignWithKeys(k2, k3); |
| 98 | + |
| 99 | + Assert.Throws<ArgumentException>(() => |
| 100 | + { |
| 101 | + BIP322Signature.FromPSBT(toSign, SignatureType.Simple); |
| 102 | + }); |
| 103 | + Assert.Throws<ArgumentException>(() => |
| 104 | + { |
| 105 | + BIP322Signature.FromPSBT(toSign, SignatureType.Legacy); |
| 106 | + }); |
| 107 | + |
| 108 | + var p2sh_signature2of3_k2_k3 = BIP322Signature.FromPSBT(toSign, SignatureType.Full); |
| 109 | + |
| 110 | + |
| 111 | + toSign = p2shAddress.CreateBIP322PSBT(message2of3); |
| 112 | + toSign.AddScripts(redeem); |
| 113 | + toSign.SignWithKeys(k1, k3); |
| 114 | + var p2sh_signature2of3_k1_k3 = BIP322Signature.FromPSBT(toSign, SignatureType.Full); |
| 115 | + |
| 116 | + Assert.Throws<PSBTException>(() => |
| 117 | + { |
| 118 | + toSign = p2shAddress.CreateBIP322PSBT(message2of3); |
| 119 | + toSign.AddScripts(redeem); |
| 120 | + toSign.SignWithKeys(k1); |
| 121 | + // Missing one sig out of 3 |
| 122 | + BIP322Signature.FromPSBT(toSign, SignatureType.Full); |
| 123 | + }); |
| 124 | + |
| 125 | + Assert.True(p2shAddress.VerifyBIP322(message2of3, p2sh_signature2of3_k2_k3)); |
| 126 | + Assert.True(p2shAddress.VerifyBIP322(message2of3, p2sh_signature2of3_k1_k3)); |
| 127 | + |
| 128 | + toSign = p2wshAddress.CreateBIP322PSBT(message2of3); |
| 129 | + toSign.AddScripts(redeem); |
| 130 | + toSign.SignWithKeys(k2, k3); |
| 131 | + var p2wsh_signature2of3_k2_k3 = BIP322Signature.FromPSBT(toSign, SignatureType.Simple); |
| 132 | + |
| 133 | + toSign = p2wshAddress.CreateBIP322PSBT(message2of3); |
| 134 | + toSign.AddScripts(redeem); |
| 135 | + toSign.SignWithKeys(k1, k3); |
| 136 | + var p2wsh_signature2of3_k1_k3 = BIP322Signature.FromPSBT(toSign, SignatureType.Simple); |
| 137 | + |
| 138 | + Assert.True(p2wshAddress.VerifyBIP322(message2of3, p2wsh_signature2of3_k2_k3)); |
| 139 | + Assert.True(p2wshAddress.VerifyBIP322(message2of3, p2wsh_signature2of3_k1_k3)); |
| 140 | + |
| 141 | + p2wsh_signature2of3_k1_k3 = BIP322Signature.FromPSBT(toSign, SignatureType.Full); |
| 142 | + Assert.True(p2wshAddress.VerifyBIP322(message2of3, p2wsh_signature2of3_k1_k3)); |
| 143 | + |
| 144 | + Assert.Throws<PSBTException>(() => |
| 145 | + { |
| 146 | + toSign = p2wshAddress.CreateBIP322PSBT(message2of3); |
| 147 | + toSign.AddScripts(redeem); |
| 148 | + toSign.SignWithKeys(k1); |
| 149 | + BIP322Signature.FromPSBT(toSign, SignatureType.Simple); |
| 150 | + }); |
| 151 | + } |
| 152 | + |
| 153 | + |
| 154 | + [Fact] |
| 155 | + public void CanVerifyBIP322Message() |
| 156 | + { |
| 157 | + var key = new BitcoinSecret("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k", Network.Main) |
| 158 | + .PrivateKey; |
| 159 | + |
| 160 | + var segwitAddress = key.GetAddress(ScriptPubKeyType.Segwit, Network.Main); |
| 161 | + Assert.Equal("bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l", segwitAddress.ToString()); |
| 162 | + |
| 163 | + var emptyStringPSBT = segwitAddress.CreateBIP322PSBT(""); |
| 164 | + var helloWorldToSpendPSBT = segwitAddress.CreateBIP322PSBT("Hello World"); |
| 165 | + |
| 166 | + Assert.Equal("c5680aa69bb8d860bf82d4e9cd3504b55dde018de765a91bb566283c545a99a7", |
| 167 | + emptyStringPSBT.Inputs[0].NonWitnessUtxo.GetHash().ToString()); |
| 168 | + Assert.Equal("b79d196740ad5217771c1098fc4a4b51e0535c32236c71f1ea4d61a2d603352b", |
| 169 | + helloWorldToSpendPSBT.Inputs[0].NonWitnessUtxo.GetHash().ToString()); |
| 170 | + |
| 171 | + Assert.Equal("1e9654e951a5ba44c8604c4de6c67fd78a27e81dcadcfe1edf638ba3aaebaed6", |
| 172 | + emptyStringPSBT.GetGlobalTransaction().GetHash().ToString()); |
| 173 | + Assert.Equal("88737ae86f2077145f93cc4b153ae9a1cb8d56afa511988c149c5c8c9d93bddf", |
| 174 | + helloWorldToSpendPSBT.GetGlobalTransaction().GetHash().ToString()); |
| 175 | + |
| 176 | + var simpleHelloWorldSignature = key.SignBIP322(segwitAddress, "Hello World", SignatureType.Simple); |
| 177 | + var simpleEmptySignature = key.SignBIP322(segwitAddress, "", SignatureType.Simple); |
| 178 | + |
| 179 | + var fullHelloWorldSignature = key.SignBIP322(segwitAddress, "Hello World", SignatureType.Full); |
| 180 | + var fullEmptySignature = key.SignBIP322(segwitAddress, "", SignatureType.Full); |
| 181 | + |
| 182 | + |
| 183 | + Assert.True(segwitAddress.VerifyBIP322("Hello World", simpleHelloWorldSignature)); |
| 184 | + Assert.True(segwitAddress.VerifyBIP322("", simpleEmptySignature)); |
| 185 | + Assert.True(segwitAddress.VerifyBIP322("Hello World", fullHelloWorldSignature)); |
| 186 | + Assert.True(segwitAddress.VerifyBIP322("", fullEmptySignature)); |
| 187 | + |
| 188 | + Assert.False(segwitAddress.VerifyBIP322("nuhuh", simpleHelloWorldSignature)); |
| 189 | + Assert.False(segwitAddress.VerifyBIP322("nuhuh", simpleEmptySignature)); |
| 190 | + Assert.False(segwitAddress.VerifyBIP322("nuhuh", fullHelloWorldSignature)); |
| 191 | + Assert.False(segwitAddress.VerifyBIP322("nuhuh", fullEmptySignature)); |
| 192 | + |
| 193 | + foreach (var t in new[] |
| 194 | + { |
| 195 | + ("", "AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="), |
| 196 | + ("", "AkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy"), |
| 197 | + ("Hello World", "AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="), |
| 198 | + ("Hello World", "AkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy") |
| 199 | + }) |
| 200 | + { |
| 201 | + (var message, var sig) = t; |
| 202 | + Assert.True(segwitAddress.VerifyBIP322(message, sig)); |
| 203 | + } |
| 204 | + |
| 205 | + // // 2-of-3 p2sh multisig BIP322 signature (created with the buidl-python library) |
| 206 | + // // Keys are defined as (HDRootWIF, bip322_path) |
| 207 | + // // Key1 (L4DksdGZ4KQJfcLHD5Dv25fu8Rxyv7hHi2RjZR4TYzr8c6h9VNrp, m/45'/0/0/1) |
| 208 | + // // Key2 (KzSRqnCVwjzY8id2X5oHEJWXkSHwKUYaAXusjwgkES8BuQPJnPNu, m/45'/0/0/3) |
| 209 | + // // Key3 (L1zt9Rw7HrU7jaguMbVzhiX8ffuVkmMis5wLHddXYuHWYf8u8uRj, m/45'/0/0/6) |
| 210 | + // // BIP322 includes signs from Key2 and Key3 |
| 211 | + // BOOST_CHECK_EQUAL( |
| 212 | + // MessageVerify( |
| 213 | + // "3LnYoUkFrhyYP3V7rq3mhpwALz1XbCY9Uq", |
| 214 | + // "AAAAAAHNcfHaNfl8f/+ZC2gTr8aF+0KgppYjKM94egaNm/u1ZAAAAAD8AEcwRAIhAJ6hdj61vLDP+aFa30qUZQmrbBfE0kiOObYvt5nqPSxsAh9IrOKFwflfPRUcQ/5e0REkdFHVP2GGdUsMgDet+sNlAUcwRAIgH3eW/VyFDoXvCasd8qxgwj5NDVo0weXvM6qyGXLCR5YCIEwjbEV6fS6RWP6QsKOcMwvlGr1/SgdCC6pW4eH87/YgAUxpUiECKJfGy28imLcuAeNBLHCNv3NRP5jnJwFDNRXCYNY/vJ4hAv1RQtaZs7+vKqQeWl2rb/jd/gMxkEjUnjZdDGPDZkMLIQL65cH2X5O7LujjTLDL2l8Pxy0Y2UUR99u1qCfjdz7dklOuAAAAAAEAAAAAAAAAAAFqAAAAAA==", |
| 215 | + // "This will be a p2sh 2-of-3 multisig BIP 322 signed message"), |
| 216 | + // MessageVerificationResult::OK); |
| 217 | + Assert.True(BitcoinAddress.Create("3LnYoUkFrhyYP3V7rq3mhpwALz1XbCY9Uq", Network.Main).VerifyBIP322("This will be a p2sh 2-of-3 multisig BIP 322 signed message", |
| 218 | + "AAAAAAHNcfHaNfl8f/+ZC2gTr8aF+0KgppYjKM94egaNm/u1ZAAAAAD8AEcwRAIhAJ6hdj61vLDP+aFa30qUZQmrbBfE0kiOObYvt5nqPSxsAh9IrOKFwflfPRUcQ/5e0REkdFHVP2GGdUsMgDet+sNlAUcwRAIgH3eW/VyFDoXvCasd8qxgwj5NDVo0weXvM6qyGXLCR5YCIEwjbEV6fS6RWP6QsKOcMwvlGr1/SgdCC6pW4eH87/YgAUxpUiECKJfGy28imLcuAeNBLHCNv3NRP5jnJwFDNRXCYNY/vJ4hAv1RQtaZs7+vKqQeWl2rb/jd/gMxkEjUnjZdDGPDZkMLIQL65cH2X5O7LujjTLDL2l8Pxy0Y2UUR99u1qCfjdz7dklOuAAAAAAEAAAAAAAAAAAFqAAAAAA==")); |
| 219 | + |
| 220 | + // // 3-of-3 p2wsh multisig BIP322 signature (created with the buidl-python library) |
| 221 | + // // Keys are defined as (HDRootWIF, bip322_path) |
| 222 | + // // Key1 (L4DksdGZ4KQJfcLHD5Dv25fu8Rxyv7hHi2RjZR4TYzr8c6h9VNrp, m/45'/0/0/6) |
| 223 | + // // Key2 (KzSRqnCVwjzY8id2X5oHEJWXkSHwKUYaAXusjwgkES8BuQPJnPNu, m/45'/0/0/9) |
| 224 | + // // Key3 (L1zt9Rw7HrU7jaguMbVzhiX8ffuVkmMis5wLHddXYuHWYf8u8uRj, m/45'/0/0/11) |
| 225 | + // BOOST_CHECK_EQUAL( |
| 226 | + // MessageVerify( |
| 227 | + // "bc1qlqtuzpmazp2xmcutlwv0qvggdvem8vahkc333usey4gskug8nutsz53msw", "BQBIMEUCIQDQoXvGKLH58exuujBOta+7+GN7vi0lKwiQxzBpuNuXuAIgIE0XYQlFDOfxbegGYYzlf+tqegleAKE6SXYIa1U+uCcBRzBEAiATegywVl6GWrG9jJuPpNwtgHKyVYCX2yfuSSDRFATAaQIgTLlU6reLQsSIrQSF21z3PtUO2yAUseUWGZqRUIE7VKoBSDBFAiEAgxtpidsU0Z4u/+5RB9cyeQtoCW5NcreLJmWXZ8kXCZMCIBR1sXoEinhZE4CF9P9STGIcMvCuZjY6F5F0XTVLj9SjAWlTIQP3dyWvTZjUENWJowMWBsQrrXCUs20Gu5YF79CG5Ga0XSEDwqI5GVBOuFkFzQOGH5eTExSAj2Z/LDV/hbcvAPQdlJMhA17FuuJd+4wGuj+ZbVxEsFapTKAOwyhfw9qpch52JKxbU64=", |
| 228 | + // "This will be a p2wsh 3-of-3 multisig BIP 322 signed message"), |
| 229 | + // MessageVerificationResult::OK); |
| 230 | + Assert.True(BitcoinAddress.Create("bc1qlqtuzpmazp2xmcutlwv0qvggdvem8vahkc333usey4gskug8nutsz53msw", Network.Main).VerifyBIP322("This will be a p2wsh 3-of-3 multisig BIP 322 signed message", |
| 231 | + "BQBIMEUCIQDQoXvGKLH58exuujBOta+7+GN7vi0lKwiQxzBpuNuXuAIgIE0XYQlFDOfxbegGYYzlf+tqegleAKE6SXYIa1U+uCcBRzBEAiATegywVl6GWrG9jJuPpNwtgHKyVYCX2yfuSSDRFATAaQIgTLlU6reLQsSIrQSF21z3PtUO2yAUseUWGZqRUIE7VKoBSDBFAiEAgxtpidsU0Z4u/+5RB9cyeQtoCW5NcreLJmWXZ8kXCZMCIBR1sXoEinhZE4CF9P9STGIcMvCuZjY6F5F0XTVLj9SjAWlTIQP3dyWvTZjUENWJowMWBsQrrXCUs20Gu5YF79CG5Ga0XSEDwqI5GVBOuFkFzQOGH5eTExSAj2Z/LDV/hbcvAPQdlJMhA17FuuJd+4wGuj+ZbVxEsFapTKAOwyhfw9qpch52JKxbU64=")); |
| 232 | + |
| 233 | + // // Single key p2tr BIP322 signature (created with the buidl-python library) |
| 234 | + // // PrivateKeyWIF L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k |
| 235 | + // BOOST_CHECK_EQUAL( |
| 236 | + // MessageVerify( |
| 237 | + // "bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3", |
| 238 | + // "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==", |
| 239 | + // "Hello World"), |
| 240 | + // MessageVerificationResult::OK); |
| 241 | + Assert.True(BitcoinAddress.Create("bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3", Network.Main).VerifyBIP322("Hello World", |
| 242 | + "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==")); |
| 243 | + } |
| 244 | + |
| 245 | + |
| 246 | + [Fact] |
| 247 | + public void CanDoProofOfFunds() |
| 248 | + { |
| 249 | + var key = new Key(); |
| 250 | + var key2 = new Key(); |
| 251 | + var addr = key.GetAddress(ScriptPubKeyType.Segwit, Network.Main); |
| 252 | + var addr2 = key2.GetAddress(ScriptPubKeyType.Segwit, Network.Main); |
| 253 | + var addr3 = key2.GetAddress(ScriptPubKeyType.SegwitP2SH, Network.Main); |
| 254 | + |
| 255 | + var multisigRedeem = |
| 256 | + PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, new[] {key.PubKey, key2.PubKey}); |
| 257 | + var p2wshScriptPubKey = multisigRedeem.WitHash.ScriptPubKey; |
| 258 | + var p2ShAddressScriptPubKey = multisigRedeem.Hash.ScriptPubKey; |
| 259 | + |
| 260 | + var p2shAddress = p2ShAddressScriptPubKey.GetDestinationAddress(Network.Main); |
| 261 | + var p2wshAddress = p2wshScriptPubKey.GetDestinationAddress(Network.Main); |
| 262 | + |
| 263 | + |
| 264 | + var toSpend = p2shAddress.Network.CreateTransaction(); |
| 265 | + toSpend.Inputs.Add(new TxIn(new OutPoint(uint256.Zero, 0xFFFFFFFF), new Script(OpcodeType.OP_0))); ; |
| 266 | + toSpend.Outputs.Add(new TxOut(Money.Zero, p2shAddress.ScriptPubKey)); |
| 267 | + |
| 268 | + var coins = new Coin[] |
| 269 | + { |
| 270 | + new Coin(OutPoint.Zero, new TxOut(Money.Coins(1), addr2)), |
| 271 | + new Coin(new OutPoint(uint256.One, 1), new TxOut(Money.Coins(1), addr3)).ToScriptCoin(addr2.ScriptPubKey), |
| 272 | + new ScriptCoin(new OutPoint(toSpend, 0), new TxOut(Money.Coins(1), p2shAddress), multisigRedeem), |
| 273 | + new ScriptCoin(new OutPoint(uint256.One, 4), new TxOut(Money.Coins(1), p2wshAddress), multisigRedeem) |
| 274 | + }; |
| 275 | + |
| 276 | + var message = "I own these coins"; |
| 277 | + |
| 278 | + var psbt = addr.CreateBIP322PSBT(message, fundProofOutputs: coins); |
| 279 | + psbt.AddScripts(multisigRedeem); |
| 280 | + psbt.AddTransactions(toSpend); |
| 281 | + psbt.SignWithKeys(key, key2); |
| 282 | + var signature = BIP322Signature.FromPSBT(psbt, SignatureType.Full); |
| 283 | + Assert.True(addr.VerifyBIP322(message, signature, coins)); |
| 284 | + } |
| 285 | + } |
| 286 | +} |
| 287 | +#endif |
0 commit comments