Class: MixinBot::MixAddress
- Inherits:
-
Object
- Object
- MixinBot::MixAddress
- Defined in:
- lib/mixin_bot/address.rb
Instance Attribute Summary collapse
-
#address ⇒ Object
Returns the value of attribute address.
-
#payload ⇒ Object
Returns the value of attribute payload.
-
#threshold ⇒ Object
Returns the value of attribute threshold.
-
#uuid_members ⇒ Object
Returns the value of attribute uuid_members.
-
#version ⇒ Object
Returns the value of attribute version.
-
#xin_members ⇒ Object
Returns the value of attribute xin_members.
Class Method Summary collapse
Instance Method Summary collapse
- #decode ⇒ Object
- #encode ⇒ Object
-
#initialize(**args) ⇒ MixAddress
constructor
A new instance of MixAddress.
- #request_or_generate_ghost_keys(output_index = 0, api: MixinBot.api) ⇒ Object
- #to_safe_recipient ⇒ Object
- #valid? ⇒ Boolean
Constructor Details
#initialize(**args) ⇒ MixAddress
Returns a new instance of MixAddress.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/mixin_bot/address.rb', line 21 def initialize(**args) args = args.with_indifferent_access if args[:address] @address = args[:address] decode elsif args[:payload] @payload = args[:payload] decode else @version = args[:version] || MIX_ADDRESS_VERSION if args[:members].present? @xin_members, @uuid_members = args[:members].partition do |member| member.start_with?(MAIN_ADDRESS_PREFIX) end else @uuid_members = args[:uuid_members] || [] @xin_members = args[:xin_members] || [] end @uuid_members = @uuid_members.sort @xin_members = @xin_members.sort @threshold = args[:threshold] encode end raise ArgumentError, 'invalid address' unless valid? end |
Instance Attribute Details
#address ⇒ Object
Returns the value of attribute address.
11 12 13 |
# File 'lib/mixin_bot/address.rb', line 11 def address @address end |
#payload ⇒ Object
Returns the value of attribute payload.
11 12 13 |
# File 'lib/mixin_bot/address.rb', line 11 def payload @payload end |
#threshold ⇒ Object
Returns the value of attribute threshold.
11 12 13 |
# File 'lib/mixin_bot/address.rb', line 11 def threshold @threshold end |
#uuid_members ⇒ Object
Returns the value of attribute uuid_members.
11 12 13 |
# File 'lib/mixin_bot/address.rb', line 11 def uuid_members @uuid_members end |
#version ⇒ Object
Returns the value of attribute version.
11 12 13 |
# File 'lib/mixin_bot/address.rb', line 11 def version @version end |
#xin_members ⇒ Object
Returns the value of attribute xin_members.
11 12 13 |
# File 'lib/mixin_bot/address.rb', line 11 def xin_members @xin_members end |
Class Method Details
.from_members(members:, threshold:) ⇒ Object
17 18 19 |
# File 'lib/mixin_bot/address.rb', line 17 def self.from_members(members:, threshold:) new(members:, threshold:) end |
.parse(string) ⇒ Object
13 14 15 |
# File 'lib/mixin_bot/address.rb', line 13 def self.parse(string) new(address: string) end |
Instance Method Details
#decode ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/mixin_bot/address.rb', line 120 def decode if address.present? 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, length invalid' if data.length < 3 + 16 + 4 self.payload = data[...-4] checksum = SHA3::Digest::SHA256.digest(MIX_ADDRESS_PREFIX + payload)[0...4] raise ArgumentError, 'invalid address, checksum invalid' unless checksum == data[-4..] else checksum = SHA3::Digest::SHA256.digest(MIX_ADDRESS_PREFIX + payload)[0...4] data = payload + checksum data = Base58.binary_to_base58 data, :bitcoin self.address = "#{MIX_ADDRESS_PREFIX}#{data}" end self.version = payload[0].ord raise ArgumentError, 'invalid address, version invalid' unless version.is_a?(Integer) self.threshold = payload[1].ord raise ArgumentError, 'invalid address, threshold invalid' unless threshold.is_a?(Integer) members_count = payload[2].ord raise ArgumentError, 'invalid address, members count invalid' unless members_count.is_a?(Integer) if payload[3...].length == members_count * UUID_ADDRESS_LENGTH uuid_members = payload[3...].chars.each_slice(UUID_ADDRESS_LENGTH).map(&:join) self.uuid_members = uuid_members.map(&->(member) { MixinBot::UUID.new(raw: member).unpacked }) self.xin_members = [] else xin_members = payload[3...].chars.each_slice(MAIN_ADDRESS_LENGTH).map(&:join) self.xin_members = xin_members.map(&->(member) { MainAddress.new(public_key: member).address }) self.uuid_members = [] end end |
#encode ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/mixin_bot/address.rb', line 85 def encode raise ArgumentError, 'members should be an array' unless uuid_members.is_a?(Array) || xin_members.is_a?(Array) raise ArgumentError, 'members should not be empty' if uuid_members.empty? && xin_members.empty? raise ArgumentError, 'members length should less than 256' if uuid_members.length + xin_members.length > 255 member_count = uuid_members.length + xin_members.length # UUID mix (Go NewUUIDMixAddress): threshold must be in 1..len(members). # XIN-only mix (Go NewMainnetMixAddress): threshold may exceed member count (sparse), e.g. storage 1-of-64 style. if uuid_members.present? && xin_members.empty? raise ArgumentError, "invalid threshold: #{threshold}" unless threshold.positive? && threshold <= member_count elsif xin_members.present? && uuid_members.empty? raise ArgumentError, "invalid threshold: #{threshold}" unless threshold.positive? raise ArgumentError, 'too many XIN members' if member_count > 64 elsif threshold > member_count raise ArgumentError, "invalid threshold: #{threshold}" end prefix = [version].pack('C*') + [threshold].pack('C*') + [uuid_members.length + xin_members.length].pack('C*') msg = uuid_members&.map(&->(member) { MixinBot::UUID.new(hex: member).packed })&.join.to_s + xin_members&.map(&->(member) { MainAddress.new(address: member).public_key })&.join.to_s self.payload = prefix + msg checksum = SHA3::Digest::SHA256.digest(MIX_ADDRESS_PREFIX + payload) data = payload + checksum[0...4] data = Base58.binary_to_base58 data, :bitcoin self.address = "#{MIX_ADDRESS_PREFIX}#{data}" address end |
#request_or_generate_ghost_keys(output_index = 0, api: MixinBot.api) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/mixin_bot/address.rb', line 65 def request_or_generate_ghost_keys(output_index = 0, api: MixinBot.api) if xin_members.present? key = JOSE::JWA::Ed25519.keypair gk = { 'mask' => key[0].unpack1('H*'), 'keys' => [] } xin_members.each do |member| payload = MixinBot.utils.parse_main_address(member) spend_key = payload[0...32] view_key = payload[-32..] ghost = MixinBot.utils.derive_ghost_public_key(key[1], view_key, spend_key, output_index) gk['keys'] << ghost.unpack1('H*') end gk else hint = SecureRandom.uuid api.create_safe_keys( { receivers: (uuid_members + xin_members).sort, index: output_index, hint: } )['data'].first end end |
#to_safe_recipient ⇒ Object
56 57 58 59 60 61 62 63 |
# File 'lib/mixin_bot/address.rb', line 56 def to_safe_recipient { members: uuid_members + xin_members, threshold:, amount:, mix_address: address } end |
#valid? ⇒ Boolean
52 53 54 |
# File 'lib/mixin_bot/address.rb', line 52 def valid? address.present? && (uuid_members.present? || xin_members.present?) && threshold.present? end |