|
| 1 | +package bip322 |
| 2 | + |
| 3 | +import ( |
| 4 | + mrand "math/rand/v2" |
| 5 | + "strings" |
| 6 | + "testing" |
| 7 | + "unicode/utf8" |
| 8 | + |
| 9 | + "github.com/btcsuite/btcd/btcec/v2" |
| 10 | + "github.com/stretchr/testify/require" |
| 11 | +) |
| 12 | + |
| 13 | +// randomUTF8String returns a string of n randomly-chosen valid UTF-8 runes. |
| 14 | +func randomUTF8String(n int) string { |
| 15 | + runes := make([]rune, n) |
| 16 | + for i := range runes { |
| 17 | + runes[i] = randomRune() |
| 18 | + } |
| 19 | + return string(runes) |
| 20 | +} |
| 21 | + |
| 22 | +// randomRune returns a random valid Unicode scalar value. It rejects the |
| 23 | +// surrogate range (U+D800–U+DFFF) and anything above U+10FFFF, both of which |
| 24 | +// cannot be legally encoded as UTF-8. |
| 25 | +func randomRune() rune { |
| 26 | + for { |
| 27 | + // Random value in [0, 0x10FFFF]. |
| 28 | + r := rune(mrand.Int64N(utf8.MaxRune + 1)) |
| 29 | + if utf8.ValidRune(r) { |
| 30 | + return r |
| 31 | + } |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +// TestSign tests that a valid signature can be created for all supported |
| 36 | +// single-key address types. |
| 37 | +func TestSign(t *testing.T) { |
| 38 | + for range 100 { |
| 39 | + randString := randomUTF8String(256) |
| 40 | + |
| 41 | + randPrivateKey, err := btcec.NewPrivateKey() |
| 42 | + require.NoError(t, err) |
| 43 | + |
| 44 | + // Sign for a P2TR address. |
| 45 | + sigP2TR, err := SignP2TR(randString, randPrivateKey) |
| 46 | + require.NoError(t, err) |
| 47 | + require.Contains(t, sigP2TR, PrefixSimple) |
| 48 | + require.True(t, strings.HasPrefix(sigP2TR, PrefixSimple)) |
| 49 | + |
| 50 | + scriptP2TR, err := payToTaprootScript(randPrivateKey) |
| 51 | + require.NoError(t, err) |
| 52 | + |
| 53 | + valid, _, err := verifyMessageForChallenge( |
| 54 | + []byte(randString), scriptP2TR, sigP2TR, |
| 55 | + ) |
| 56 | + require.NoError(t, err) |
| 57 | + require.True(t, valid) |
| 58 | + |
| 59 | + // Sign for a P2WPKH address. |
| 60 | + sigP2WPKH, err := SignP2WPKH(randString, randPrivateKey) |
| 61 | + require.NoError(t, err) |
| 62 | + require.Contains(t, sigP2WPKH, PrefixSimple) |
| 63 | + require.True(t, strings.HasPrefix(sigP2WPKH, PrefixSimple)) |
| 64 | + |
| 65 | + scriptP2WPKH, err := payToWitnessPubKeyHashScript( |
| 66 | + randPrivateKey, |
| 67 | + ) |
| 68 | + require.NoError(t, err) |
| 69 | + |
| 70 | + valid, _, err = verifyMessageForChallenge( |
| 71 | + []byte(randString), scriptP2WPKH, sigP2WPKH, |
| 72 | + ) |
| 73 | + require.NoError(t, err) |
| 74 | + require.True(t, valid) |
| 75 | + |
| 76 | + // Sign for a NP2WPKH address. |
| 77 | + sigNP2WPKH, err := SignNestedP2WPKH(randString, randPrivateKey) |
| 78 | + require.NoError(t, err) |
| 79 | + require.Contains(t, sigNP2WPKH, PrefixFull) |
| 80 | + require.True(t, strings.HasPrefix(sigNP2WPKH, PrefixFull)) |
| 81 | + |
| 82 | + scriptNP2WPKH, _, err := payToNestedWitnessPubKeyHashScript( |
| 83 | + randPrivateKey, |
| 84 | + ) |
| 85 | + require.NoError(t, err) |
| 86 | + |
| 87 | + valid, _, err = verifyMessageForChallenge( |
| 88 | + []byte(randString), scriptNP2WPKH, sigNP2WPKH, |
| 89 | + ) |
| 90 | + require.NoError(t, err) |
| 91 | + require.True(t, valid) |
| 92 | + |
| 93 | + // Sign for a P2PKH address. |
| 94 | + sigP2PKH, err := SignP2PKH(randString, randPrivateKey) |
| 95 | + require.NoError(t, err) |
| 96 | + require.Contains(t, sigP2PKH, PrefixFull) |
| 97 | + require.True(t, strings.HasPrefix(sigP2PKH, PrefixFull)) |
| 98 | + |
| 99 | + scriptP2PKH, err := payToPubKeyHashScript(randPrivateKey) |
| 100 | + require.NoError(t, err) |
| 101 | + |
| 102 | + valid, _, err = verifyMessageForChallenge( |
| 103 | + []byte(randString), scriptP2PKH, sigP2PKH, |
| 104 | + ) |
| 105 | + require.NoError(t, err) |
| 106 | + require.True(t, valid) |
| 107 | + } |
| 108 | +} |
0 commit comments