Module: Bitcoin::MessageSign
- Defined in:
- lib/bitcoin/message_sign.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- FORMAT_LEGACY =
:legacy
- FORMAT_SIMPLE =
:simple
- FORMAT_FULL =
:full
Class Method Summary collapse
-
.message_hash(message, prefix: Bitcoin.chain_params.message_magic, legacy: true) ⇒ Object
Hashes a message for signing and verification.
-
.sign_message(key, message, prefix: Bitcoin.chain_params.message_magic, format: FORMAT_LEGACY, address: nil) ⇒ String
Sign a message.
- .to_sign_tx(digest, addr) ⇒ Object
- .to_spend_tx(digest, addr) ⇒ Object
-
.verify_message(address, signature, message, prefix: Bitcoin.chain_params.message_magic) ⇒ Boolean
Verify a signed message.
Class Method Details
.message_hash(message, prefix: Bitcoin.chain_params.message_magic, legacy: true) ⇒ Object
Hashes a message for signing and verification.
82 83 84 85 86 87 88 |
# File 'lib/bitcoin/message_sign.rb', line 82 def (, prefix: Bitcoin.chain_params., legacy: true) if legacy Bitcoin.double_sha256(Bitcoin.pack_var_string(prefix) << Bitcoin.pack_var_string()) else Bitcoin.tagged_hash('BIP0322-signed-message', ) end end |
.sign_message(key, message, prefix: Bitcoin.chain_params.message_magic, format: FORMAT_LEGACY, address: nil) ⇒ String
Sign a message.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/bitcoin/message_sign.rb', line 20 def (key, , prefix: Bitcoin.chain_params., format: FORMAT_LEGACY, address: nil) validate_format!(format) digest = (, prefix: prefix, legacy: format == FORMAT_LEGACY) sig = case format when FORMAT_LEGACY key.sign_compact(digest) else validate_address!(address) addr = Bitcoin::Script.parse_from_addr(address) sig_ver, algo = if addr.p2wpkh? [:witness_v0, :ecdsa] elsif addr.p2tr? [:taproot, :schnorr] else raise ArgumentError "#{address} dose not supported." end tx = to_sign_tx(digest, address) prev_out = Bitcoin::TxOut.new(script_pubkey: addr) sighash = tx.sighash_for_input(0, addr, sig_version: sig_ver, amount: 0, prevouts: [prev_out]) sig = key.sign(sighash, algo: algo) + [Bitcoin::SIGHASH_TYPE[:all]].pack('C') tx.in[0].script_witness.stack << sig tx.in[0].script_witness.stack << key.pubkey.htb format == FORMAT_SIMPLE ? tx.in[0].script_witness.to_payload : tx.to_payload end Base64.strict_encode64(sig) end |
.to_sign_tx(digest, addr) ⇒ Object
114 115 116 117 118 119 120 121 122 |
# File 'lib/bitcoin/message_sign.rb', line 114 def to_sign_tx(digest, addr) tx = Bitcoin::Tx.new tx.version = 0 tx.lock_time = 0 prev_out = Bitcoin::OutPoint.from_txid(to_spend_tx(digest, addr).txid, 0) tx.in << Bitcoin::TxIn.new(out_point: prev_out, sequence: 0) tx.out << Bitcoin::TxOut.new(script_pubkey: Bitcoin::Script.new << Bitcoin::Opcodes::OP_RETURN) tx end |
.to_spend_tx(digest, addr) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/bitcoin/message_sign.rb', line 101 def to_spend_tx(digest, addr) validate_address!(addr) = Bitcoin::Script.parse_from_addr(addr) tx = Bitcoin::Tx.new tx.version = 0 tx.lock_time = 0 prev_out = Bitcoin::OutPoint.create_coinbase_outpoint script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 << digest tx.in << Bitcoin::TxIn.new(out_point: prev_out, sequence: 0, script_sig: script_sig) tx.out << Bitcoin::TxOut.new(script_pubkey: ) tx end |
.verify_message(address, signature, message, prefix: Bitcoin.chain_params.message_magic) ⇒ Boolean
Verify a signed message.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/bitcoin/message_sign.rb', line 52 def (address, signature, , prefix: Bitcoin.chain_params.) addr_script = Bitcoin::Script.parse_from_addr(address) begin sig = Base64.strict_decode64(signature) rescue ArgumentError raise ArgumentError, 'Invalid signature' end if addr_script.p2pkh? begin # Legacy verification pubkey = Bitcoin::Key.recover_compact((, prefix: prefix, legacy: true), sig) return false unless pubkey pubkey.to_p2pkh == address rescue RuntimeError return false end elsif addr_script.witness_program? # BIP322 verification tx = to_sign_tx((, prefix: prefix, legacy: false), address) tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig) script_pubkey = Bitcoin::Script.parse_from_addr(address) tx_out = Bitcoin::TxOut.new(script_pubkey: script_pubkey) interpreter = Bitcoin::ScriptInterpreter.new(checker: Bitcoin::TxChecker.new(tx: tx, input_index: 0, prevouts: [tx_out])) interpreter.verify_script(Bitcoin::Script.new, script_pubkey, tx.in[0].script_witness) else raise ArgumentError, "This address unsupported." end end |