Skip to content

Base58: protect alphabet from mutation - #4374

Open
schildbach wants to merge 1 commit into
bitcoinj:masterfrom
schildbach:base58-immutable-alphabet
Open

schildbach wants to merge 1 commit into
bitcoinj:masterfrom
schildbach:base58-immutable-alphabet

Conversation

@schildbach

Copy link
Copy Markdown
Member

Make the constant ALPHABET private and provide an accessor alphabet() to use instead.

Add tests to verify the alphabet cannot be mutated any more.

Make the constant `ALPHABET` private and provide an accessor `alphabet()`
to use instead.

Add tests to verify the alphabet cannot be mutated any more.
@schildbach schildbach added this to the 0.18 milestone Sep 12, 2026

@msgilligan msgilligan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to return an (immutable/unmodifiable) List I have some requested changes.

Alternatively, we could return a String which is immutable. We should also be able to use a String internally. I'll make a separate PR to explore this option.

return CharBuffer.wrap(ALPHABET)
.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.toList());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we replace Collectors.toList() with our utility StreamUtils.toUnmodifiableList() the returned list will be unmodifiable and will throw UnsupportedOperationException if modification is attempted.

alphabet1.add(0, '₿');
List<Character> alphabet2 = Base58.alphabet();
assertEquals(previousChar, alphabet2.get(0).charValue());
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If StreamUtils.toUnmodifiableList() is used as suggested above, this test can use assertThrows to test for unmodifiability.

@msgilligan

Copy link
Copy Markdown
Member

I'll make a separate PR to explore [using String].

See PR #4378

@schildbach

Copy link
Copy Markdown
Member Author

Alternatively, we could return a String which is immutable.

For the API method alphabet(), I'd rather not return a String. An alphabet is a collection of characters, not a String.

For the internal storage, I'm more relaxed about what type to use.

@msgilligan

msgilligan commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Alternatively, we could return a String which is immutable.

For the API method alphabet(), I'd rather not return a String. An alphabet is a collection of characters, not a String.

An alphabet is an ordered collection/sequence of characters. So is String which implements CharSequence which is a read-only sequence of char values.

Yes, Strings are often serialized/output in their (ordered) entirety, but they are still a collection/sequence of characters.

String is an immutable, built-in type that does everything we need. We should use it. Using a generic List<Character> is very inefficient (although in a far-distant Valhalla future it may eventually be flattened to the equivalent of a char[].)

For the internal storage, I'm more relaxed about what type to use.

We could make ALPHABET private and provide an alphabet() accessor (see 15b2d30.) And the accessor could return the narrower CharSequence type (which has a toString() method) but I see no harm in it returning String directly. Note that String has the toCharArray() method which returns a (mutable) character array, so we could use that to return char[], but if the user really wants that they can get it from String themselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants