bech32_test.go raw

   1  // Copyright (c) 2017-2020 The btcsuite developers
   2  // Copyright (c) 2019 The Decred developers
   3  // Use of this source code is governed by an ISC
   4  // license that can be found in the LICENSE file.
   5  
   6  package bech32
   7  
   8  import (
   9  	"bytes"
  10  	"encoding/hex"
  11  	"errors"
  12  	"fmt"
  13  	"strings"
  14  	"testing"
  15  
  16  	"next.orly.dev/pkg/nostr/utils"
  17  )
  18  
  19  // TestBech32 tests whether decoding and re-encoding the valid BIP-173 test
  20  // vectors works and if decoding invalid test vectors fails for the correct
  21  // reason.
  22  func TestBech32(t *testing.T) {
  23  	tests := []struct {
  24  		str           string
  25  		expectedError error
  26  	}{
  27  		{"A12UEL5L", nil},
  28  		{"a12uel5l", nil},
  29  		{
  30  			"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
  31  			nil,
  32  		},
  33  		{"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", nil},
  34  		{
  35  			"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
  36  			nil,
  37  		},
  38  		{"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", nil},
  39  		{
  40  			"split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w",
  41  			ErrInvalidChecksum{
  42  				"2y9e3w", "lc445v",
  43  				"2y9e2w",
  44  			},
  45  		}, // invalid checksum
  46  		{
  47  			"s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p",
  48  			ErrInvalidCharacter(' '),
  49  		}, // invalid character (space) in hrp
  50  		{
  51  			"spl\x7Ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
  52  			ErrInvalidCharacter(127),
  53  		}, // invalid character (DEL) in hrp
  54  		{
  55  			"split1cheo2y9e2w",
  56  			ErrNonCharsetChar('o'),
  57  		}, // invalid character (o) in data part
  58  		{"split1a2y9w", ErrInvalidSeparatorIndex(5)}, // too short data part
  59  		{
  60  			"1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
  61  			ErrInvalidSeparatorIndex(0),
  62  		}, // empty hrp
  63  		{
  64  			"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
  65  			ErrInvalidLength(91),
  66  		}, // too long
  67  		// Additional test vectors used in bitcoin core
  68  		{" 1nwldj5", ErrInvalidCharacter(' ')},
  69  		{"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)},
  70  		{"\x801eym55h", ErrInvalidCharacter(0x80)},
  71  		{
  72  			"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",
  73  			ErrInvalidLength(91),
  74  		},
  75  		{"pzry9x0s0muk", ErrInvalidSeparatorIndex(-1)},
  76  		{"1pzry9x0s0muk", ErrInvalidSeparatorIndex(0)},
  77  		{"x1b4n0q5v", ErrNonCharsetChar(98)},
  78  		{"li1dgmt3", ErrInvalidSeparatorIndex(2)},
  79  		{"de1lg7wt\xff", ErrInvalidCharacter(0xff)},
  80  		{"A1G7SGD8", ErrInvalidChecksum{"2uel5l", "lqfn3a", "g7sgd8"}},
  81  		{"10a06t8", ErrInvalidLength(7)},
  82  		{"1qzzfhee", ErrInvalidSeparatorIndex(0)},
  83  		{"a12UEL5L", ErrMixedCase{}},
  84  		{"A12uEL5L", ErrMixedCase{}},
  85  	}
  86  	for i, test := range tests {
  87  		str := []byte(test.str)
  88  		hrp, decoded, err := Decode([]byte(str))
  89  		if !errors.Is(err, test.expectedError) {
  90  			t.Errorf(
  91  				"%d: expected decoding error %v "+
  92  					"instead got %v", i, test.expectedError, err,
  93  			)
  94  			continue
  95  		}
  96  		if err != nil {
  97  			// End test case here if a decoding error was expected.
  98  			continue
  99  		}
 100  		// Check that it encodes to the same string
 101  		encoded, err := Encode(hrp, decoded)
 102  		if err != nil {
 103  			t.Errorf("encoding failed: %v", err)
 104  		}
 105  		if !utils.FastEqual(encoded, bytes.ToLower([]byte(str))) {
 106  			t.Errorf(
 107  				"expected data to encode to %v, but got %v",
 108  				str, encoded,
 109  			)
 110  		}
 111  		// Flip a bit in the string an make sure it is caught.
 112  		pos := bytes.LastIndexAny(str, "1")
 113  		flipped := []byte(string(str[:pos+1]) + string(str[pos+1]^1) + string(str[pos+2:]))
 114  		_, _, err = Decode(flipped)
 115  		if err == nil {
 116  			t.Error("expected decoding to fail")
 117  		}
 118  	}
 119  }
 120  
 121  // TestBech32M tests that the following set of strings, based on the test
 122  // vectors in BIP-350 are either valid or invalid using the new bech32m
 123  // checksum algo. Some of these strings are similar to the set of above test
 124  // vectors, but end up with different checksums.
 125  func TestBech32M(t *testing.T) {
 126  	tests := []struct {
 127  		str           string
 128  		expectedError error
 129  	}{
 130  		{"A1LQFN3A", nil},
 131  		{"a1lqfn3a", nil},
 132  		{
 133  			"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
 134  			nil,
 135  		},
 136  		{"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", nil},
 137  		{
 138  			"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
 139  			nil,
 140  		},
 141  		{"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", nil},
 142  		{"?1v759aa", nil},
 143  		// Additional test vectors used in bitcoin core
 144  		{"\x201xj0phk", ErrInvalidCharacter('\x20')},
 145  		{"\x7f1g6xzxy", ErrInvalidCharacter('\x7f')},
 146  		{"\x801vctc34", ErrInvalidCharacter('\x80')},
 147  		{
 148  			"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4",
 149  			ErrInvalidLength(91),
 150  		},
 151  		{"qyrz8wqd2c9m", ErrInvalidSeparatorIndex(-1)},
 152  		{"1qyrz8wqd2c9m", ErrInvalidSeparatorIndex(0)},
 153  		{"y1b0jsk6g", ErrNonCharsetChar(98)},
 154  		{"lt1igcx5c0", ErrNonCharsetChar(105)},
 155  		{"in1muywd", ErrInvalidSeparatorIndex(2)},
 156  		{"mm1crxm3i", ErrNonCharsetChar(105)},
 157  		{"au1s5cgom", ErrNonCharsetChar(111)},
 158  		{"M1VUXWEZ", ErrInvalidChecksum{"mzl49c", "w70eq6", "vuxwez"}},
 159  		{"16plkw9", ErrInvalidLength(7)},
 160  		{"1p2gdwpf", ErrInvalidSeparatorIndex(0)},
 161  
 162  		{" 1nwldj5", ErrInvalidCharacter(' ')},
 163  		{"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)},
 164  		{"\x801eym55h", ErrInvalidCharacter(0x80)},
 165  	}
 166  	for i, test := range tests {
 167  		str := []byte(test.str)
 168  		hrp, decoded, err := Decode(str)
 169  		if test.expectedError != err {
 170  			t.Errorf(
 171  				"%d: (%v) expected decoding error %v "+
 172  					"instead got %v", i, str, test.expectedError,
 173  				err,
 174  			)
 175  			continue
 176  		}
 177  		if err != nil {
 178  			// End test case here if a decoding error was expected.
 179  			continue
 180  		}
 181  		// Check that it encodes to the same string, using bech32 m.
 182  		encoded, err := EncodeM(hrp, decoded)
 183  		if err != nil {
 184  			t.Errorf("encoding failed: %v", err)
 185  		}
 186  
 187  		if !utils.FastEqual(encoded, bytes.ToLower(str)) {
 188  			t.Errorf(
 189  				"expected data to encode to %v, but got %v",
 190  				str, encoded,
 191  			)
 192  		}
 193  		// Flip a bit in the string an make sure it is caught.
 194  		pos := bytes.LastIndexAny(str, "1")
 195  		flipped := []byte(string(str[:pos+1]) + string(str[pos+1]^1) + string(str[pos+2:]))
 196  		_, _, err = Decode(flipped)
 197  		if err == nil {
 198  			t.Error("expected decoding to fail")
 199  		}
 200  	}
 201  }
 202  
 203  // TestBech32DecodeGeneric tests that given a bech32 string, or a bech32m
 204  // string, the proper checksum version is returned so that callers can perform
 205  // segwit addr validation.
 206  func TestBech32DecodeGeneric(t *testing.T) {
 207  	tests := []struct {
 208  		str     string
 209  		version Version
 210  	}{
 211  		{"A1LQFN3A", VersionM},
 212  		{"a1lqfn3a", VersionM},
 213  		{
 214  			"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
 215  			VersionM,
 216  		},
 217  		{"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", VersionM},
 218  		{
 219  			"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
 220  			VersionM,
 221  		},
 222  		{
 223  			"split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
 224  			VersionM,
 225  		},
 226  		{"?1v759aa", VersionM},
 227  		{"A12UEL5L", Version0},
 228  		{"a12uel5l", Version0},
 229  		{
 230  			"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
 231  			Version0,
 232  		},
 233  		{"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", Version0},
 234  		{
 235  			"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
 236  			Version0,
 237  		},
 238  		{
 239  			"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
 240  			Version0,
 241  		},
 242  		{"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", Version0},
 243  		{
 244  			"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
 245  			Version0,
 246  		},
 247  		{
 248  			"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
 249  			VersionM,
 250  		},
 251  		{"BC1SW50QGDZ25J", VersionM},
 252  		{"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", VersionM},
 253  		{
 254  			"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
 255  			Version0,
 256  		},
 257  		{
 258  			"tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c",
 259  			VersionM,
 260  		},
 261  		{
 262  			"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
 263  			VersionM,
 264  		},
 265  	}
 266  	for i, test := range tests {
 267  		_, _, version, err := DecodeGeneric([]byte(test.str))
 268  		if err != nil {
 269  			t.Errorf(
 270  				"%d: (%v) unexpected error during "+
 271  					"decoding: %v", i, test.str, err,
 272  			)
 273  			continue
 274  		}
 275  		if version != test.version {
 276  			t.Errorf(
 277  				"(%v): invalid version: expected %v, got %v",
 278  				test.str, test.version, version,
 279  			)
 280  		}
 281  	}
 282  }
 283  
 284  // TestMixedCaseEncode ensures mixed case HRPs are converted to lowercase as
 285  // expected when encoding and that decoding the produced encoding when converted
 286  // to all uppercase produces the lowercase HRP and original data.
 287  func TestMixedCaseEncode(t *testing.T) {
 288  	tests := []struct {
 289  		name    string
 290  		hrp     string
 291  		data    string
 292  		encoded string
 293  	}{
 294  		{
 295  			name:    "all uppercase HRP with no data",
 296  			hrp:     "A",
 297  			data:    "",
 298  			encoded: "a12uel5l",
 299  		}, {
 300  			name:    "all uppercase HRP with data",
 301  			hrp:     "UPPERCASE",
 302  			data:    "787878",
 303  			encoded: "uppercase10pu8sss7kmp",
 304  		}, {
 305  			name:    "mixed case HRP even offsets uppercase",
 306  			hrp:     "AbCdEf",
 307  			data:    "00443214c74254b635cf84653a56d7c675be77df",
 308  			encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
 309  		}, {
 310  			name:    "mixed case HRP odd offsets uppercase ",
 311  			hrp:     "aBcDeF",
 312  			data:    "00443214c74254b635cf84653a56d7c675be77df",
 313  			encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
 314  		}, {
 315  			name:    "all lowercase HRP",
 316  			hrp:     "abcdef",
 317  			data:    "00443214c74254b635cf84653a56d7c675be77df",
 318  			encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
 319  		},
 320  	}
 321  	for _, test := range tests {
 322  		// Convert the text hex to bytes, convert those bytes from base256 to
 323  		// base32, then ensure the encoded result with the HRP provided in the
 324  		// test data is as expected.
 325  		data, err := hex.DecodeString(test.data)
 326  		if err != nil {
 327  			t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err)
 328  			continue
 329  		}
 330  		convertedData, err := ConvertBits(data, 8, 5, true)
 331  		if err != nil {
 332  			t.Errorf(
 333  				"%q: unexpected convert bits error: %v", test.name,
 334  				err,
 335  			)
 336  			continue
 337  		}
 338  		gotEncoded, err := Encode([]byte(test.hrp), convertedData)
 339  		if err != nil {
 340  			t.Errorf("%q: unexpected encode error: %v", test.name, err)
 341  			continue
 342  		}
 343  		if !utils.FastEqual(gotEncoded, []byte(test.encoded)) {
 344  			t.Errorf(
 345  				"%q: mismatched encoding -- got %q, want %q", test.name,
 346  				gotEncoded, test.encoded,
 347  			)
 348  			continue
 349  		}
 350  		// Ensure the decoding the expected lowercase encoding converted to all
 351  		// uppercase produces the lowercase HRP and original data.
 352  		gotHRP, gotData, err := Decode(bytes.ToUpper([]byte(test.encoded)))
 353  		if err != nil {
 354  			t.Errorf("%q: unexpected decode error: %v", test.name, err)
 355  			continue
 356  		}
 357  		wantHRP := strings.ToLower(test.hrp)
 358  		if !utils.FastEqual(gotHRP, []byte(wantHRP)) {
 359  			t.Errorf(
 360  				"%q: mismatched decoded HRP -- got %q, want %q", test.name,
 361  				gotHRP, wantHRP,
 362  			)
 363  			continue
 364  		}
 365  		convertedGotData, err := ConvertBits(gotData, 5, 8, false)
 366  		if err != nil {
 367  			t.Errorf(
 368  				"%q: unexpected convert bits error: %v", test.name,
 369  				err,
 370  			)
 371  			continue
 372  		}
 373  		if !utils.FastEqual(convertedGotData, data) {
 374  			t.Errorf(
 375  				"%q: mismatched data -- got %x, want %x", test.name,
 376  				convertedGotData, data,
 377  			)
 378  			continue
 379  		}
 380  	}
 381  }
 382  
 383  // TestCanDecodeUnlimtedBech32 tests whether decoding a large bech32 string works
 384  // when using the DecodeNoLimit version
 385  func TestCanDecodeUnlimtedBech32(t *testing.T) {
 386  	input := "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5kx0yd"
 387  	// Sanity check that an input of this length errors on regular Decode()
 388  	_, _, err := Decode([]byte(input))
 389  	if err == nil {
 390  		t.Fatalf("Test vector not appropriate")
 391  	}
 392  	// Try and decode it.
 393  	hrp, data, err := DecodeNoLimit([]byte(input))
 394  	if err != nil {
 395  		t.Fatalf(
 396  			"Expected decoding of large string to work. Got error: %v",
 397  			err,
 398  		)
 399  	}
 400  	// Verify data for correctness.
 401  	if !utils.FastEqual(hrp, []byte("1")) {
 402  		t.Fatalf("Unexpected hrp: %v", hrp)
 403  	}
 404  	decodedHex := fmt.Sprintf("%x", data)
 405  	expected := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000"
 406  	if decodedHex != expected {
 407  		t.Fatalf("Unexpected decoded data: %s", decodedHex)
 408  	}
 409  }
 410  
 411  // TestBech32Base256 ensures decoding and encoding various bech32, HRPs, and
 412  // data produces the expected results when using EncodeFromBase256 and
 413  // DecodeToBase256.  It includes tests for proper handling of case
 414  // manipulations.
 415  func TestBech32Base256(t *testing.T) {
 416  	tests := []struct {
 417  		name    string // test name
 418  		encoded string // bech32 string to decode
 419  		hrp     string // expected human-readable part
 420  		data    string // expected hex-encoded data
 421  		err     error  // expected error
 422  	}{
 423  		{
 424  			name:    "all uppercase, no data",
 425  			encoded: "A12UEL5L",
 426  			hrp:     "a",
 427  			data:    "",
 428  		}, {
 429  			name:    "long hrp with separator and excluded chars, no data",
 430  			encoded: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
 431  			hrp:     "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio",
 432  			data:    "",
 433  		}, {
 434  			name:    "6 char hrp with data with leading zero",
 435  			encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
 436  			hrp:     "abcdef",
 437  			data:    "00443214c74254b635cf84653a56d7c675be77df",
 438  		}, {
 439  			name:    "hrp same as separator and max length encoded string",
 440  			encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
 441  			hrp:     "1",
 442  			data:    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
 443  		}, {
 444  			name:    "5 char hrp with data chosen to produce human-readable data part",
 445  			encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
 446  			hrp:     "split",
 447  			data:    "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d",
 448  		}, {
 449  			name:    "same as previous but with checksum invalidated",
 450  			encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w",
 451  			err:     ErrInvalidChecksum{"2y9e3w", "lc445v", "2y9e2w"},
 452  		}, {
 453  			name:    "hrp with invalid character (space)",
 454  			encoded: "s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p",
 455  			err:     ErrInvalidCharacter(' '),
 456  		}, {
 457  			name:    "hrp with invalid character (DEL)",
 458  			encoded: "spl\x7ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
 459  			err:     ErrInvalidCharacter(127),
 460  		}, {
 461  			name:    "data part with invalid character (o)",
 462  			encoded: "split1cheo2y9e2w",
 463  			err:     ErrNonCharsetChar('o'),
 464  		}, {
 465  			name:    "data part too short",
 466  			encoded: "split1a2y9w",
 467  			err:     ErrInvalidSeparatorIndex(5),
 468  		}, {
 469  			name:    "empty hrp",
 470  			encoded: "1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
 471  			err:     ErrInvalidSeparatorIndex(0),
 472  		}, {
 473  			name:    "no separator",
 474  			encoded: "pzry9x0s0muk",
 475  			err:     ErrInvalidSeparatorIndex(-1),
 476  		}, {
 477  			name:    "too long by one char",
 478  			encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
 479  			err:     ErrInvalidLength(91),
 480  		}, {
 481  			name:    "invalid due to mixed case in hrp",
 482  			encoded: "aBcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
 483  			err:     ErrMixedCase{},
 484  		}, {
 485  			name:    "invalid due to mixed case in data part",
 486  			encoded: "abcdef1Qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
 487  			err:     ErrMixedCase{},
 488  		},
 489  	}
 490  	for _, test := range tests {
 491  		// Ensure the decode either produces an error or not as expected.
 492  		str := test.encoded
 493  		gotHRP, gotData, err := DecodeToBase256([]byte(str))
 494  		if test.err != err {
 495  			t.Errorf(
 496  				"%q: unexpected decode error -- got %v, want %v",
 497  				test.name, err, test.err,
 498  			)
 499  			continue
 500  		}
 501  		if err != nil {
 502  			// End test case here if a decoding error was expected.
 503  			continue
 504  		}
 505  		// Ensure the expected HRP and original data are as expected.
 506  		if !utils.FastEqual(gotHRP, []byte(test.hrp)) {
 507  			t.Errorf(
 508  				"%q: mismatched decoded HRP -- got %q, want %q", test.name,
 509  				gotHRP, test.hrp,
 510  			)
 511  			continue
 512  		}
 513  		data, err := hex.DecodeString(test.data)
 514  		if err != nil {
 515  			t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err)
 516  			continue
 517  		}
 518  		if !utils.FastEqual(gotData, data) {
 519  			t.Errorf(
 520  				"%q: mismatched data -- got %x, want %x", test.name,
 521  				gotData, data,
 522  			)
 523  			continue
 524  		}
 525  		// Encode the same data with the HRP converted to all uppercase and
 526  		// ensure the result is the lowercase version of the original encoded
 527  		// bech32 string.
 528  		gotEncoded, err := EncodeFromBase256(
 529  			bytes.ToUpper([]byte(test.hrp)), data,
 530  		)
 531  		if err != nil {
 532  			t.Errorf(
 533  				"%q: unexpected uppercase HRP encode error: %v", test.name,
 534  				err,
 535  			)
 536  		}
 537  		wantEncoded := bytes.ToLower([]byte(str))
 538  		if !utils.FastEqual(gotEncoded, wantEncoded) {
 539  			t.Errorf(
 540  				"%q: mismatched encoding -- got %q, want %q", test.name,
 541  				gotEncoded, wantEncoded,
 542  			)
 543  		}
 544  		// Encode the same data with the HRP converted to all lowercase and
 545  		// ensure the result is the lowercase version of the original encoded
 546  		// bech32 string.
 547  		gotEncoded, err = EncodeFromBase256(
 548  			bytes.ToLower([]byte(test.hrp)), data,
 549  		)
 550  		if err != nil {
 551  			t.Errorf(
 552  				"%q: unexpected lowercase HRP encode error: %v", test.name,
 553  				err,
 554  			)
 555  		}
 556  		if !utils.FastEqual(gotEncoded, wantEncoded) {
 557  			t.Errorf(
 558  				"%q: mismatched encoding -- got %q, want %q", test.name,
 559  				gotEncoded, wantEncoded,
 560  			)
 561  		}
 562  		// Encode the same data with the HRP converted to mixed upper and
 563  		// lowercase and ensure the result is the lowercase version of the
 564  		// original encoded bech32 string.
 565  		var mixedHRPBuilder bytes.Buffer
 566  		for i, r := range test.hrp {
 567  			if i%2 == 0 {
 568  				mixedHRPBuilder.WriteString(strings.ToUpper(string(r)))
 569  				continue
 570  			}
 571  			mixedHRPBuilder.WriteRune(r)
 572  		}
 573  		gotEncoded, err = EncodeFromBase256(mixedHRPBuilder.Bytes(), data)
 574  		if err != nil {
 575  			t.Errorf(
 576  				"%q: unexpected lowercase HRP encode error: %v", test.name,
 577  				err,
 578  			)
 579  		}
 580  		if !utils.FastEqual(gotEncoded, wantEncoded) {
 581  			t.Errorf(
 582  				"%q: mismatched encoding -- got %q, want %q", test.name,
 583  				gotEncoded, wantEncoded,
 584  			)
 585  		}
 586  		// Ensure a bit flip in the string is caught.
 587  		pos := strings.LastIndexAny(test.encoded, "1")
 588  		flipped := str[:pos+1] + string(str[pos+1]^1) + str[pos+2:]
 589  		_, _, err = DecodeToBase256([]byte(flipped))
 590  		if err == nil {
 591  			t.Error("expected decoding to fail")
 592  		}
 593  	}
 594  }
 595  
 596  // BenchmarkEncodeDecodeCycle performs a benchmark for a full encode/decode
 597  // cycle of a bech32 string. It also  reports the allocation count, which we
 598  // expect to be 2 for a fully optimized cycle.
 599  func BenchmarkEncodeDecodeCycle(b *testing.B) {
 600  	// Use a fixed, 49-byte raw data for testing.
 601  	inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1")
 602  	if err != nil {
 603  		b.Fatalf("failed to initialize input data: %v", err)
 604  	}
 605  	// Convert this into a 79-byte, base 32 byte slice.
 606  	base32Input, err := ConvertBits(inputData, 8, 5, true)
 607  	if err != nil {
 608  		b.Fatalf("failed to convert input to 32 bits-per-element: %v", err)
 609  	}
 610  	// Use a fixed hrp for the tests. This should generate an encoded bech32
 611  	// string of size 90 (the maximum allowed by BIP-173).
 612  	hrp := "bc"
 613  	// Begin the benchmark. Given that we test one roundtrip per iteration
 614  	// (that is, one Encode() and one Decode() operation), we expect at most
 615  	// 2 allocations per reported test op.
 616  	b.ReportAllocs()
 617  	b.ResetTimer()
 618  	for i := 0; i < b.N; i++ {
 619  		str, err := Encode([]byte(hrp), base32Input)
 620  		if err != nil {
 621  			b.Fatalf("failed to encode input: %v", err)
 622  		}
 623  		_, _, err = Decode(str)
 624  		if err != nil {
 625  			b.Fatalf("failed to decode string: %v", err)
 626  		}
 627  	}
 628  }
 629  
 630  // TestConvertBits tests whether base conversion works using TestConvertBits().
 631  func TestConvertBits(t *testing.T) {
 632  	tests := []struct {
 633  		input    string
 634  		output   string
 635  		fromBits uint8
 636  		toBits   uint8
 637  		pad      bool
 638  	}{
 639  		// Trivial empty conversions.
 640  		{"", "", 8, 5, false},
 641  		{"", "", 8, 5, true},
 642  		{"", "", 5, 8, false},
 643  		{"", "", 5, 8, true},
 644  		// Conversions of 0 value with/without padding.
 645  		{"00", "00", 8, 5, false},
 646  		{"00", "0000", 8, 5, true},
 647  		{"0000", "00", 5, 8, false},
 648  		{"0000", "0000", 5, 8, true},
 649  		// Testing when conversion ends exactly at the byte edge. This makes
 650  		// both padded and unpadded versions the same.
 651  		{"0000000000", "0000000000000000", 8, 5, false},
 652  		{"0000000000", "0000000000000000", 8, 5, true},
 653  		{"0000000000000000", "0000000000", 5, 8, false},
 654  		{"0000000000000000", "0000000000", 5, 8, true},
 655  		// Conversions of full byte sequences.
 656  		{"ffffff", "1f1f1f1f1e", 8, 5, true},
 657  		{"1f1f1f1f1e", "ffffff", 5, 8, false},
 658  		{"1f1f1f1f1e", "ffffff00", 5, 8, true},
 659  		// Sample random conversions.
 660  		{"c9ca", "190705", 8, 5, false},
 661  		{"c9ca", "19070500", 8, 5, true},
 662  		{"19070500", "c9ca", 5, 8, false},
 663  		{"19070500", "c9ca00", 5, 8, true},
 664  		// Test cases tested on TestConvertBitsFailures with their corresponding
 665  		// fixes.
 666  		{"ff", "1f1c", 8, 5, true},
 667  		{"1f1c10", "ff20", 5, 8, true},
 668  		// Large conversions.
 669  		{
 670  			"cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1",
 671  			"190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408",
 672  			8, 5, true,
 673  		},
 674  		{
 675  			"190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408",
 676  			"cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed100",
 677  			5, 8, true,
 678  		},
 679  	}
 680  	for i, tc := range tests {
 681  		input, err := hex.DecodeString(tc.input)
 682  		if err != nil {
 683  			t.Fatalf("invalid test input data: %v", err)
 684  		}
 685  		expected, err := hex.DecodeString(tc.output)
 686  		if err != nil {
 687  			t.Fatalf("invalid test output data: %v", err)
 688  		}
 689  		actual, err := ConvertBits(input, tc.fromBits, tc.toBits, tc.pad)
 690  		if err != nil {
 691  			t.Fatalf("test case %d failed: %v", i, err)
 692  		}
 693  		if !utils.FastEqual(actual, expected) {
 694  			t.Fatalf(
 695  				"test case %d has wrong output; expected=%x actual=%x",
 696  				i, expected, actual,
 697  			)
 698  		}
 699  	}
 700  }
 701  
 702  // TestConvertBitsFailures tests for the expected conversion failures of
 703  // ConvertBits().
 704  func TestConvertBitsFailures(t *testing.T) {
 705  	tests := []struct {
 706  		input    string
 707  		fromBits uint8
 708  		toBits   uint8
 709  		pad      bool
 710  		err      error
 711  	}{
 712  		// Not enough output bytes when not using padding.
 713  		{"ff", 8, 5, false, ErrInvalidIncompleteGroup{}},
 714  		{"1f1c10", 5, 8, false, ErrInvalidIncompleteGroup{}},
 715  		// Unsupported bit conversions.
 716  		{"", 0, 5, false, ErrInvalidBitGroups{}},
 717  		{"", 10, 5, false, ErrInvalidBitGroups{}},
 718  		{"", 5, 0, false, ErrInvalidBitGroups{}},
 719  		{"", 5, 10, false, ErrInvalidBitGroups{}},
 720  	}
 721  	for i, tc := range tests {
 722  		input, err := hex.DecodeString(tc.input)
 723  		if err != nil {
 724  			t.Fatalf("invalid test input data: %v", err)
 725  		}
 726  		_, err = ConvertBits(input, tc.fromBits, tc.toBits, tc.pad)
 727  		if err != tc.err {
 728  			t.Fatalf(
 729  				"test case %d failure: expected '%v' got '%v'", i,
 730  				tc.err, err,
 731  			)
 732  		}
 733  	}
 734  }
 735  
 736  // BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior
 737  // of ConvertBits when converting from a higher base into a lower base (e.g. 8
 738  // => 5).
 739  //
 740  // Only a single allocation is expected, which is used for the output array.
 741  func BenchmarkConvertBitsDown(b *testing.B) {
 742  	// Use a fixed, 49-byte raw data for testing.
 743  	inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1")
 744  	if err != nil {
 745  		b.Fatalf("failed to initialize input data: %v", err)
 746  	}
 747  	b.ReportAllocs()
 748  	b.ResetTimer()
 749  	for i := 0; i < b.N; i++ {
 750  		_, err := ConvertBits(inputData, 8, 5, true)
 751  		if err != nil {
 752  			b.Fatalf("error converting bits: %v", err)
 753  		}
 754  	}
 755  }
 756  
 757  // BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior
 758  // of ConvertBits when converting from a lower base into a higher base (e.g. 5
 759  // => 8).
 760  //
 761  // Only a single allocation is expected, which is used for the output array.
 762  func BenchmarkConvertBitsUp(b *testing.B) {
 763  	// Use a fixed, 79-byte raw data for testing.
 764  	inputData, err := hex.DecodeString("190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408")
 765  	if err != nil {
 766  		b.Fatalf("failed to initialize input data: %v", err)
 767  	}
 768  	b.ReportAllocs()
 769  	b.ResetTimer()
 770  	for i := 0; i < b.N; i++ {
 771  		_, err := ConvertBits(inputData, 8, 5, true)
 772  		if err != nil {
 773  			b.Fatalf("error converting bits: %v", err)
 774  		}
 775  	}
 776  }
 777