Module: MixinBot::API::User
- Included in:
- MixinBot::API
- Defined in:
- lib/mixin_bot/api/user.rb
Overview
User-related API endpoints: bot/network user lookup, plain network-user creation, and the full Safe Network registration flow.
The Safe-network registration mirrors the official Go SDK reference implementation in RegisterSafeWithSetupPin / RegisterSafeBareUser: github.com/MixinNetwork/bot-api-go-client/blob/master/safe_user.go
Constant Summary collapse
- SAFE_REGISTER_MAX_RETRIES =
Maximum number of times to retry
safe_registerwhen the freshly-set TIP PIN has not yet propagated through the Mixin server. 3- SAFE_REGISTER_RETRY_BASE_DELAY =
Base seconds to wait between
safe_registerretries. The wait grows linearly with the attempt number. 1- TIP_PIN_PROPAGATION_DELAY =
Seconds to wait after
update_pinbefore callingsafe_register, so the new TIP PIN has time to propagate on the server side. 1- CREATE_USER_BILLING_INCREMENT =
Billed cost per network user created via #create_user (USD), after the free tier. Pass increment: 0 to skip headroom for the new user.
'0.5'
Instance Method Summary collapse
-
#create_safe_user(name, private_key: nil, spend_key: nil, force: false, increment: CREATE_USER_BILLING_INCREMENT) ⇒ Hash
Creates a Safe-network user end-to-end.
-
#create_user(full_name, key: nil, force: false, increment: CREATE_USER_BILLING_INCREMENT) ⇒ Hash
Creates a Mixin network user.
- #fetch_users(user_ids) ⇒ Object
-
#migrate_to_safe(spend_key:, pin: nil) ⇒ TrueClass, Hash
Migrates an existing legacy user to the Safe network.
-
#safe_register(spend_key) ⇒ Hash
Registers an existing user on the Safe network.
- #search_user(query, access_token: nil) ⇒ Object
- #user(user_id, access_token: nil) ⇒ Object
Instance Method Details
#create_safe_user(name, private_key: nil, spend_key: nil, force: false, increment: CREATE_USER_BILLING_INCREMENT) ⇒ Hash
Creates a Safe-network user end-to-end.
Mirrors RegisterSafeWithSetupPin in the Go SDK:
-
generate (or accept) a session keypair and a spend keypair
-
create the network user via #create_user
-
set the user’s PIN to the TIP public key derived from the spend key
-
wait briefly for propagation, then register on the Safe network (retrying transient failures)
The returned keystore is suitable for instantiating a new MixinBot::API that authenticates as the freshly-registered user.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/mixin_bot/api/user.rb', line 110 def create_safe_user(name, private_key: nil, spend_key: nil, force: false, increment: CREATE_USER_BILLING_INCREMENT) session_keypair = JOSE::JWA::Ed25519.keypair private_key spend_keypair = JOSE::JWA::Ed25519.keypair spend_key spend_key_hex = spend_keypair[1].unpack1('H*') user = create_user name, key: session_keypair[1][...32], force: force, increment: increment data = user.fetch('data') keystore = { app_id: data['user_id'], session_id: data['session_id'], session_private_key: session_keypair[1].unpack1('H*'), server_public_key: data['pin_token_base64'], spend_key: spend_key_hex } user_api = MixinBot::API.new(**keystore) tip_pin = MixinBot.utils.tip_public_key spend_keypair[0], counter: data['tip_counter'] user_api.update_pin pin: tip_pin # Allow the freshly-set TIP PIN to propagate before registering. sleep TIP_PIN_PROPAGATION_DELAY with_safe_register_retries do user_api.safe_register spend_key_hex end keystore end |
#create_user(full_name, key: nil, force: false, increment: CREATE_USER_BILLING_INCREMENT) ⇒ Hash
Creates a Mixin network user.
When key is omitted a fresh Ed25519 keypair is generated. The response is merged with the hex-encoded session private key under :private_key.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/mixin_bot/api/user.rb', line 51 def create_user(full_name, key: nil, force: false, increment: CREATE_USER_BILLING_INCREMENT) ensure_app_billing_credit!(force:, increment:) keypair = JOSE::JWA::Ed25519.keypair key session_secret = Base64.urlsafe_encode64 keypair[0], padding: false private_key = keypair[1].unpack1('H*') path = '/users' payload = { full_name:, session_secret: } res = client.post path, **payload res.merge(private_key:).with_indifferent_access end |
#fetch_users(user_ids) ⇒ Object
74 75 76 77 78 79 80 |
# File 'lib/mixin_bot/api/user.rb', line 74 def fetch_users(user_ids) path = '/users/fetch' user_ids = [user_ids] if user_ids.is_a? String payload = user_ids client.post path, *payload end |
#migrate_to_safe(spend_key:, pin: nil) ⇒ TrueClass, Hash
Migrates an existing legacy user to the Safe network.
When the user has not yet upgraded to a TIP PIN, pin must be the user’s current 6-digit PIN so Pin#update_pin can rotate it to a TIP PIN derived from spend_key. When the user already has a TIP PIN, pin may be omitted.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/mixin_bot/api/user.rb', line 203 def migrate_to_safe(spend_key:, pin: nil) profile = me['data'] return true if profile['has_safe'] spend_keypair = JOSE::JWA::Ed25519.keypair spend_key spend_key_hex = spend_keypair[1].unpack1('H*') if profile['tip_key_base64'].blank? new_pin = MixinBot.utils.tip_public_key spend_keypair[0], counter: profile['tip_counter'] update_pin pin: new_pin, old_pin: pin end # Allow the freshly-set TIP PIN to propagate before registering. sleep TIP_PIN_PROPAGATION_DELAY with_safe_register_retries do safe_register spend_key_hex end { spend_key: spend_key_hex }.with_indifferent_access end |
#safe_register(spend_key) ⇒ Hash
Registers an existing user on the Safe network.
spend_key may be supplied as raw bytes, a hex string, or a Base64-encoded string. It must encode the user’s full Ed25519 spend private key (or a 32-byte seed).
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/mixin_bot/api/user.rb', line 154 def safe_register(spend_key) path = '/safe/users' spend_key_bytes = MixinBot.utils.decode_key spend_key raise ArgumentError, 'invalid spend_key' if spend_key_bytes.nil? || spend_key_bytes.size < 32 keypair = JOSE::JWA::Ed25519.keypair spend_key_bytes[...32] public_key = keypair[0].unpack1('H*') # Normalize to a 64-byte signing key in hex so that callers may pass a # 32-byte seed without crashing the downstream signer. signing_key_hex = keypair[1].unpack1('H*') # NOTE: the Go SDK's +crypto.Sha256Hash+ is misleadingly named — it # actually computes SHA3-256, so +SHA3::Digest::SHA256+ is the correct # match. See bot-api-go-client safe_user.go +RegisterSafeBareUser+. app_id_hash = SHA3::Digest::SHA256.hexdigest config.app_id signature = Base64.urlsafe_encode64( JOSE::JWA::Ed25519.sign([app_id_hash].pack('H*'), keypair[1]), padding: false ) pin_base64 = encrypt_tip_pin signing_key_hex, 'SEQUENCER:REGISTER:', config.app_id, public_key payload = { public_key:, signature:, pin_base64: } client.post path, **payload end |
#search_user(query, access_token: nil) ⇒ Object
68 69 70 71 72 |
# File 'lib/mixin_bot/api/user.rb', line 68 def search_user(query, access_token: nil) path = format('/search/%<query>s', query:) client.get path, access_token: end |
#user(user_id, access_token: nil) ⇒ Object
30 31 32 33 |
# File 'lib/mixin_bot/api/user.rb', line 30 def user(user_id, access_token: nil) path = format('/users/%<user_id>s', user_id:) client.get path, access_token: end |