Module: MixinBot::Utils::Address

Included in:
MixinBot::Utils
Defined in:
lib/mixin_bot/utils/address.rb

Constant Summary collapse

MAIN_ADDRESS_PREFIX =
'XIN'
MIX_ADDRESS_PREFIX =
'MIX'
MIX_ADDRESS_VERSION =
2

Instance Method Summary collapse

Instance Method Details

#build_main_address(public_key) ⇒ Object



10
11
12
13
14
15
16
# File 'lib/mixin_bot/utils/address.rb', line 10

def build_main_address(public_key)
  msg = MAIN_ADDRESS_PREFIX + public_key
  checksum = SHA3::Digest::SHA256.digest msg
  data = public_key + checksum[0...4]
  base58 = Base58.binary_to_base58 data, :bitcoin
  "#{MAIN_ADDRESS_PREFIX}#{base58}"
end

#build_mix_address(members, threshold) ⇒ Object

Raises:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/mixin_bot/utils/address.rb', line 35

def build_mix_address(members, threshold)
  raise ArgumentError, 'members should be an array' unless members.is_a? Array
  raise ArgumentError, 'members should not be empty' if members.empty?
  raise ArgumentError, 'members length should less than 256' if members.length > 255
  raise ArgumentError, "invalid threshold: #{threshold}" if threshold > members.length

  prefix = [MIX_ADDRESS_VERSION].pack('C*') + [threshold].pack('C*') + [members.length].pack('C*')

  msg =
    if members.all?(&->(member) { member.start_with? MAIN_ADDRESS_PREFIX })
      members.map(&->(member) { parse_main_address(member) }).join
    elsif members.none?(&->(member) { member.start_with? MAIN_ADDRESS_PREFIX })
      members.map(&->(member) { MixinBot::UUID.new(hex: member).packed }).join
    else
      raise ArgumentError, 'invalid members'
    end

  checksum = SHA3::Digest::SHA256.digest(MIX_ADDRESS_PREFIX + prefix + msg)

  data = prefix + msg + checksum[0...4]
  data = Base58.binary_to_base58 data, :bitcoin
  "#{MIX_ADDRESS_PREFIX}#{data}"
end

#build_safe_recipient(**kwargs) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/mixin_bot/utils/address.rb', line 91

def build_safe_recipient(**kwargs)
  members = kwargs[:members]
  threshold = kwargs[:threshold]
  amount = kwargs[:amount]

  members = [members] if members.is_a? String
  amount = format('%.8f', amount.to_d.to_r).gsub(/\.?0+$/, '')

  {
    members:,
    threshold:,
    amount:,
    mix_address: build_mix_address(members, threshold)
  }
end

#parse_main_address(address) ⇒ Object

Raises:



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mixin_bot/utils/address.rb', line 18

def parse_main_address(address)
  raise ArgumentError, 'invalid address' unless address.start_with? MAIN_ADDRESS_PREFIX

  data = address[MAIN_ADDRESS_PREFIX.length..]
  data = Base58.base58_to_binary data, :bitcoin
  raise ArgumentError, 'invalid address' unless data.length == 68

  payload = data[...-4]

  msg = MAIN_ADDRESS_PREFIX + payload
  checksum = SHA3::Digest::SHA256.digest msg

  raise ArgumentError, 'invalid address' unless checksum[0...4] == data[-4..]

  payload
end

#parse_mix_address(address) ⇒ Object

Raises:



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/mixin_bot/utils/address.rb', line 59

def parse_mix_address(address)
  raise ArgumentError, 'invalid address' unless address.start_with? MIX_ADDRESS_PREFIX

  data = address[MIX_ADDRESS_PREFIX.length..]
  data = Base58.base58_to_binary data, :bitcoin
  raise ArgumentError, 'invalid address' if data.length < 3 + 16 + 4

  msg = data[...-4]
  checksum = SHA3::Digest::SHA256.digest((MIX_ADDRESS_PREFIX + msg))[0...4]

  raise ArgumentError, 'invalid address' unless checksum[0...4] == data[-4..]

  version = data[0].ord
  raise ArgumentError, 'invalid address' unless version == MIX_ADDRESS_VERSION

  threshold = data[1].ord
  members_count = data[2].ord

  if data[3..].length == members_count * 16
    members = data[3..].scan(/.{16}/)
    members = members.map(&->(member) { MixinBot::UUID.new(raw: member).unpacked })
  else
    members = data[3..].scan(/.{64}/)
    members = members.map(&->(member) { build_main_address(member) })
  end

  {
    members:,
    threshold:
  }
end