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:
- 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.
- 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 [Arrayprotocol_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+