/ref/ruby/segwit_addr.rb

https://github.com/sipa/bech32 · Ruby · 86 lines · 56 code · 11 blank · 19 comment · 36 complexity · 332d9efde710c95661d5875f370d5c86 MD5 · raw file

  1. # Copyright (c) 2017 Shigeyuki Azuchi
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. # THE SOFTWARE.
  20. require './bech32'
  21. class SegwitAddr
  22. attr_accessor :hrp # human-readable part
  23. attr_accessor :ver # witness version
  24. attr_accessor :prog # witness program
  25. def initialize(addr = nil)
  26. @hrp, @ver, @prog = parse_addr(addr) if addr
  27. end
  28. def to_scriptpubkey
  29. v = ver == 0 ? ver : ver + 0x50
  30. ([v, prog.length].pack("CC") + prog.map{|p|[p].pack("C")}.join).unpack('H*').first
  31. end
  32. def scriptpubkey=(script)
  33. values = [script].pack('H*').unpack("C*")
  34. @ver = values[0] == 0 ? values[0] : values[0] - 0x50
  35. @prog = values[2..-1]
  36. end
  37. def addr
  38. spec = (ver == 0 ? Bech32::Encoding::BECH32 : Bech32::Encoding::BECH32M)
  39. Bech32.encode(hrp, [ver] + convert_bits(prog, 8, 5), spec)
  40. end
  41. private
  42. def parse_addr(addr)
  43. hrp, data, spec = Bech32.decode(addr)
  44. raise 'Invalid address.' if hrp.nil? || data[0].nil? || (hrp != 'bc' && hrp != 'tb')
  45. ver = data[0]
  46. raise 'Invalid witness version' if ver > 16
  47. prog = convert_bits(data[1..-1], 5, 8, false)
  48. raise 'Invalid witness program' if prog.nil? || prog.length < 2 || prog.length > 40
  49. raise 'Invalid witness program with version 0' if ver == 0 && (prog.length != 20 && prog.length != 32)
  50. raise 'Witness version and encoding spec do not match' if (ver == 0 && spec != Bech32::Encoding::BECH32) || (ver != 0 && spec != Bech32::Encoding::BECH32M)
  51. [hrp, ver, prog]
  52. end
  53. def convert_bits(data, from, to, padding=true)
  54. acc = 0
  55. bits = 0
  56. ret = []
  57. maxv = (1 << to) - 1
  58. max_acc = (1 << (from + to - 1)) - 1
  59. data.each do |v|
  60. return nil if v < 0 || (v >> from) != 0
  61. acc = ((acc << from) | v) & max_acc
  62. bits += from
  63. while bits >= to
  64. bits -= to
  65. ret << ((acc >> bits) & maxv)
  66. end
  67. end
  68. if padding
  69. ret << ((acc << (to - bits)) & maxv) unless bits == 0
  70. elsif bits >= from || ((acc << (to - bits)) & maxv) != 0
  71. return nil
  72. end
  73. ret
  74. end
  75. end