-
Notifications
You must be signed in to change notification settings - Fork 20
Sign message
Bitcoin::MessageSign class provides the ability to sign and verify Bitcoin messages.
The following signature formats are supported:
| Format | Constant | Address types | Prefix |
|---|---|---|---|
| Legacy (Bitcoin Core sign-message) |
FORMAT_LEGACY (default) |
P2PKH | none |
| Simple (BIP-322) | FORMAT_SIMPLE |
P2WPKH, P2WSH, P2TR | smp |
| Full (BIP-322) | FORMAT_FULL |
All of the above | ful |
Note: Proof of Funds (
pof) variant of BIP-322 is not supported. Callingverify_messagewith apof-prefixed signature raisesNotImplementedError.
You can generate a legacy signature for any message with the following code:
message = 'Trust no one'
private_key = 'd97f5108f11cda6eeebaaa420fef0726b1f898060b98489fa3098463c0032866'
key = Bitcoin::Key.new(priv_key: private_key, key_type: Bitcoin::Key::TYPES[:compressed])
signature = Bitcoin::MessageSign.sign_message(key, message)
=> 'IPojfrX2dfPnH26UegfbGQQLrdK844DlHq5157/P6h57WyuS/Qsl+h/WSVGDF4MUi4rWSswW38oimDYfNNUBUOk='A prefix will be assigned to the message when it is signed. The default value of the prefix is Bitcoin Signed Message:\n, but it can also be specified with the prefix: keyword argument of sign_message like:
signature = Bitcoin::MessageSign.sign_message(key, message, prefix: "Dogecoin Signed Message:\n")Pass format: Bitcoin::MessageSign::FORMAT_SIMPLE together with the address being signed for. Currently a single-key API is supported (P2WPKH and P2TR key-path):
key = Bitcoin::Key.from_wif('L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k')
addr = key.to_p2wpkh
signature = Bitcoin::MessageSign.sign_message(
key, 'Hello World',
format: Bitcoin::MessageSign::FORMAT_SIMPLE,
address: addr
)
=> 'smpAkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI='For P2TR, the private key is automatically tweaked for the key-path spend, and the resulting witness contains a single 64-byte Schnorr signature (SIGHASH_DEFAULT).
Note: Producing a simple/full signature for multisig P2WSH addresses (which requires multiple signers) is not supported by the current single-key
sign_messageAPI. Verification of such signatures works as described below.
signature = Bitcoin::MessageSign.sign_message(
key, 'Hello World',
format: Bitcoin::MessageSign::FORMAT_FULL,
address: addr
)
=> 'fulAgAAAAA...'The full format serializes the entire to_sign transaction (rather than just the witness stack), and is the only choice when you want to attach non-default nVersion/nLockTime/nSequence to the proof.
You can validate a signature using the address and message. verify_message automatically detects the signature variant from its prefix (smp / ful) and falls back to the simple format if no prefix is present (for
backward compatibility with pre-finalized BIP-322 signatures).
Bitcoin::MessageSign.verify_message('15CRxFdyRpGZLW9w8HnHvVduizdL5jKNbs', signature, message)
=> trueSupported address types for verification:
- Legacy: P2PKH
- BIP-322 simple: P2WPKH, P2WSH (including multisig), P2TR (key-path)
- BIP-322 full: P2PKH, P2SH-P2WPKH, P2WPKH, P2WSH, P2SH-P2WSH, P2TR (key-path / script-path)
Errors and their meanings:
| Situation | Behavior |
|---|---|
Malformed base64, unknown prefix, structurally invalid to_sign
|
raises ArgumentError
|
| Well-formed but signature does not verify against the message/address | returns false
|
pof-prefixed signature |
raises NotImplementedError
|