Skip to content

Commit

Permalink
Bech32 address improvements (#2571)
Browse files Browse the repository at this point in the history
* Bech32 address improvements

* Respond to review
  • Loading branch information
benthecarman committed Jan 27, 2021
1 parent 5225b2a commit 72ac981
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 13 deletions.
Expand Up @@ -29,6 +29,15 @@ class Bech32Test extends BitcoinSUnitTest {
}
}

it must "properly read an uppercase bech32 address" in {
val addrStr = "bcrt1qq6w6pu6zq90az9krn53zlkvgyzkyeglzukyepf"
val addrT = Address.fromStringT(addrStr.toUpperCase)
addrT match {
case Success(addr: Bech32Address) => assert(addr.value == addrStr)
case _ => fail()
}
}

it must "follow the example in BIP173" in {
//https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#examples
val key = ECPublicKey(
Expand Down
14 changes: 9 additions & 5 deletions core/src/main/scala/org/bitcoins/core/protocol/Address.scala
Expand Up @@ -105,6 +105,10 @@ sealed abstract class Bech32Address extends BitcoinAddress {
def expandHrp: Vector[UInt5] = {
hrp.expand
}

def verifyChecksum: Boolean = {
Bech32.verifyChecksum(hrp.expand, data ++ checksum)
}
}

object Bech32Address extends AddressFactory[Bech32Address] {
Expand All @@ -113,7 +117,7 @@ object Bech32Address extends AddressFactory[Bech32Address] {
networkParameters: NetworkParameters,
data: Vector[UInt5])
extends Bech32Address {
//require(verifyChecksum(hrp, data), "checksum did not pass")
require(verifyChecksum, s"checksum did not pass $checksum")
}

def empty(network: NetworkParameters = MainNet): Bech32Address =
Expand Down Expand Up @@ -192,7 +196,7 @@ object Bech32Address extends AddressFactory[Bech32Address] {
spk: ScriptPubKey,
np: NetworkParameters): Try[Bech32Address] =
spk match {
case witSPK: WitnessScriptPubKey =>
case witSPK: WitnessScriptPubKeyV0 =>
Success(Bech32Address(witSPK, np))
case x @ (_: P2PKScriptPubKey | _: P2PKHScriptPubKey |
_: P2PKWithTimeoutScriptPubKey | _: MultiSignatureScriptPubKey |
Expand Down Expand Up @@ -386,9 +390,9 @@ object BitcoinAddress extends AddressFactory[BitcoinAddress] {
spk: ScriptPubKey,
np: NetworkParameters): Try[BitcoinAddress] =
spk match {
case p2pkh: P2PKHScriptPubKey => Success(P2PKHAddress(p2pkh, np))
case p2sh: P2SHScriptPubKey => Success(P2SHAddress(p2sh, np))
case witSPK: WitnessScriptPubKey => Success(Bech32Address(witSPK, np))
case p2pkh: P2PKHScriptPubKey => Success(P2PKHAddress(p2pkh, np))
case p2sh: P2SHScriptPubKey => Success(P2SHAddress(p2sh, np))
case witSPK: WitnessScriptPubKeyV0 => Success(Bech32Address(witSPK, np))
case x @ (_: P2PKScriptPubKey | _: P2PKWithTimeoutScriptPubKey |
_: MultiSignatureScriptPubKey | _: LockTimeScriptPubKey |
_: ConditionalScriptPubKey | _: NonStandardScriptPubKey |
Expand Down
Expand Up @@ -39,7 +39,7 @@ object BtcHumanReadablePart extends StringFactory[BtcHumanReadablePart] {
}

override def fromString(str: String): BtcHumanReadablePart =
str match {
str.toLowerCase match {
case "bc" => bc
case "tb" => tb
case "bcrt" => bcrt // Bitcoin Core specific
Expand Down
27 changes: 20 additions & 7 deletions core/src/main/scala/org/bitcoins/core/util/Bech32.scala
Expand Up @@ -135,20 +135,20 @@ sealed abstract class Bech32 {
remaining match {
case Nil => Success(accum.reverse)
case h :: t =>
if (!Bech32.charset.contains(h.toLower)) {
if ((h.isUpper && hasLower) || (h.isLower && hasUpper)) {
Failure(
new IllegalArgumentException(
"Invalid character in data of bech32 address, got: " + h))
"Cannot have mixed case for bech32 address"))
} else {
if ((h.isUpper && hasLower) || (h.isLower && hasUpper)) {
val value = Bech32.charsetReversed(h.toInt)

if (value == -1) {
Failure(
new IllegalArgumentException(
"Cannot have mixed case for bech32 address"))
"Invalid character in data of bech32 address, got: " + h))
} else {
val byte = Bech32.charset.indexOf(h.toLower).toByte

loop(remaining = t,
accum = UInt5.fromByte(byte) +: accum,
accum = UInt5.fromByte(value.toByte) +: accum,
hasUpper = h.isUpper || hasUpper,
hasLower = h.isLower || hasLower)
}
Expand Down Expand Up @@ -305,6 +305,19 @@ object Bech32 extends Bech32 {
val charset: Vector[Char] = Vector('q', 'p', 'z', 'r', 'y', '9', 'x', '8',
'g', 'f', '2', 't', 'v', 'd', 'w', '0', 's', '3', 'j', 'n', '5', '4', 'k',
'h', 'c', 'e', '6', 'm', 'u', 'a', '7', 'l')

/** The Bech32 character set for decoding.
* @see https://github.com/sipa/bech32/blob/master/ref/c%2B%2B/bech32.cpp#L33
*/
val charsetReversed: Vector[Int] = Vector(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7,
5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31,
27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1,
29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11,
28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
)
}

abstract class Bech32HumanReadablePart {
Expand Down

0 comments on commit 72ac981

Please sign in to comment.