Class: BSV::Script::PushDropTemplate
- Inherits:
-
Object
- Object
- BSV::Script::PushDropTemplate
- Defined in:
- lib/bsv/script/push_drop_template.rb
Overview
Wallet-integrated PushDrop template for any protocol.
Generalises the pattern used by 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 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.
Defined Under Namespace
Classes: Unlocker
Constant Summary collapse
- GENERATOR_PUBKEY_HEX =
Public key for PrivateKey(1) — the generator point G. Used when counterparty: ‘anyone’ so any party can verify.
'0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
Instance Method Summary collapse
-
#initialize(wallet:, originator: nil) ⇒ PushDropTemplate
constructor
A new instance of PushDropTemplate.
-
#lock(fields:, protocol_id:, key_id:, counterparty:, include_signature: true, lock_position: :before) ⇒ BSV::Script::Script
Create a PushDrop locking script for the given data fields.
-
#unlock(protocol_id:, key_id:, counterparty:) ⇒ Unlocker
Return an unlocker for spending a PushDrop token output.
Constructor Details
#initialize(wallet:, originator: nil) ⇒ PushDropTemplate
Returns a new instance of PushDropTemplate.
138 139 140 141 |
# File 'lib/bsv/script/push_drop_template.rb', line 138 def initialize(wallet:, originator: nil) @wallet = wallet @originator = originator end |
Instance Method Details
#lock(fields:, protocol_id:, key_id:, counterparty:, include_signature: true, lock_position: :before) ⇒ BSV::Script::Script
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.
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 |
# File 'lib/bsv/script/push_drop_template.rb', line 169 def lock(fields:, protocol_id:, key_id:, counterparty:, include_signature: true, lock_position: :before) raise ArgumentError, 'fields must not be empty' if fields.empty? # When counterparty is 'anyone', use the generator point directly as the # locking key — this is the "anyone can verify" convention. pubkey_bytes = if counterparty == 'anyone' [GENERATOR_PUBKEY_HEX].pack('H*') else pub_args = { protocol_id: protocol_id, key_id: key_id, counterparty: counterparty } orig_kw = @originator ? { originator: @originator } : {} pub_result = @wallet.get_public_key(pub_args, **orig_kw) [pub_result[:public_key]].pack('H*') end # Build all fields, optionally appending a signature over their concatenation all_fields = fields.map(&:b) if include_signature data_to_sign = all_fields.reduce(''.b) { |acc, f| acc + f }.unpack('C*') sig_args = { data: data_to_sign, protocol_id: protocol_id, key_id: key_id, counterparty: counterparty } orig_kw = @originator ? { originator: @originator } : {} sig_result = @wallet.create_signature(sig_args, **orig_kw) all_fields << sig_result[:signature].pack('C*') end # Build P2PKH lock from the derived pubkey's Hash160 pubkey_hash = BSV::Primitives::Digest.hash160(pubkey_bytes) p2pkh_lock = BSV::Script::Script.p2pkh_lock(pubkey_hash) BSV::Script::Script.pushdrop_lock(all_fields, p2pkh_lock, lock_position: lock_position) end |
#unlock(protocol_id:, key_id:, counterparty:) ⇒ Unlocker
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.
210 211 212 |
# File 'lib/bsv/script/push_drop_template.rb', line 210 def unlock(protocol_id:, key_id:, counterparty:) Unlocker.new(@wallet, protocol_id, key_id, counterparty, @originator) end |