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.
93 94 95 96 97 98 99 |
# File 'lib/bitcoin/message_sign.rb', line 93 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
133 134 135 136 137 138 139 140 141 |
# File 'lib/bitcoin/message_sign.rb', line 133 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
120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/bitcoin/message_sign.rb', line 120 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 81 82 83 84 85 86 87 88 89 90 |
# 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 Exception false end elsif addr_script.witness_program? # BIP322 verification digest = (, prefix: prefix, legacy: false) begin # Full tx = Bitcoin::Tx.parse_from_payload(sig) validate_to_sign_tx!(tx) to_spend = to_spend_tx(digest, address) return false unless tx.in[0].out_point.tx_hash == to_spend.tx_hash rescue Exception # Simple tx = to_sign_tx(digest, address) tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig) end 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 |