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.
83 84 85 86 87 88 89 |
# File 'lib/bitcoin/message_sign.rb', line 83 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
115 116 117 118 119 120 121 122 123 |
# File 'lib/bitcoin/message_sign.rb', line 115 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
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/bitcoin/message_sign.rb', line 102 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 80 |
# 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) flags = Bitcoin::STANDARD_SCRIPT_VERIFY_FLAGS interpreter = Bitcoin::ScriptInterpreter.new(flags: flags, 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 |