github.com/gagliardetto/solana-go@v1.11.0/keys_test.go (about) 1 // Copyright 2021 github.com/gagliardetto 2 // This file has been modified by github.com/gagliardetto 3 // 4 // Copyright 2020 dfuse Platform Inc. 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package solana 19 20 import ( 21 "encoding/binary" 22 "encoding/hex" 23 "errors" 24 "flag" 25 "testing" 26 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 ) 30 31 func TestPublicKeyFromBytes(t *testing.T) { 32 tests := []struct { 33 name string 34 inHex string 35 expected PublicKey 36 }{ 37 { 38 "empty", 39 "", 40 MustPublicKeyFromBase58("11111111111111111111111111111111"), 41 }, 42 { 43 "smaller than required", 44 "010203040506", 45 MustPublicKeyFromBase58("4wBqpZM9k69W87zdYXT2bRtLViWqTiJV3i2Kn9q7S6j"), 46 }, 47 { 48 "equal to 32 bytes", 49 "0102030405060102030405060102030405060102030405060102030405060101", 50 MustPublicKeyFromBase58("4wBqpZM9msxygzsdeLPq6Zw3LoiAxJk3GjtKPpqkcsi"), 51 }, 52 { 53 "longer than required", 54 "0102030405060102030405060102030405060102030405060102030405060101FFFFFFFFFF", 55 MustPublicKeyFromBase58("4wBqpZM9msxygzsdeLPq6Zw3LoiAxJk3GjtKPpqkcsi"), 56 }, 57 } 58 59 for _, test := range tests { 60 t.Run(test.name, func(t *testing.T) { 61 bytes, err := hex.DecodeString(test.inHex) 62 require.NoError(t, err) 63 64 actual := PublicKeyFromBytes(bytes) 65 assert.Equal(t, test.expected, actual, "%s != %s", test.expected, actual) 66 }) 67 } 68 } 69 70 func TestPublicKeyFromBase58(t *testing.T) { 71 tests := []struct { 72 name string 73 in string 74 expected PublicKey 75 expectedErr error 76 }{ 77 { 78 "hand crafted", 79 "SerumkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", 80 MustPublicKeyFromBase58("SerumkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), 81 nil, 82 }, 83 { 84 "hand crafted error", 85 "SerkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", 86 zeroPublicKey, 87 errors.New("invalid length, expected 32, got 30"), 88 }, 89 } 90 91 for _, test := range tests { 92 t.Run(test.name, func(t *testing.T) { 93 actual, err := PublicKeyFromBase58(test.in) 94 if test.expectedErr == nil { 95 require.NoError(t, err) 96 assert.Equal(t, test.expected, actual) 97 } else { 98 assert.Equal(t, test.expectedErr, err) 99 } 100 }) 101 } 102 } 103 104 func TestPrivateKeyFromSolanaKeygenFile(t *testing.T) { 105 tests := []struct { 106 inFile string 107 expected PrivateKey 108 expectedPub PublicKey 109 expectedErr error 110 }{ 111 { 112 "testdata/standard.solana-keygen.json", 113 MustPrivateKeyFromBase58("66cDvko73yAf8LYvFMM3r8vF5vJtkk7JKMgEKwkmBC86oHdq41C7i1a2vS3zE1yCcdLLk6VUatUb32ZzVjSBXtRs"), 114 MustPublicKeyFromBase58("F8UvVsKnzWyp2nF8aDcqvQ2GVcRpqT91WDsAtvBKCMt9"), 115 nil, 116 }, 117 } 118 119 for _, test := range tests { 120 t.Run(test.inFile, func(t *testing.T) { 121 actual, err := PrivateKeyFromSolanaKeygenFile(test.inFile) 122 if test.expectedErr == nil { 123 require.NoError(t, err) 124 assert.Equal(t, test.expected, actual) 125 assert.Equal(t, test.expectedPub, actual.PublicKey(), "%s != %s", test.expectedPub, actual.PublicKey()) 126 127 } else { 128 assert.Equal(t, test.expectedErr, err) 129 } 130 }) 131 } 132 } 133 134 func TestPublicKey_MarshalText(t *testing.T) { 135 keyString := "4wBqpZM9k69W87zdYXT2bRtLViWqTiJV3i2Kn9q7S6j" 136 keyParsed := MustPublicKeyFromBase58(keyString) 137 138 var key PublicKey 139 err := key.UnmarshalText([]byte(keyString)) 140 require.NoError(t, err) 141 142 assert.True(t, keyParsed.Equals(key)) 143 144 keyText, err := key.MarshalText() 145 require.NoError(t, err) 146 assert.Equal(t, []byte(keyString), keyText) 147 148 type IdentityToSlotsBlocks map[PublicKey][2]int64 149 150 var payload IdentityToSlotsBlocks 151 data := `{"` + keyString + `":[3,4]}` 152 err = json.Unmarshal([]byte(data), &payload) 153 require.NoError(t, err) 154 155 assert.Equal(t, 156 IdentityToSlotsBlocks{ 157 keyParsed: [2]int64{3, 4}, 158 }, 159 payload, 160 ) 161 } 162 163 func TestPublicKey_Flag(t *testing.T) { 164 flagSet := flag.NewFlagSet("", flag.ContinueOnError) 165 var key PublicKey 166 flagSet.Var(&key, "account", "Public key") 167 err := flagSet.Parse([]string{"--account", "7cVfgArCheMR6Cs4t6vz5rfnqd56vZq4ndaBrY5xkxXy"}) 168 require.NoError(t, err) 169 assert.Equal(t, PublicKey{ 170 0x62, 0x3d, 0xdd, 0x11, 0x7e, 0x7c, 0xc5, 0x62, 171 0xf6, 0x63, 0x15, 0x05, 0x25, 0x8c, 0xd1, 0xdc, 172 0xee, 0x81, 0x94, 0x9f, 0x8a, 0xfd, 0x1e, 0xa2, 173 0x94, 0xdc, 0x47, 0xbe, 0x6e, 0xcf, 0xf3, 0xa8, 174 }, key) 175 } 176 177 func TestPublicKeySlice(t *testing.T) { 178 { 179 slice := make(PublicKeySlice, 0) 180 require.False(t, slice.Has(BPFLoaderProgramID)) 181 182 slice.Append(BPFLoaderProgramID) 183 require.True(t, slice.Has(BPFLoaderProgramID)) 184 require.Len(t, slice, 1) 185 186 slice.UniqueAppend(BPFLoaderProgramID) 187 require.Len(t, slice, 1) 188 slice.Append(ConfigProgramID) 189 require.Len(t, slice, 2) 190 require.True(t, slice.Has(ConfigProgramID)) 191 } 192 193 { 194 slice := make(PublicKeySlice, 0) 195 { 196 require.Equal(t, []PublicKeySlice{}, slice.Split(1)) 197 } 198 slice.Append( 199 SysVarRentPubkey, 200 SysVarRewardsPubkey, 201 ) 202 { 203 require.Equal(t, 204 []PublicKeySlice{}, 205 slice.Split(0), 206 ) 207 require.Equal(t, 208 []PublicKeySlice{}, 209 slice.Split(-333), 210 ) 211 } 212 { 213 require.Equal(t, 214 []PublicKeySlice{ 215 {SysVarRentPubkey}, 216 {SysVarRewardsPubkey}, 217 }, 218 slice.Split(1), 219 ) 220 } 221 slice.Append( 222 BPFLoaderProgramID, 223 BPFLoaderDeprecatedProgramID, 224 FeatureProgramID, 225 ConfigProgramID, 226 StakeProgramID, 227 VoteProgramID, 228 SystemProgramID, 229 ) 230 { 231 require.Equal(t, 232 []PublicKeySlice{ 233 {SysVarRentPubkey}, 234 {SysVarRewardsPubkey}, 235 {BPFLoaderProgramID}, 236 {BPFLoaderDeprecatedProgramID}, 237 {FeatureProgramID}, 238 {ConfigProgramID}, 239 {StakeProgramID}, 240 {VoteProgramID}, 241 {SystemProgramID}, 242 }, 243 slice.Split(1), 244 ) 245 } 246 { 247 require.Equal(t, 248 []PublicKeySlice{ 249 {SysVarRentPubkey, SysVarRewardsPubkey}, 250 {BPFLoaderProgramID, BPFLoaderDeprecatedProgramID}, 251 {FeatureProgramID, ConfigProgramID}, 252 {StakeProgramID, VoteProgramID}, 253 {SystemProgramID}, 254 }, 255 slice.Split(2), 256 ) 257 } 258 } 259 } 260 261 func TestGetAddedRemovedPubkeys(t *testing.T) { 262 { 263 previous := PublicKeySlice{} 264 next := PublicKeySlice{BPFLoaderProgramID} 265 266 added, removed := GetAddedRemovedPubkeys(previous, next) 267 require.Equal(t, 268 PublicKeySlice{BPFLoaderProgramID}, 269 added, 270 ) 271 require.Equal(t, 272 PublicKeySlice{}, 273 removed, 274 ) 275 } 276 { 277 previous := PublicKeySlice{ 278 SysVarClockPubkey, 279 SysVarEpochSchedulePubkey, 280 SysVarFeesPubkey, 281 SysVarInstructionsPubkey, 282 SysVarRecentBlockHashesPubkey, 283 } 284 next := PublicKeySlice{ 285 SysVarClockPubkey, 286 SysVarEpochSchedulePubkey, 287 SysVarFeesPubkey, 288 SysVarInstructionsPubkey, 289 SysVarRecentBlockHashesPubkey, 290 } 291 292 added, removed := GetAddedRemovedPubkeys(previous, next) 293 require.Equal(t, 294 PublicKeySlice{}, 295 added, 296 ) 297 require.Equal(t, 298 PublicKeySlice{}, 299 removed, 300 ) 301 } 302 { 303 previous := PublicKeySlice{ 304 SysVarClockPubkey, 305 SysVarEpochSchedulePubkey, 306 SysVarFeesPubkey, 307 SysVarInstructionsPubkey, 308 SysVarRecentBlockHashesPubkey, 309 } 310 next := PublicKeySlice{ 311 SysVarEpochSchedulePubkey, 312 SysVarFeesPubkey, 313 SysVarInstructionsPubkey, 314 SysVarRecentBlockHashesPubkey, 315 ConfigProgramID, 316 } 317 318 added, removed := GetAddedRemovedPubkeys(previous, next) 319 require.Equal(t, 320 PublicKeySlice{ConfigProgramID}, 321 added, 322 ) 323 require.Equal(t, 324 PublicKeySlice{SysVarClockPubkey}, 325 removed, 326 ) 327 } 328 } 329 330 func TestGetAddedRemoved(t *testing.T) { 331 { 332 previous := PublicKeySlice{} 333 next := PublicKeySlice{BPFLoaderProgramID} 334 335 added, removed := previous.GetAddedRemoved(next) 336 require.Equal(t, 337 PublicKeySlice{BPFLoaderProgramID}, 338 added, 339 ) 340 require.Equal(t, 341 PublicKeySlice{}, 342 removed, 343 ) 344 } 345 { 346 previous := PublicKeySlice{ 347 SysVarClockPubkey, 348 SysVarEpochSchedulePubkey, 349 SysVarFeesPubkey, 350 SysVarInstructionsPubkey, 351 SysVarRecentBlockHashesPubkey, 352 } 353 next := PublicKeySlice{ 354 SysVarClockPubkey, 355 SysVarEpochSchedulePubkey, 356 SysVarFeesPubkey, 357 SysVarInstructionsPubkey, 358 SysVarRecentBlockHashesPubkey, 359 } 360 361 added, removed := previous.GetAddedRemoved(next) 362 require.Equal(t, 363 PublicKeySlice{}, 364 added, 365 ) 366 require.Equal(t, 367 PublicKeySlice{}, 368 removed, 369 ) 370 } 371 { 372 previous := PublicKeySlice{ 373 SysVarClockPubkey, 374 SysVarEpochSchedulePubkey, 375 SysVarFeesPubkey, 376 SysVarInstructionsPubkey, 377 SysVarRecentBlockHashesPubkey, 378 } 379 next := PublicKeySlice{ 380 SysVarEpochSchedulePubkey, 381 SysVarFeesPubkey, 382 SysVarInstructionsPubkey, 383 SysVarRecentBlockHashesPubkey, 384 ConfigProgramID, 385 } 386 387 added, removed := previous.GetAddedRemoved(next) 388 require.Equal(t, 389 PublicKeySlice{ConfigProgramID}, 390 added, 391 ) 392 require.Equal(t, 393 PublicKeySlice{SysVarClockPubkey}, 394 removed, 395 ) 396 } 397 } 398 399 func TestIsNativeProgramID(t *testing.T) { 400 require.True(t, isNativeProgramID(ConfigProgramID)) 401 } 402 403 func TestCreateWithSeed(t *testing.T) { 404 { 405 got, err := CreateWithSeed(PublicKey{}, "limber chicken: 4/45", PublicKey{}) 406 require.NoError(t, err) 407 require.True(t, got.Equals(MustPublicKeyFromBase58("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"))) 408 } 409 } 410 411 func TestCreateProgramAddressFromRust(t *testing.T) { 412 // Ported from https://github.com/solana-labs/solana/blob/f32216588dfdbc7a7160c26331ce657a90f95ae7/sdk/program/src/pubkey.rs#L636 413 program_id := MustPublicKeyFromBase58("BPFLoaderUpgradeab1e11111111111111111111111") 414 public_key := MustPublicKeyFromBase58("SeedPubey1111111111111111111111111111111111") 415 416 { 417 got, err := CreateProgramAddress([][]byte{ 418 {}, 419 {1}, 420 }, 421 program_id, 422 ) 423 require.NoError(t, err) 424 require.True(t, got.Equals(MustPublicKeyFromBase58("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"))) 425 } 426 427 { 428 got, err := CreateProgramAddress([][]byte{ 429 []byte("☉"), 430 {0}, 431 }, 432 program_id, 433 ) 434 require.NoError(t, err) 435 require.True(t, got.Equals(MustPublicKeyFromBase58("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"))) 436 } 437 438 { 439 got, err := CreateProgramAddress([][]byte{ 440 []byte("Talking"), 441 []byte("Squirrels"), 442 }, 443 program_id, 444 ) 445 require.NoError(t, err) 446 require.True(t, got.Equals(MustPublicKeyFromBase58("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"))) 447 } 448 449 { 450 got, err := CreateProgramAddress([][]byte{ 451 public_key[:], 452 {1}, 453 }, 454 program_id, 455 ) 456 require.NoError(t, err) 457 require.True(t, got.Equals(MustPublicKeyFromBase58("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"))) 458 } 459 } 460 461 func TestCreateProgramAddressFromTypescript(t *testing.T) { 462 t.Run( 463 "createProgramAddress", 464 // Ported from https://github.com/solana-labs/solana-web3.js/blob/168d5e088edd48f9f0c1a877e888592ca4cfdf38/test/publickey.test.ts#L113 465 func(t *testing.T) { 466 program_id := MustPublicKeyFromBase58("BPFLoader1111111111111111111111111111111111") 467 public_key := MustPublicKeyFromBase58("SeedPubey1111111111111111111111111111111111") 468 469 { 470 programAddress, err := CreateProgramAddress([][]byte{ 471 []byte(""), 472 {1}, 473 }, 474 program_id, 475 ) 476 require.NoError(t, err) 477 require.True(t, programAddress.Equals(MustPublicKeyFromBase58("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"))) 478 } 479 { 480 programAddress, err := CreateProgramAddress([][]byte{ 481 []byte("☉"), 482 }, 483 program_id, 484 ) 485 require.NoError(t, err) 486 require.True(t, programAddress.Equals(MustPublicKeyFromBase58("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"))) 487 } 488 { 489 programAddress, err := CreateProgramAddress([][]byte{ 490 []byte("Talking"), 491 []byte("Squirrels"), 492 }, 493 program_id, 494 ) 495 require.NoError(t, err) 496 require.True(t, programAddress.Equals(MustPublicKeyFromBase58("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"))) 497 } 498 { 499 programAddress, err := CreateProgramAddress([][]byte{ 500 public_key[:], 501 }, 502 program_id, 503 ) 504 require.NoError(t, err) 505 require.True(t, programAddress.Equals(MustPublicKeyFromBase58("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"))) 506 507 { 508 programAddress2, err := CreateProgramAddress([][]byte{ 509 []byte("Talking"), 510 }, 511 program_id, 512 ) 513 require.NoError(t, err) 514 require.False(t, programAddress.Equals(programAddress2)) 515 } 516 } 517 { 518 _, err := CreateProgramAddress([][]byte{ 519 make([]byte, MaxSeedLength+1), 520 }, 521 program_id, 522 ) 523 require.EqualError(t, err, ErrMaxSeedLengthExceeded.Error()) 524 } 525 { 526 bn := make([]byte, 8) 527 binary.LittleEndian.PutUint64(bn, 2) 528 programAddress, err := CreateProgramAddress([][]byte{ 529 MustPublicKeyFromBase58("H4snTKK9adiU15gP22ErfZYtro3aqR9BTMXiH3AwiUTQ").Bytes(), 530 bn, 531 }, 532 MustPublicKeyFromBase58("4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn"), 533 ) 534 require.NoError(t, err) 535 require.True(t, programAddress.Equals(MustPublicKeyFromBase58("12rqwuEgBYiGhBrDJStCiqEtzQpTTiZbh7teNVLuYcFA"))) 536 } 537 }, 538 ) 539 540 t.Run( 541 "findProgramAddress", 542 // Ported from https://github.com/solana-labs/solana-web3.js/blob/168d5e088edd48f9f0c1a877e888592ca4cfdf38/test/publickey.test.ts#L194 543 func(t *testing.T) { 544 programId := MustPublicKeyFromBase58("BPFLoader1111111111111111111111111111111111") 545 546 programAddress, nonce, err := FindProgramAddress( 547 [][]byte{ 548 []byte(""), 549 }, 550 programId, 551 ) 552 require.NoError(t, err) 553 554 { 555 got, err := CreateProgramAddress([][]byte{ 556 []byte(""), 557 {nonce}, 558 }, 559 programId, 560 ) 561 require.NoError(t, err) 562 require.True(t, programAddress.Equals(got)) 563 } 564 }, 565 ) 566 567 t.Run( 568 "isOnCurve", 569 // Ported from https://github.com/solana-labs/solana-web3.js/blob/168d5e088edd48f9f0c1a877e888592ca4cfdf38/test/publickey.test.ts#L212 570 func(t *testing.T) { 571 onCurve := NewWallet().PublicKey() 572 require.True(t, onCurve.IsOnCurve()) 573 574 // A program address, yanked from one of the above tests. This is a pretty 575 // poor test vector since it was created by the same code it is testing. 576 // Unfortunately, I've been unable to find a golden negative example input 577 // for curve25519 point decompression :/ 578 offCurve := MustPublicKeyFromBase58("12rqwuEgBYiGhBrDJStCiqEtzQpTTiZbh7teNVLuYcFA") 579 require.False(t, offCurve.IsOnCurve()) 580 }, 581 ) 582 } 583 584 // https://github.com/solana-labs/solana/blob/216983c50e0a618facc39aa07472ba6d23f1b33a/sdk/program/src/pubkey.rs#L590 585 func TestFindProgramAddress(t *testing.T) { 586 for i := 0; i < 1_000; i++ { 587 588 program_id := NewWallet().PrivateKey.PublicKey() 589 address, bump_seed, err := FindProgramAddress( 590 [][]byte{ 591 []byte("Lil'"), 592 []byte("Bits"), 593 }, 594 program_id, 595 ) 596 require.NoError(t, err) 597 598 got, err := CreateProgramAddress( 599 [][]byte{ 600 []byte("Lil'"), 601 []byte("Bits"), 602 {bump_seed}, 603 }, 604 program_id, 605 ) 606 require.NoError(t, err) 607 require.Equal(t, address, got) 608 } 609 } 610 611 func TestFindTokenMetadataAddress(t *testing.T) { 612 // Zuuper Grapes (TOILET) 613 // https://solscan.io/token/77K8mr457qxUSSNSfi4sSj5euP8DyuJJWHAUQVW8QCp3 614 mint := MustPublicKeyFromBase58("77K8mr457qxUSSNSfi4sSj5euP8DyuJJWHAUQVW8QCp3") 615 metadataPDA, bumpSeed, err := FindTokenMetadataAddress(mint) 616 require.NoError(t, err) 617 // https://solscan.io/account/GfihrEYCPrvUyrMyMQPdhGEStxa9nKEK2Wfn9iK4AZq2 618 assert.Equal(t, metadataPDA, MustPublicKeyFromBase58("GfihrEYCPrvUyrMyMQPdhGEStxa9nKEK2Wfn9iK4AZq2")) 619 assert.Equal(t, bumpSeed, uint8(0xfd)) 620 }