/core-test/src/test/scala/org/bitcoins/core/protocol/Bech32mSpec.scala

https://github.com/bitcoin-s/bitcoin-s-core · Scala · 82 lines · 70 code · 9 blank · 3 comment · 9 complexity · a7d4eb810de7519a0a7daf0bda3b5566 MD5 · raw file

  1. package org.bitcoins.core.protocol
  2. import org.bitcoins.core.protocol.script.WitnessVersion0
  3. import org.bitcoins.core.util.{Bech32, Bech32Encoding}
  4. import org.bitcoins.testkitcore.gen._
  5. import org.scalacheck.{Prop, Properties}
  6. import scala.annotation.tailrec
  7. import scala.util.{Random, Success}
  8. class Bech32mSpec extends Properties("Bech32mSpec") {
  9. property("split all Bech32m addresses into HRP and data") = {
  10. Prop.forAll(AddressGenerator.bech32mAddress) { address =>
  11. val splitT =
  12. Bech32.splitToHrpAndData(address.value, Bech32Encoding.Bech32m)
  13. splitT.isSuccess
  14. }
  15. }
  16. property("serialization symmetry") = {
  17. Prop.forAll(ScriptGenerators.witnessScriptPubKey.suchThat(
  18. _._1.witnessVersion != WitnessVersion0),
  19. ChainParamsGenerator.networkParams) {
  20. case ((witSPK, _), network) =>
  21. val addr = Bech32mAddress(witSPK, network)
  22. val spk = Bech32mAddress.fromStringToWitSPK(addr.value)
  23. spk == Success(witSPK)
  24. }
  25. }
  26. property("checksum must not work if we modify a char") = {
  27. Prop.forAll(AddressGenerator.bech32mAddress) { addr: Bech32mAddress =>
  28. val old = addr.value
  29. val rand = Math.abs(Random.nextInt())
  30. val idx = rand % old.length
  31. val (f, l) = old.splitAt(idx)
  32. val replacementChar = pickReplacementChar(l.head)
  33. val replaced = s"$f$replacementChar${l.tail}"
  34. //should fail because we replaced a char in the addr, so checksum invalid
  35. Bech32mAddress.fromStringT(replaced).isFailure
  36. }
  37. }
  38. property("must fail if we have a mixed case") = {
  39. Prop.forAllNoShrink(AddressGenerator.bech32mAddress) {
  40. addr: Bech32mAddress =>
  41. val old = addr.value
  42. val replaced = switchCaseRandChar(old)
  43. //should fail because we we switched the case of a random char
  44. val actual = Bech32mAddress.fromStringT(replaced)
  45. actual.isFailure
  46. }
  47. }
  48. @tailrec
  49. private def pickReplacementChar(oldChar: Char): Char = {
  50. val rand = Math.abs(Random.nextInt())
  51. val newChar = Bech32.charset(rand % Bech32.charset.size)
  52. //make sure we don't pick the same char we are replacing in the bech32m address
  53. if (oldChar == newChar) pickReplacementChar(oldChar)
  54. else newChar
  55. }
  56. @tailrec
  57. private def switchCaseRandChar(addr: String): String = {
  58. val rand = Math.abs(Random.nextInt())
  59. val idx = rand % addr.length
  60. val (f, l) = addr.splitAt(idx)
  61. if (l.head.isDigit) {
  62. switchCaseRandChar(addr)
  63. } else {
  64. val middle =
  65. if (l.head.isUpper) {
  66. l.head.toLower
  67. } else {
  68. l.head.toUpper
  69. }
  70. s"$f$middle${l.tail}"
  71. }
  72. }
  73. }