Class: BSV::Overlay::AdminTokenTemplate

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/overlay/admin_token_template.rb

Overview

Script template for creating, unlocking, and decoding SHIP and SLAP advertisements.

SHIP (Service Host Interconnect) and SLAP (Service Lookup Availability) tokens are PushDrop scripts containing four data fields:

Field 0: protocol string — 'SHIP' or 'SLAP'
Field 1: identity key — 33-byte compressed public key (binary)
Field 2: domain — UTF-8 string
Field 3: topic or service name — UTF-8 string

The locking script includes a fifth field containing a wallet signature over the concatenation of fields 0–3, which authenticates the token at creation time. The lock is secured with a P2PK condition derived from the wallet using BRC-42/43 key derivation at security level 2.

Script layout (lock-after PushDrop format):

<protocol> <identity_key> <domain> <topic> <wallet_sig>
OP_2DROP OP_2DROP OP_DROP
<derived_pubkey> OP_CHECKSIG

Examples:

Lock a SHIP advertisement

wallet = BSV::Wallet::ProtoWallet.new(private_key)
template = BSV::Overlay::AdminTokenTemplate.new(wallet)
locking_script = template.lock('SHIP', 'myhost.example.com', 'tm_payments')
decoded = BSV::Overlay::AdminTokenTemplate.decode(locking_script)
decoded.identity_key  # => hex public key of the wallet

Defined Under Namespace

Classes: Advertisement, Unlocker

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wallet, originator: nil) ⇒ AdminTokenTemplate

Construct a new AdminTokenTemplate instance.

Parameters:

  • wallet (#get_public_key, #create_signature)

    any object implementing the BRC-100 wallet interface (e.g. Wallet::ProtoWallet)

  • originator (String, nil) (defaults to: nil)

    optional FQDN of the originating application



149
150
151
152
# File 'lib/bsv/overlay/admin_token_template.rb', line 149

def initialize(wallet, originator: nil)
  @wallet = wallet
  @originator = originator
end

Class Method Details

.decode(script) ⇒ Advertisement?

Decode a SHIP or SLAP advertisement from a PushDrop locking script.

Parameters:

Returns:

  • (Advertisement, nil)

    the decoded advertisement, or nil if the script is nil, empty, or not a PushDrop script

Raises:

  • (BSV::Overlay::OverlayError)

    if the script is PushDrop but has fewer than 4 fields, or if the protocol field is not ‘SHIP’ or ‘SLAP’



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/bsv/overlay/admin_token_template.rb', line 122

def self.decode(script)
  return nil if script.nil? || script.bytes.empty?
  return nil unless script.pushdrop?

  fields = script.pushdrop_fields
  raise OverlayError, 'Invalid SHIP/SLAP advertisement: expected at least 4 fields' if fields.length < 4

  protocol = fields[0].force_encoding('UTF-8')
  raise OverlayError, "Invalid SHIP/SLAP protocol: #{protocol.inspect}" unless VALID_PROTOCOLS.include?(protocol)

  identity_key = fields[1].unpack1('H*')
  domain = normalise_field(fields[2])
  topic_or_service = normalise_field(fields[3])

  Advertisement.new(
    protocol: protocol,
    identity_key: identity_key,
    domain: domain,
    topic_or_service: topic_or_service
  )
end

Instance Method Details

#lock(protocol, domain, topic_or_service) ⇒ BSV::Script::Script

Create a SHIP or SLAP advertisement locking script.

Derives the wallet’s identity key, builds the four advertisement fields, signs them with the protocol-derived key, and constructs a PushDrop locking script with a P2PK spending condition.

Parameters:

  • protocol (String)

    ‘SHIP’ or ‘SLAP’

  • domain (String)

    domain where the service or topic is available

  • topic_or_service (String)

    topic or service name to advertise

Returns:

Raises:



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/bsv/overlay/admin_token_template.rb', line 165

def lock(protocol, domain, topic_or_service)
  raise OverlayError, "Invalid protocol: #{protocol.inspect}. Must be 'SHIP' or 'SLAP'" \
    unless VALID_PROTOCOLS.include?(protocol)

  protocol_id = protocol_id_for(protocol)

  # Fetch the wallet's identity key (compressed public key hex)
  id_args = { identity_key: true }
  id_args[:originator] = @originator if @originator
  identity_result = @wallet.get_public_key(id_args)
  identity_key_hex = identity_result[:public_key]
  identity_key_bytes = [identity_key_hex].pack('H*')

  # Derive the locking public key for this protocol
  lock_args = { protocol_id: protocol_id, key_id: '1', counterparty: 'self' }
  lock_args[:originator] = @originator if @originator
  locking_result = @wallet.get_public_key(lock_args)
  locking_pubkey_hex = locking_result[:public_key]
  locking_pubkey_bytes = [locking_pubkey_hex].pack('H*')

  # Build the four advertisement fields
  field_protocol = protocol.b
  field_identity = identity_key_bytes
  field_domain = domain.encode('binary')
  field_topic = topic_or_service.encode('binary')

  # Sign the concatenation of all four fields as authentication
  data_to_sign = (field_protocol + field_identity + field_domain + field_topic).unpack('C*')
  sig_args = { data: data_to_sign, protocol_id: protocol_id, key_id: '1', counterparty: 'self' }
  sig_args[:originator] = @originator if @originator
  sig_result = @wallet.create_signature(sig_args)
  field_sig = sig_result[:signature].pack('C*')

  fields = [field_protocol, field_identity, field_domain, field_topic, field_sig]
  lock_script = BSV::Script::Script.p2pk_lock(locking_pubkey_bytes)
  BSV::Script::Script.pushdrop_lock(fields, lock_script)
end

#unlock(protocol) ⇒ Unlocker

Return an unlocker for spending an advertisement token.

The returned object follows the Transaction::UnlockingScriptTemplate interface and can be assigned to an input’s unlocking_script_template.

Parameters:

  • protocol (String)

    ‘SHIP’ or ‘SLAP’ — must match the locked token

Returns:

  • (Unlocker)

    an object with #sign and #estimated_length

Raises:



211
212
213
214
215
216
# File 'lib/bsv/overlay/admin_token_template.rb', line 211

def unlock(protocol)
  raise OverlayError, "Invalid protocol: #{protocol.inspect}. Must be 'SHIP' or 'SLAP'" \
    unless VALID_PROTOCOLS.include?(protocol)

  Unlocker.new(@wallet, protocol_id_for(protocol), @originator)
end