github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/btc/bitcoinparser_test.go (about) 1 //go:build unittest 2 3 package btc 4 5 import ( 6 "encoding/hex" 7 "math/big" 8 "os" 9 "reflect" 10 "testing" 11 12 "github.com/martinboehm/btcutil/chaincfg" 13 "github.com/trezor/blockbook/bchain" 14 ) 15 16 func TestMain(m *testing.M) { 17 c := m.Run() 18 chaincfg.ResetParams() 19 os.Exit(c) 20 } 21 22 func TestGetAddrDescFromAddress(t *testing.T) { 23 type args struct { 24 address string 25 } 26 tests := []struct { 27 name string 28 args args 29 want string 30 wantErr bool 31 }{ 32 { 33 name: "P2PKH", 34 args: args{address: "1JKgN43B9SyLuZH19H5ECvr4KcfrbVHzZ6"}, 35 want: "76a914be027bf3eac907bd4ac8cb9c5293b6f37662722088ac", 36 wantErr: false, 37 }, 38 { 39 name: "P2PKH from P2PK", 40 args: args{address: "1HY6bKYhFH7HF3F48ikvziPHLrEWPGwXcE"}, 41 want: "76a914b563933904dceba5c234e978bea0e9eb8b7e721b88ac", 42 wantErr: false, 43 }, 44 { 45 name: "P2SH", 46 args: args{address: "321x69Cb9HZLWwAWGiUBT1U81r1zPLnEjL"}, 47 want: "a9140394b3cf9a44782c10105b93962daa8dba304d7f87", 48 wantErr: false, 49 }, 50 { 51 name: "P2WPKH", 52 args: args{address: "bc1qrsf2l34jvqnq0lduyz0j5pfu2nkd93nnq0qggn"}, 53 want: "00141c12afc6b2602607fdbc209f2a053c54ecd2c673", 54 wantErr: false, 55 }, 56 { 57 name: "P2WSH", 58 args: args{address: "bc1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355sw5exgr"}, 59 want: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29", 60 wantErr: false, 61 }, 62 { 63 name: " witness_unknown v1", 64 args: args{address: "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y"}, 65 want: "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", 66 wantErr: false, 67 }, 68 { 69 name: " witness_unknown v16", 70 args: args{address: "bc1sw50qgdz25j"}, 71 want: "6002751e", 72 wantErr: false, 73 }, 74 } 75 parser := NewBitcoinParser(GetChainParams("main"), &Configuration{}) 76 77 for _, tt := range tests { 78 t.Run(tt.name, func(t *testing.T) { 79 got, err := parser.GetAddrDescFromAddress(tt.args.address) 80 if (err != nil) != tt.wantErr { 81 t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) 82 return 83 } 84 h := hex.EncodeToString(got) 85 if !reflect.DeepEqual(h, tt.want) { 86 t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) 87 } 88 }) 89 } 90 } 91 92 func TestGetAddrDescFromAddressTestnet(t *testing.T) { 93 type args struct { 94 address string 95 } 96 tests := []struct { 97 name string 98 args args 99 want string 100 wantErr bool 101 }{ 102 { 103 name: "pubkeyhash", 104 args: args{address: "mtkbaiLiUH3fvGJeSzuN3kUgmJzqinLejJ"}, 105 want: "76a914912e2b234f941f30b18afbb4fa46171214bf66c888ac", 106 wantErr: false, 107 }, 108 { 109 name: "scripthash", 110 args: args{address: "2Mv28xcUJdFXBTfGMtja6fVBMCEbsH3r2AW"}, 111 want: "a9141e6ec5a1d12912b396d77d98dcb000e91f517fa487", 112 wantErr: false, 113 }, 114 { 115 name: "witness_v0_keyhash", 116 args: args{address: "tb1qupjdck20as3y4l95cd5wepkv0grcz0p7d8rd5s"}, 117 want: "0014e064dc594fec224afcb4c368ec86cc7a07813c3e", 118 wantErr: false, 119 }, 120 { 121 name: "witness_v0_scripthash", 122 args: args{address: "tb1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355seu0fjv"}, 123 want: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29", 124 wantErr: false, 125 }, 126 { 127 name: "witness_v1_taproot", 128 args: args{address: "tb1pqsv2qyp8hsma46422ecfd3ek02jayumkkzjx7vkf3cqpmfd4ucpsx0cc9h"}, 129 want: "51200418a01027bc37daeaaa567096c7367aa5d27376b0a46f32c98e001da5b5e603", 130 wantErr: false, 131 }, 132 } 133 parser := NewBitcoinParser(GetChainParams("test"), &Configuration{}) 134 135 for _, tt := range tests { 136 t.Run(tt.name, func(t *testing.T) { 137 got, err := parser.GetAddrDescFromAddress(tt.args.address) 138 if (err != nil) != tt.wantErr { 139 t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) 140 return 141 } 142 h := hex.EncodeToString(got) 143 if !reflect.DeepEqual(h, tt.want) { 144 t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) 145 } 146 }) 147 } 148 } 149 150 func TestGetAddrDescFromVout(t *testing.T) { 151 type args struct { 152 vout bchain.Vout 153 } 154 tests := []struct { 155 name string 156 args args 157 want string 158 wantErr bool 159 }{ 160 { 161 name: "P2PKH", 162 args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a914be027bf3eac907bd4ac8cb9c5293b6f37662722088ac"}}}, 163 want: "76a914be027bf3eac907bd4ac8cb9c5293b6f37662722088ac", 164 wantErr: false, 165 }, 166 { 167 name: "P2PK compressed 1P3rU1Nk1pmc2BiWC8dEy9bZa1ZbMp5jfg", 168 args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "21020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9ac"}}}, 169 want: "76a914f1dce4182fce875748c4986b240ff7d7bc3fffb088ac", 170 wantErr: false, 171 }, 172 { 173 name: "P2PK uncompressed 1HY6bKYhFH7HF3F48ikvziPHLrEWPGwXcE", 174 args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "41041057356b91bfd3efeff5fc0fa8b865faafafb67bd653c5da2cd16ce15c7b86db0e622c8e1e135f68918a23601eb49208c1ac72c7b64a4ee99c396cf788da16ccac"}}}, 175 want: "76a914b563933904dceba5c234e978bea0e9eb8b7e721b88ac", 176 wantErr: false, 177 }, 178 { 179 name: "P2SH", 180 args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "a9140394b3cf9a44782c10105b93962daa8dba304d7f87"}}}, 181 want: "a9140394b3cf9a44782c10105b93962daa8dba304d7f87", 182 wantErr: false, 183 }, 184 { 185 name: "P2WPKH", 186 args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "00141c12afc6b2602607fdbc209f2a053c54ecd2c673"}}}, 187 want: "00141c12afc6b2602607fdbc209f2a053c54ecd2c673", 188 wantErr: false, 189 }, 190 { 191 name: "P2WSH", 192 args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29"}}}, 193 want: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29", 194 wantErr: false, 195 }, 196 } 197 parser := NewBitcoinParser(GetChainParams("main"), &Configuration{}) 198 199 for _, tt := range tests { 200 t.Run(tt.name, func(t *testing.T) { 201 got, err := parser.GetAddrDescFromVout(&tt.args.vout) 202 if (err != nil) != tt.wantErr { 203 t.Errorf("GetAddrDescFromVout() error = %v, wantErr %v", err, tt.wantErr) 204 return 205 } 206 h := hex.EncodeToString(got) 207 if !reflect.DeepEqual(h, tt.want) { 208 t.Errorf("GetAddrDescFromVout() = %v, want %v", h, tt.want) 209 } 210 }) 211 } 212 } 213 214 func TestGetAddressesFromAddrDesc(t *testing.T) { 215 type args struct { 216 script string 217 } 218 tests := []struct { 219 name string 220 args args 221 want []string 222 want2 bool 223 wantErr bool 224 }{ 225 { 226 name: "P2PKH", 227 args: args{script: "76a914be027bf3eac907bd4ac8cb9c5293b6f37662722088ac"}, 228 want: []string{"1JKgN43B9SyLuZH19H5ECvr4KcfrbVHzZ6"}, 229 want2: true, 230 wantErr: false, 231 }, 232 { 233 name: "P2PK compressed", 234 args: args{script: "21020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9ac"}, 235 want: []string{"1P3rU1Nk1pmc2BiWC8dEy9bZa1ZbMp5jfg"}, 236 want2: false, 237 wantErr: false, 238 }, 239 { 240 name: "P2PK uncompressed", 241 args: args{script: "41041057356b91bfd3efeff5fc0fa8b865faafafb67bd653c5da2cd16ce15c7b86db0e622c8e1e135f68918a23601eb49208c1ac72c7b64a4ee99c396cf788da16ccac"}, 242 want: []string{"1HY6bKYhFH7HF3F48ikvziPHLrEWPGwXcE"}, 243 want2: false, 244 wantErr: false, 245 }, 246 { 247 name: "P2SH", 248 args: args{script: "a9140394b3cf9a44782c10105b93962daa8dba304d7f87"}, 249 want: []string{"321x69Cb9HZLWwAWGiUBT1U81r1zPLnEjL"}, 250 want2: true, 251 wantErr: false, 252 }, 253 { 254 name: "P2WPKH", 255 args: args{script: "00141c12afc6b2602607fdbc209f2a053c54ecd2c673"}, 256 want: []string{"bc1qrsf2l34jvqnq0lduyz0j5pfu2nkd93nnq0qggn"}, 257 want2: true, 258 wantErr: false, 259 }, 260 { 261 name: "P2WSH", 262 args: args{script: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29"}, 263 want: []string{"bc1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355sw5exgr"}, 264 want2: true, 265 wantErr: false, 266 }, 267 { 268 name: "OP_RETURN ascii", 269 args: args{script: "6a0461686f6a"}, 270 want: []string{"OP_RETURN (ahoj)"}, 271 want2: false, 272 wantErr: false, 273 }, 274 { 275 name: "OP_RETURN OP_PUSHDATA1 ascii", 276 args: args{script: "6a4c0b446c6f7568792074657874"}, 277 want: []string{"OP_RETURN (Dlouhy text)"}, 278 want2: false, 279 wantErr: false, 280 }, 281 { 282 name: "OP_RETURN OP_PUSHDATA1 utf8", 283 args: args{script: "6a31e5bfabe981a9e381ab4254434658e58f96e5bc95e3818ce381a7e3818de3828b5043e3818ce6acb2e38197e38184e38082"}, 284 want: []string{"OP_RETURN (快適にBTCFX取引ができるPCが欲しい。)"}, 285 want2: false, 286 wantErr: false, 287 }, 288 { 289 name: "OP_RETURN OP_PUSHDATA2 ascii", 290 args: args{script: "6a4dd7035765277265206e6f20737472616e6765727320746f206c6f76650a596f75206b6e6f77207468652072756c657320616e6420736f20646f20490a412066756c6c20636f6d6d69746d656e74277320776861742049276d207468696e6b696e67206f660a596f7520776f756c646e27742067657420746869732066726f6d20616e79206f74686572206775790a49206a7573742077616e6e612074656c6c20796f7520686f772049276d206665656c696e670a476f747461206d616b6520796f7520756e6465727374616e640a0a43484f5255530a4e6576657220676f6e6e61206769766520796f752075702c0a4e6576657220676f6e6e61206c657420796f7520646f776e0a4e6576657220676f6e6e612072756e2061726f756e6420616e642064657365727420796f750a4e6576657220676f6e6e61206d616b6520796f75206372792c0a4e6576657220676f6e6e612073617920676f6f646279650a4e6576657220676f6e6e612074656c6c2061206c696520616e64206875727420796f750a0a5765277665206b6e6f776e2065616368206f7468657220666f7220736f206c6f6e670a596f75722068656172742773206265656e20616368696e672062757420796f7527726520746f6f2073687920746f207361792069740a496e7369646520776520626f7468206b6e6f7720776861742773206265656e20676f696e67206f6e0a5765206b6e6f77207468652067616d6520616e6420776527726520676f6e6e6120706c61792069740a416e6420696620796f752061736b206d6520686f772049276d206665656c696e670a446f6e27742074656c6c206d6520796f7527726520746f6f20626c696e6420746f20736565202843484f525553290a0a43484f52555343484f5255530a284f6f68206769766520796f75207570290a284f6f68206769766520796f75207570290a284f6f6829206e6576657220676f6e6e6120676976652c206e6576657220676f6e6e6120676976650a286769766520796f75207570290a284f6f6829206e6576657220676f6e6e6120676976652c206e6576657220676f6e6e6120676976650a286769766520796f75207570290a0a5765277665206b6e6f776e2065616368206f7468657220666f7220736f206c6f6e670a596f75722068656172742773206265656e20616368696e672062757420796f7527726520746f6f2073687920746f207361792069740a496e7369646520776520626f7468206b6e6f7720776861742773206265656e20676f696e67206f6e0a5765206b6e6f77207468652067616d6520616e6420776527726520676f6e6e6120706c61792069742028544f2046524f4e54290a0a"}, 291 want: []string{`OP_RETURN (We're no strangers to love 292 You know the rules and so do I 293 A full commitment's what I'm thinking of 294 You wouldn't get this from any other guy 295 I just wanna tell you how I'm feeling 296 Gotta make you understand 297 298 CHORUS 299 Never gonna give you up, 300 Never gonna let you down 301 Never gonna run around and desert you 302 Never gonna make you cry, 303 Never gonna say goodbye 304 Never gonna tell a lie and hurt you 305 306 We've known each other for so long 307 Your heart's been aching but you're too shy to say it 308 Inside we both know what's been going on 309 We know the game and we're gonna play it 310 And if you ask me how I'm feeling 311 Don't tell me you're too blind to see (CHORUS) 312 313 CHORUSCHORUS 314 (Ooh give you up) 315 (Ooh give you up) 316 (Ooh) never gonna give, never gonna give 317 (give you up) 318 (Ooh) never gonna give, never gonna give 319 (give you up) 320 321 We've known each other for so long 322 Your heart's been aching but you're too shy to say it 323 Inside we both know what's been going on 324 We know the game and we're gonna play it (TO FRONT) 325 326 )`}, 327 want2: false, 328 wantErr: false, 329 }, 330 { 331 name: "OP_RETURN hex", 332 args: args{script: "6a072020f1686f6a20"}, 333 want: []string{"OP_RETURN 2020f1686f6a20"}, 334 want2: false, 335 wantErr: false, 336 }, 337 { 338 name: "OP_RETURN omni simple send tether", 339 args: args{script: "6a146f6d6e69000000000000001f00000709bb647351"}, 340 want: []string{"OMNI Simple Send: 77383.80022609 TetherUS (#31)"}, 341 want2: false, 342 wantErr: false, 343 }, 344 { 345 name: "OP_RETURN omni simple send not supported coin", 346 args: args{script: "6a146f6d6e69000000000000000300000709bb647351"}, 347 want: []string{"OP_RETURN 6f6d6e69000000000000000300000709bb647351"}, 348 want2: false, 349 wantErr: false, 350 }, 351 { 352 name: "OP_RETURN omni not supported version", 353 args: args{script: "6a146f6d6e69010000000000000300000709bb647351"}, 354 want: []string{"OP_RETURN 6f6d6e69010000000000000300000709bb647351"}, 355 want2: false, 356 wantErr: false, 357 }, 358 } 359 360 parser := NewBitcoinParser(GetChainParams("main"), &Configuration{}) 361 362 for _, tt := range tests { 363 t.Run(tt.name, func(t *testing.T) { 364 b, _ := hex.DecodeString(tt.args.script) 365 got, got2, err := parser.GetAddressesFromAddrDesc(b) 366 if (err != nil) != tt.wantErr { 367 t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr) 368 return 369 } 370 if !reflect.DeepEqual(got, tt.want) { 371 t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want) 372 } 373 if !reflect.DeepEqual(got2, tt.want2) { 374 t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2) 375 } 376 }) 377 } 378 } 379 380 func TestGetAddressesFromAddrDescTestnet(t *testing.T) { 381 type args struct { 382 script string 383 } 384 tests := []struct { 385 name string 386 args args 387 want []string 388 want2 bool 389 wantErr bool 390 }{ 391 { 392 name: "pubkeyhash", 393 args: args{script: "76a914912e2b234f941f30b18afbb4fa46171214bf66c888ac"}, 394 want: []string{"mtkbaiLiUH3fvGJeSzuN3kUgmJzqinLejJ"}, 395 want2: true, 396 wantErr: false, 397 }, 398 { 399 name: "pubkey compressed", 400 args: args{script: "2102a741071164b40b01c4ad28913c4aa2a1015cc5b064f0c802272552f17ae08750ac"}, 401 want: []string{"mkMe1fsfCWFext2qxf4bk3yiruBTvnici4"}, 402 want2: false, 403 wantErr: false, 404 }, 405 { 406 name: "pubkey uncompressed", 407 args: args{script: "41041057356b91bfd3efeff5fc0fa8b865faafafb67bd653c5da2cd16ce15c7b86db0e622c8e1e135f68918a23601eb49208c1ac72c7b64a4ee99c396cf788da16ccac"}, 408 want: []string{"mx43tNdg4JYY29ifrHjJpdbcCqqDGVSng5"}, 409 want2: false, 410 wantErr: false, 411 }, 412 { 413 name: "scripthash", 414 args: args{script: "a9141e6ec5a1d12912b396d77d98dcb000e91f517fa487"}, 415 want: []string{"2Mv28xcUJdFXBTfGMtja6fVBMCEbsH3r2AW"}, 416 want2: true, 417 wantErr: false, 418 }, 419 { 420 name: "witness_v0_keyhash", 421 args: args{script: "0014e064dc594fec224afcb4c368ec86cc7a07813c3e"}, 422 want: []string{"tb1qupjdck20as3y4l95cd5wepkv0grcz0p7d8rd5s"}, 423 want2: true, 424 wantErr: false, 425 }, 426 { 427 name: "witness_v0_scripthash", 428 args: args{script: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29"}, 429 want: []string{"tb1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355seu0fjv"}, 430 want2: true, 431 wantErr: false, 432 }, 433 { 434 name: "witness_v1_taproot", 435 args: args{script: "51200418a01027bc37daeaaa567096c7367aa5d27376b0a46f32c98e001da5b5e603"}, 436 want: []string{"tb1pqsv2qyp8hsma46422ecfd3ek02jayumkkzjx7vkf3cqpmfd4ucpsx0cc9h"}, 437 want2: true, 438 wantErr: false, 439 }, 440 { 441 name: "OP_RETURN ascii", 442 args: args{script: "6a0461686f6a"}, 443 want: []string{"OP_RETURN (ahoj)"}, 444 want2: false, 445 wantErr: false, 446 }, 447 } 448 449 parser := NewBitcoinParser(GetChainParams("test"), &Configuration{}) 450 451 for _, tt := range tests { 452 t.Run(tt.name, func(t *testing.T) { 453 b, _ := hex.DecodeString(tt.args.script) 454 got, got2, err := parser.GetAddressesFromAddrDesc(b) 455 if (err != nil) != tt.wantErr { 456 t.Errorf("TestGetAddressesFromAddrDesc_Testnet() error = %v, wantErr %v", err, tt.wantErr) 457 return 458 } 459 if !reflect.DeepEqual(got, tt.want) { 460 t.Errorf("TestGetAddressesFromAddrDesc_Testnet() = %v, want %v", got, tt.want) 461 } 462 if !reflect.DeepEqual(got2, tt.want2) { 463 t.Errorf("TestGetAddressesFromAddrDesc_Testnet() = %v, want %v", got2, tt.want2) 464 } 465 }) 466 } 467 } 468 469 var ( 470 testTx1, testTx2, testTx3 bchain.Tx 471 472 testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700" 473 testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000" 474 testTxPacked3 = "00003d818bfda9aa3e02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000" 475 ) 476 477 func init() { 478 testTx1 = bchain.Tx{ 479 Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700", 480 Blocktime: 1519053802, 481 Txid: "056e3d82e5ffd0e915fb9b62797d76263508c34fe3e5dbed30dd3e943930f204", 482 LockTime: 512115, 483 VSize: 189, 484 Version: 1, 485 Vin: []bchain.Vin{ 486 { 487 ScriptSig: bchain.ScriptSig{ 488 Hex: "4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80", 489 }, 490 Txid: "425fed43ba74e9205875eb934d5bcf7bf338f146f70d4002d94bf5cbc9229a7f", 491 Vout: 4, 492 Sequence: 4294967294, 493 }, 494 }, 495 Vout: []bchain.Vout{ 496 { 497 ValueSat: *big.NewInt(38812), 498 N: 0, 499 ScriptPubKey: bchain.ScriptPubKey{ 500 Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87", 501 Addresses: []string{ 502 "3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK", 503 }, 504 }, 505 }, 506 }, 507 } 508 509 testTx2 = bchain.Tx{ 510 Hex: "010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000", 511 Blocktime: 1235678901, 512 Txid: "474e6795760ebe81cb4023dc227e5a0efe340e1771c89a0035276361ed733de7", 513 LockTime: 0, 514 VSize: 166, 515 Version: 1, 516 Vin: []bchain.Vin{ 517 { 518 ScriptSig: bchain.ScriptSig{ 519 Hex: "160014550da1f5d25a9dae2eafd6902b4194c4c6500af6", 520 }, 521 Txid: "c13e32a4428e31f85d7aee4ec7344504b12e72aaffcbde0160200d2ac7f0649d", 522 Vout: 0, 523 Sequence: 4294967295, 524 }, 525 }, 526 Vout: []bchain.Vout{ 527 { 528 ValueSat: *big.NewInt(10000000), 529 N: 0, 530 ScriptPubKey: bchain.ScriptPubKey{ 531 Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887", 532 Addresses: []string{ 533 "2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu", 534 }, 535 }, 536 }, 537 { 538 ValueSat: *big.NewInt(920081157), 539 N: 1, 540 ScriptPubKey: bchain.ScriptPubKey{ 541 Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87", 542 Addresses: []string{ 543 "2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D", 544 }, 545 }, 546 }, 547 }, 548 } 549 550 testTx3 = bchain.Tx{ 551 Hex: "02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000", 552 Blocktime: 1607805599, 553 Txid: "24551a58a1d1fb89d7052e2bbac7cb69a7825ee1e39439befbec8c32148cf735", 554 LockTime: 15745, 555 VSize: 208, 556 Version: 2, 557 Vin: []bchain.Vin{ 558 { 559 ScriptSig: bchain.ScriptSig{ 560 Hex: "", 561 }, 562 Txid: "9d8b6a98d942ce077574fff2e5dd9e405ba75ed9fb126b3da1b07a859a99b1de", 563 Vout: 0, 564 Sequence: 4294967293, 565 }, 566 { 567 ScriptSig: bchain.ScriptSig{ 568 Hex: "", 569 }, 570 Txid: "98227ffe94726bc77c3c587e1e5375305beffb8e43a6eb75233b201e36d3d29f", 571 Vout: 0, 572 Sequence: 4294967293, 573 }, 574 }, 575 Vout: []bchain.Vout{ 576 { 577 ValueSat: *big.NewInt(100000), 578 N: 0, 579 ScriptPubKey: bchain.ScriptPubKey{ 580 Hex: "00148091746745464e7555c31e9a5afceac14a02978a", 581 Addresses: []string{ 582 "tb1qszghge69ge8824wrr6d94l82c99q99u2ccgv5w", 583 }, 584 }, 585 }, 586 { 587 ValueSat: *big.NewInt(1899751), 588 N: 1, 589 ScriptPubKey: bchain.ScriptPubKey{ 590 Hex: "0014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e", 591 Addresses: []string{ 592 "tb1q2e02nl6938f7qkapfxhxufth22lac2s792vsxp", 593 }, 594 }, 595 }, 596 }, 597 } 598 } 599 600 func TestPackTx(t *testing.T) { 601 type args struct { 602 tx bchain.Tx 603 height uint32 604 blockTime int64 605 parser *BitcoinParser 606 } 607 tests := []struct { 608 name string 609 args args 610 want string 611 wantErr bool 612 }{ 613 { 614 name: "btc-1", 615 args: args{ 616 tx: testTx1, 617 height: 123456, 618 blockTime: 1519053802, 619 parser: NewBitcoinParser(GetChainParams("main"), &Configuration{}), 620 }, 621 want: testTxPacked1, 622 wantErr: false, 623 }, 624 { 625 name: "testnet-1", 626 args: args{ 627 tx: testTx2, 628 height: 510234, 629 blockTime: 1235678901, 630 parser: NewBitcoinParser(GetChainParams("test"), &Configuration{}), 631 }, 632 want: testTxPacked2, 633 wantErr: false, 634 }, 635 { 636 name: "signet-1", 637 args: args{ 638 tx: testTx3, 639 height: 15745, 640 blockTime: 1607805599, 641 parser: NewBitcoinParser(GetChainParams("signet"), &Configuration{}), 642 }, 643 want: testTxPacked3, 644 wantErr: false, 645 }, 646 } 647 for _, tt := range tests { 648 t.Run(tt.name, func(t *testing.T) { 649 got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime) 650 if (err != nil) != tt.wantErr { 651 t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr) 652 return 653 } 654 h := hex.EncodeToString(got) 655 if !reflect.DeepEqual(h, tt.want) { 656 t.Errorf("packTx() = %v, want %v", h, tt.want) 657 } 658 }) 659 } 660 } 661 662 func TestUnpackTx(t *testing.T) { 663 type args struct { 664 packedTx string 665 parser *BitcoinParser 666 } 667 tests := []struct { 668 name string 669 args args 670 want *bchain.Tx 671 want1 uint32 672 wantErr bool 673 }{ 674 { 675 name: "btc-1", 676 args: args{ 677 packedTx: testTxPacked1, 678 parser: NewBitcoinParser(GetChainParams("main"), &Configuration{}), 679 }, 680 want: &testTx1, 681 want1: 123456, 682 wantErr: false, 683 }, 684 { 685 name: "testnet-1", 686 args: args{ 687 packedTx: testTxPacked2, 688 parser: NewBitcoinParser(GetChainParams("test"), &Configuration{}), 689 }, 690 want: &testTx2, 691 want1: 510234, 692 wantErr: false, 693 }, 694 { 695 name: "signet-1", 696 args: args{ 697 packedTx: testTxPacked3, 698 parser: NewBitcoinParser(GetChainParams("signet"), &Configuration{}), 699 }, 700 want: &testTx3, 701 want1: 15745, 702 wantErr: false, 703 }, 704 } 705 for _, tt := range tests { 706 t.Run(tt.name, func(t *testing.T) { 707 b, _ := hex.DecodeString(tt.args.packedTx) 708 got, got1, err := tt.args.parser.UnpackTx(b) 709 if (err != nil) != tt.wantErr { 710 t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) 711 return 712 } 713 // ignore witness unpacking 714 for i := range got.Vin { 715 got.Vin[i].Witness = nil 716 } 717 if !reflect.DeepEqual(got, tt.want) { 718 t.Errorf("unpackTx() got = %v, want %v", got, tt.want) 719 } 720 if got1 != tt.want1 { 721 t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1) 722 } 723 }) 724 } 725 } 726 727 func TestParseXpubDescriptors(t *testing.T) { 728 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) 729 btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198}) 730 tests := []struct { 731 name string 732 xpub string 733 parser *BitcoinParser 734 want *bchain.XpubDescriptor 735 wantErr bool 736 }{ 737 { 738 name: "tpub", 739 xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 740 parser: btcTestnetParser, 741 want: &bchain.XpubDescriptor{ 742 XpubDescriptor: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 743 Xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 744 Type: bchain.P2PKH, 745 Bip: "44", 746 ChangeIndexes: []uint32{0, 1}, 747 }, 748 }, 749 { 750 name: "tr(tpub)", 751 xpub: "tr(tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN)", 752 parser: btcTestnetParser, 753 want: &bchain.XpubDescriptor{ 754 XpubDescriptor: "tr(tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN)", 755 Xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 756 Type: bchain.P2TR, 757 Bip: "86", 758 ChangeIndexes: []uint32{0, 1}, 759 }, 760 }, 761 { 762 name: "tr([5c9e228d/86'/1'/0']tpubD/{0,1,2}/*)#4rqwxvej", 763 xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1,2}/*)#4rqwxvej", 764 parser: btcTestnetParser, 765 want: &bchain.XpubDescriptor{ 766 XpubDescriptor: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/{0,1,2}/*)#4rqwxvej", 767 Xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 768 Type: bchain.P2TR, 769 Bip: "86", 770 ChangeIndexes: []uint32{0, 1, 2}, 771 }, 772 }, 773 { 774 name: "tr([5c9e228d/86'/1'/0']tpubD/<0;1;2>/*)#4rqwxvej", 775 xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/<0;1;2>/*)#4rqwxvej", 776 parser: btcTestnetParser, 777 want: &bchain.XpubDescriptor{ 778 XpubDescriptor: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/<0;1;2>/*)#4rqwxvej", 779 Xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 780 Type: bchain.P2TR, 781 Bip: "86", 782 ChangeIndexes: []uint32{0, 1, 2}, 783 }, 784 }, 785 { 786 name: "tr([5c9e228d/86'/1'/0']tpubD/3/*)#4rqwxvej", 787 xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/3/*)#4rqwxvej", 788 parser: btcTestnetParser, 789 want: &bchain.XpubDescriptor{ 790 XpubDescriptor: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/3/*)#4rqwxvej", 791 Xpub: "tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN", 792 Type: bchain.P2TR, 793 Bip: "86", 794 ChangeIndexes: []uint32{3}, 795 }, 796 }, 797 { 798 name: "xpub", 799 xpub: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", 800 parser: btcMainParser, 801 want: &bchain.XpubDescriptor{ 802 XpubDescriptor: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", 803 Xpub: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", 804 Type: bchain.P2PKH, 805 Bip: "44", 806 ChangeIndexes: []uint32{0, 1}, 807 }, 808 }, 809 { 810 name: "ypub", 811 xpub: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", 812 parser: btcMainParser, 813 want: &bchain.XpubDescriptor{ 814 XpubDescriptor: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", 815 Xpub: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", 816 Type: bchain.P2SHWPKH, 817 Bip: "49", 818 ChangeIndexes: []uint32{0, 1}, 819 }, 820 }, 821 { 822 name: "zpub", 823 xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", 824 parser: btcMainParser, 825 want: &bchain.XpubDescriptor{ 826 XpubDescriptor: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", 827 Xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", 828 Type: bchain.P2WPKH, 829 Bip: "84", 830 ChangeIndexes: []uint32{0, 1}, 831 }, 832 }, 833 { 834 name: "sh(wpkh([5c9e228d/99'/0'/0']xpub/{122,123,4431}/*))", 835 xpub: "sh(wpkh([5c9e228d/99'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/{122,123,4431}/*))", 836 parser: btcMainParser, 837 want: &bchain.XpubDescriptor{ 838 XpubDescriptor: "sh(wpkh([5c9e228d/99'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/{122,123,4431}/*))", 839 Xpub: "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", 840 Type: bchain.P2SHWPKH, 841 Bip: "99", 842 ChangeIndexes: []uint32{122, 123, 4431}, 843 }, 844 }, 845 { 846 name: "sh(wpkh([5c9e228d/99'/0'/0']xpub/<122;123;4431>/*))", 847 xpub: "sh(wpkh([5c9e228d/99'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/<122;123;4431>/*))", 848 parser: btcMainParser, 849 want: &bchain.XpubDescriptor{ 850 XpubDescriptor: "sh(wpkh([5c9e228d/99'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/<122;123;4431>/*))", 851 Xpub: "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", 852 Type: bchain.P2SHWPKH, 853 Bip: "99", 854 ChangeIndexes: []uint32{122, 123, 4431}, 855 }, 856 }, 857 { 858 name: "pkh(xpub)", 859 xpub: "pkh(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ)", 860 parser: btcMainParser, 861 want: &bchain.XpubDescriptor{ 862 XpubDescriptor: "pkh(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ)", 863 Xpub: "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", 864 Type: bchain.P2PKH, 865 Bip: "44", 866 ChangeIndexes: []uint32{0, 1}, 867 }, 868 }, 869 { 870 name: "sh(wpkh(xpub))", 871 xpub: "sh(wpkh(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ))", 872 parser: btcMainParser, 873 want: &bchain.XpubDescriptor{ 874 XpubDescriptor: "sh(wpkh(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ))", 875 Xpub: "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", 876 Type: bchain.P2SHWPKH, 877 Bip: "49", 878 ChangeIndexes: []uint32{0, 1}, 879 }, 880 }, 881 { 882 name: "wpkh(xpub)", 883 xpub: "wpkh(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ)", 884 parser: btcMainParser, 885 want: &bchain.XpubDescriptor{ 886 XpubDescriptor: "wpkh(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ)", 887 Xpub: "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ", 888 Type: bchain.P2WPKH, 889 Bip: "84", 890 ChangeIndexes: []uint32{0, 1}, 891 }, 892 }, 893 { 894 name: "xxx(xpub) error - unknown output script", 895 xpub: "xxx(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ)", 896 parser: btcMainParser, 897 wantErr: true, 898 }, 899 { 900 name: "sh(wpkh([5c9e228d/99'/0'/0']xpub/{0,123,4431}/1)) error - * in index is mandatory", 901 xpub: "sh(wpkh([5c9e228d/99'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/{122,123,4431}/1))", 902 parser: btcMainParser, 903 wantErr: true, 904 }, 905 { 906 name: "sh(wpkh([5c9e228d/99'/0'/0']xpub/{0,123,4431}/1) error - path too long", 907 xpub: "sh(wpkh([5c9e228d/99'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/{122,123,4431}/1/*))", 908 parser: btcMainParser, 909 wantErr: true, 910 }, 911 } 912 for _, tt := range tests { 913 t.Run(tt.name, func(t *testing.T) { 914 got, err := tt.parser.ParseXpub(tt.xpub) 915 if (err != nil) != tt.wantErr { 916 t.Errorf("ParseXpub() error = %v, wantErr %v", err, tt.wantErr) 917 return 918 } 919 if err == nil { 920 if got.ExtKey == nil { 921 t.Errorf("ParseXpub() got nil ExtKey") 922 return 923 } 924 got.ExtKey = nil 925 if !reflect.DeepEqual(got, tt.want) { 926 t.Errorf("ParseXpub() = %+v, want %+v", got, tt.want) 927 } 928 } 929 }) 930 } 931 } 932 933 func TestDeriveAddressDescriptors(t *testing.T) { 934 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) 935 btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198}) 936 type args struct { 937 xpub string 938 change uint32 939 indexes []uint32 940 parser *BitcoinParser 941 } 942 tests := []struct { 943 name string 944 args args 945 want []string 946 wantErr bool 947 }{ 948 { 949 name: "m/86'/1'/0'", 950 args: args{ 951 xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/0/*)#4rqwxvej", 952 change: 0, 953 indexes: []uint32{0, 1, 10}, 954 parser: btcTestnetParser, 955 }, 956 want: []string{"tb1pswrqtykue8r89t9u4rprjs0gt4qzkdfuursfnvqaa3f2yql07zmq8s8a5u", "tb1p8tvmvsvhsee73rhym86wt435qrqm92psfsyhy6a3n5gw455znnpqm8wald", "tb1pqr4803xedptkvsr6ksed2m7fx780y3u8shnd0fqdupnc0w75262sl49kwz"}, 957 }, 958 { 959 name: "m/86'/0'/0'", 960 args: args{ 961 xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr", 962 change: 0, 963 indexes: []uint32{0, 1}, 964 parser: btcMainParser, 965 }, 966 want: []string{"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr", "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh"}, 967 }, 968 { 969 name: "m/86'/0'/0'/1", 970 args: args{ 971 xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr", 972 change: 1, 973 indexes: []uint32{0}, 974 parser: btcMainParser, 975 }, 976 want: []string{"bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7"}, 977 }, 978 { 979 name: "m/44'/0'/0'", 980 args: args{ 981 xpub: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", 982 change: 0, 983 indexes: []uint32{0, 1234}, 984 parser: btcMainParser, 985 }, 986 want: []string{"1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA", "1P9w11dXAmG3QBjKLAvCsek8izs1iR2iFi"}, 987 }, 988 { 989 name: "m/49'/0'/0'", 990 args: args{ 991 xpub: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", 992 change: 0, 993 indexes: []uint32{0, 1234}, 994 parser: btcMainParser, 995 }, 996 want: []string{"37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf", "367meFzJ9KqDLm9PX6U8Z8RdmkSNBuxX8T"}, 997 }, 998 { 999 name: "m/84'/0'/0'", 1000 args: args{ 1001 xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", 1002 change: 0, 1003 indexes: []uint32{0, 1234}, 1004 parser: btcMainParser, 1005 }, 1006 want: []string{"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "bc1q4nm6g46ujzyjaeusralaz2nfv2rf04jjfyamkw"}, 1007 }, 1008 } 1009 for _, tt := range tests { 1010 t.Run(tt.name, func(t *testing.T) { 1011 descriptor, err := tt.args.parser.ParseXpub(tt.args.xpub) 1012 if err != nil { 1013 t.Errorf("ParseXpub() error = %v", err) 1014 return 1015 } 1016 got, err := tt.args.parser.DeriveAddressDescriptors(descriptor, tt.args.change, tt.args.indexes) 1017 if (err != nil) != tt.wantErr { 1018 t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr) 1019 return 1020 } 1021 gotAddresses := make([]string, len(got)) 1022 for i, ad := range got { 1023 aa, _, err := tt.args.parser.GetAddressesFromAddrDesc(ad) 1024 if err != nil || len(aa) != 1 { 1025 t.Errorf("DeriveAddressDescriptorsFromTo() got incorrect address descriptor %v, error %v", ad, err) 1026 return 1027 } 1028 gotAddresses[i] = aa[0] 1029 } 1030 if !reflect.DeepEqual(gotAddresses, tt.want) { 1031 t.Errorf("DeriveAddressDescriptorsFromTo() = %v, want %v", gotAddresses, tt.want) 1032 } 1033 }) 1034 } 1035 } 1036 1037 func TestDeriveAddressDescriptorsFromTo(t *testing.T) { 1038 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) 1039 btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198}) 1040 type args struct { 1041 xpub string 1042 change uint32 1043 fromIndex uint32 1044 toIndex uint32 1045 parser *BitcoinParser 1046 } 1047 tests := []struct { 1048 name string 1049 args args 1050 want []string 1051 wantErr bool 1052 }{ 1053 { 1054 name: "m/44'/0'/0'", 1055 args: args{ 1056 xpub: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", 1057 change: 0, 1058 fromIndex: 0, 1059 toIndex: 1, 1060 parser: btcMainParser, 1061 }, 1062 want: []string{"1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"}, 1063 }, 1064 { 1065 name: "m/49'/0'/0'", 1066 args: args{ 1067 xpub: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", 1068 change: 0, 1069 fromIndex: 0, 1070 toIndex: 1, 1071 parser: btcMainParser, 1072 }, 1073 want: []string{"37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"}, 1074 }, 1075 { 1076 name: "m/84'/0'/0'", 1077 args: args{ 1078 xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", 1079 change: 0, 1080 fromIndex: 0, 1081 toIndex: 1, 1082 parser: btcMainParser, 1083 }, 1084 want: []string{"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"}, 1085 }, 1086 { 1087 name: "m/86'/0'/0'", 1088 args: args{ 1089 xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr", 1090 change: 0, 1091 fromIndex: 0, 1092 toIndex: 1, 1093 parser: btcMainParser, 1094 }, 1095 want: []string{"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"}, 1096 }, 1097 { 1098 name: "m/49'/1'/0'", 1099 args: args{ 1100 xpub: "upub5DR1Mg5nykixzYjFXWW5GghAU7dDqoPVJ2jrqFbL8sJ7Hs7jn69MP7KBnnmxn88GeZtnH8PRKV9w5MMSFX8AdEAoXY8Qd8BJPoXtpMeHMxJ", 1101 change: 0, 1102 fromIndex: 0, 1103 toIndex: 10, 1104 parser: btcTestnetParser, 1105 }, 1106 want: []string{"2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp", "2Mt7P2BAfE922zmfXrdcYTLyR7GUvbwSEns", "2N6aUMgQk8y1zvoq6FeWFyotyj75WY9BGsu", "2NA7tbZWM9BcRwBuebKSQe2xbhhF1paJwBM", "2N8RZMzvrUUnpLmvACX9ysmJ2MX3GK5jcQM", "2MvUUSiQZDSqyeSdofKX9KrSCio1nANPDTe", "2NBXaWu1HazjoUVgrXgcKNoBLhtkkD9Gmet", "2N791Ttf89tMVw2maj86E1Y3VgxD9Mc7PU7", "2NCJmwEq8GJm8t8GWWyBXAfpw7F2qZEVP5Y", "2NEgW71hWKer2XCSA8ZCC2VnWpB77L6bk68"}, 1107 }, 1108 } 1109 for _, tt := range tests { 1110 t.Run(tt.name, func(t *testing.T) { 1111 descriptor, err := tt.args.parser.ParseXpub(tt.args.xpub) 1112 if err != nil { 1113 t.Errorf("ParseXpub() error = %v", err) 1114 return 1115 } 1116 got, err := tt.args.parser.DeriveAddressDescriptorsFromTo(descriptor, tt.args.change, tt.args.fromIndex, tt.args.toIndex) 1117 if (err != nil) != tt.wantErr { 1118 t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr) 1119 return 1120 } 1121 gotAddresses := make([]string, len(got)) 1122 for i, ad := range got { 1123 aa, _, err := tt.args.parser.GetAddressesFromAddrDesc(ad) 1124 if err != nil || len(aa) != 1 { 1125 t.Errorf("DeriveAddressDescriptorsFromTo() got incorrect address descriptor %v, error %v", ad, err) 1126 return 1127 } 1128 gotAddresses[i] = aa[0] 1129 } 1130 if !reflect.DeepEqual(gotAddresses, tt.want) { 1131 t.Errorf("DeriveAddressDescriptorsFromTo() = %v, want %v", gotAddresses, tt.want) 1132 } 1133 }) 1134 } 1135 } 1136 1137 func BenchmarkDeriveAddressDescriptorsFromToXpub(b *testing.B) { 1138 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) 1139 for i := 0; i < b.N; i++ { 1140 descriptor, _ := btcMainParser.ParseXpub("xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj") 1141 btcMainParser.DeriveAddressDescriptorsFromTo(descriptor, 1, 0, 100) 1142 } 1143 } 1144 1145 func BenchmarkDeriveAddressDescriptorsFromToYpub(b *testing.B) { 1146 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) 1147 for i := 0; i < b.N; i++ { 1148 descriptor, _ := btcMainParser.ParseXpub("ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP") 1149 btcMainParser.DeriveAddressDescriptorsFromTo(descriptor, 1, 0, 100) 1150 } 1151 } 1152 1153 func BenchmarkDeriveAddressDescriptorsFromToZpub(b *testing.B) { 1154 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) 1155 for i := 0; i < b.N; i++ { 1156 descriptor, _ := btcMainParser.ParseXpub("zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs") 1157 btcMainParser.DeriveAddressDescriptorsFromTo(descriptor, 1, 0, 100) 1158 } 1159 } 1160 1161 func TestBitcoinParser_DerivationBasePath(t *testing.T) { 1162 btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518, Slip44: 0}) 1163 btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198, Slip44: 1}) 1164 zecMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, Slip44: 133}) 1165 type args struct { 1166 xpub string 1167 parser *BitcoinParser 1168 } 1169 tests := []struct { 1170 name string 1171 args args 1172 want string 1173 wantErr bool 1174 }{ 1175 { 1176 name: "m/86'/1'/0'", 1177 args: args{ 1178 xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/0/*)#4rqwxvej", 1179 parser: btcTestnetParser, 1180 }, 1181 want: "m/86'/1'/0'", 1182 }, 1183 { 1184 name: "m/86'/0'/0'", 1185 args: args{ 1186 xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr", 1187 parser: btcMainParser, 1188 }, 1189 want: "m/86'/0'/0'", 1190 }, 1191 { 1192 name: "m/84'/0'/0'", 1193 args: args{ 1194 xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", 1195 parser: btcMainParser, 1196 }, 1197 want: "m/84'/0'/0'", 1198 }, 1199 { 1200 name: "m/49'/0'/55 - not hardened account", 1201 args: args{ 1202 xpub: "ypub6XKbB5DJRAbW4TRJLp4uXQXG3ob5BtByXsNZFBjq9qcbzrczjVXfCz5cEo1SFDexmeWRnbCMDaRgaW4m9d2nBaa8FvUQCu3n9G1UBR8WhbT", 1203 parser: btcMainParser, 1204 }, 1205 want: "m/49'/0'/55", 1206 }, 1207 { 1208 name: "m/49'/0' - incomplete path, without account", 1209 args: args{ 1210 xpub: "ypub6UzM8PUqxcSoqC9gumfoiFhE8Qt84HbGpCD4eVJfJAojXTVtBxeddvTWJGJhGoaVBNJLmEgMdLXHgaLVJa4xEvk2tcokkdZhFdkxMLUE9sB", 1211 parser: btcMainParser, 1212 }, 1213 want: "unknown/0'", 1214 }, 1215 { 1216 name: "m/49'/1'/0'", 1217 args: args{ 1218 xpub: "upub5DR1Mg5nykixzYjFXWW5GghAU7dDqoPVJ2jrqFbL8sJ7Hs7jn69MP7KBnnmxn88GeZtnH8PRKV9w5MMSFX8AdEAoXY8Qd8BJPoXtpMeHMxJ", 1219 parser: btcTestnetParser, 1220 }, 1221 want: "m/49'/1'/0'", 1222 }, 1223 { 1224 name: "m/44'/133'/12'", 1225 args: args{ 1226 xpub: "xpub6CQdEahwhKRTLYpP6cyb7ZaGb3r4tVdyPX6dC1PfrNuByrCkWDgUkmpD28UdV9QccKgY1ZiAbGv1Fakcg2LxdFVSTNKHcjdRjqhjPK8Trkb", 1227 parser: zecMainParser, 1228 }, 1229 want: "m/44'/133'/12'", 1230 }, 1231 } 1232 for _, tt := range tests { 1233 t.Run(tt.name, func(t *testing.T) { 1234 descriptor, err := tt.args.parser.ParseXpub(tt.args.xpub) 1235 if err != nil { 1236 t.Errorf("ParseXpub() error = %v", err) 1237 return 1238 } 1239 got, err := tt.args.parser.DerivationBasePath(descriptor) 1240 if (err != nil) != tt.wantErr { 1241 t.Errorf("BitcoinParser.DerivationBasePath() error = %v, wantErr %v", err, tt.wantErr) 1242 return 1243 } 1244 if got != tt.want { 1245 t.Errorf("BitcoinParser.DerivationBasePath() = %v, want %v", got, tt.want) 1246 } 1247 }) 1248 } 1249 }