Skip to content

Class BSV::Script::PushDropTemplate

Inherits: Object

Wallet-integrated PushDrop template for any protocol.

Generalises the pattern used by {BSV::Overlay::AdminTokenTemplate} —derives a locking key from the wallet, optionally signs the data fields, and wraps everything in a PushDrop script backed by a P2PKH condition.

Note: P2PKH vs P2PK

This template uses P2PKH as the underlying spending condition. The {BSV::Overlay::AdminTokenTemplate} uses P2PK (matching the TS/Go SDKs' overlay admin token convention). The two lock types are not interchangeable — tokens locked by one cannot be unlocked by the other.

PushDrop scripts embed arbitrary token data inline in a spendable output:

<field0> <field1> ... <fieldN> [OP_2DROP...] [OP_DROP?]
OP_DUP OP_HASH160 <hash160(derived_pubkey)> OP_EQUALVERIFY OP_CHECKSIG

When +include_signature: true+ (the default), an ECDSA signature over the concatenation of all fields is appended as a final field. This authenticates the token at creation time using the same derived key.

Security note: +counterparty: 'anyone'+ tokens

When counterparty is +'anyone'+, the locking key is derived from the secp256k1 generator point G (PrivateKey(1)). This is a publicly known scalar, meaning:

  1. The output is publicly spendable — any party can sign with PrivateKey(1) and spend the token. This is by design for overlay tokens where public revocability is desired.
  2. The field signature (+include_signature: true+) provides no authenticity guarantee — anyone can produce a valid signature with the known key. Rely on higher-level mechanisms (e.g. certificate keyrings from prove_certificate) for field-level integrity.

@example Lock a token

wallet   = BSV::Wallet::Client.new(private_key, storage: BSV::Wallet::Store::Memory.new)
template = BSV::Script::PushDropTemplate.new(wallet:)
script   = template.lock(
  fields:       ['hello'.b, 'world'.b],
  protocol_id:  [1, 'my-protocol'],
  key_id:       '1',
  counterparty: 'self'
)
script.pushdrop? #=> true

@example Unlock a token

unlocker = template.unlock(
  protocol_id:  [1, 'my-protocol'],
  key_id:       '1',
  counterparty: 'self'
)
input.unlocking_script_template = unlocker

Constants

GENERATOR_PUBKEY_HEX

Public key for PrivateKey(1) — the generator point G. Used when +counterparty: 'anyone'+ so any party can verify.

Public Instance Methods

initialize(wallet:, originator: = nil)

  • @param wallet [#get_public_key, #create_signature] BRC-100 wallet interface
  • @param originator [String, nil] optional FQDN of the originating application
  • @return [PushDropTemplate] a new instance of PushDropTemplate

lock(fields:, protocol_id:, key_id:, counterparty:, include_signature: = true, lock_position: = :before)

Create a PushDrop locking script for the given data fields.

Derives a public key from the wallet using the supplied protocol parameters, then builds a P2PKH locking condition from it. When +include_signature: true+ (the default), signs the concatenation of all fields and appends the DER signature as an additional field.

When counterparty is +'anyone'+, the generator point (PrivateKey(1) public key) is used directly as the locking key. This is the convention for tokens that any party can verify.

The lock_position parameter controls where the P2PKH locking condition is placed relative to the data fields. Defaults to :before (lock first, then fields and drops), matching the ts-sdk convention.

Breaking change (v0.9): the default changed from :after to :before. Callers that relied on the old layout must pass +lock_position: :after+. - @param fields [Array] data payloads to embed (binary strings) - @param protocol_id [Array] two-element [security_level, protocol_name] - @param key_id [String] key identifier - @param counterparty [String] 'self', 'anyone', or a hex public key - @param include_signature [Boolean] whether to append an ECDSA field signature - @param lock_position [Symbol] +:before+ (default) or +:after+ - @raise [ArgumentError] if fields is empty - @return [BSV::Script::Script] the PushDrop locking script

unlock(protocol_id:, key_id:, counterparty:)

Return an unlocker for spending a PushDrop token output.

The returned {Unlocker} follows the unlocking template interface and can be assigned to an input's unlocking_script_template. - @param protocol_id [Array] two-element [security_level, protocol_name] - @param key_id [String] key identifier - @param counterparty [String] 'self', 'anyone', or a hex public key - @return [Unlocker] object with +#sign+ and +#estimated_length+