Class: Bitcoin::PSBT::Input
Overview
Class for PSBTs which contain per-input information
Instance Attribute Summary collapse
-
#final_script_sig ⇒ Object
Returns the value of attribute final_script_sig.
-
#final_script_witness ⇒ Object
Returns the value of attribute final_script_witness.
-
#hd_key_paths ⇒ Object
Returns the value of attribute hd_key_paths.
-
#non_witness_utxo ⇒ Object
Bitcoin::Tx.
-
#partial_sigs ⇒ Object
Returns the value of attribute partial_sigs.
-
#redeem_script ⇒ Object
Returns the value of attribute redeem_script.
-
#sighash_type ⇒ Object
Returns the value of attribute sighash_type.
-
#unknowns ⇒ Object
Returns the value of attribute unknowns.
-
#witness_script ⇒ Object
Returns the value of attribute witness_script.
-
#witness_utxo ⇒ Object
Bitcoin::TxOut.
Class Method Summary collapse
-
.parse_from_buf(buf) ⇒ Bitcoin::PSBTInput
parse PSBT input data form buffer.
Instance Method Summary collapse
-
#add_sig(pubkey, sig) ⇒ Object
add signature as partial sig.
-
#finalize! ⇒ Bitcoin::PSBT::Input
finalize input.
-
#initialize(non_witness_utxo: nil, witness_utxo: nil) ⇒ Input
constructor
A new instance of Input.
-
#merge(psbi) ⇒ Bitcoin::PSBT::Input
merge two PSBT inputs to create one PSBT.
-
#ready_to_sign?(utxo) ⇒ Boolean
Check whether the signer can sign this input.
-
#sane? ⇒ Boolean
Sanity check.
-
#signed? ⇒ Boolean
Checks whether a PSBTInput is already signed.
- #to_payload ⇒ Object
-
#valid_non_witness_input?(utxo) ⇒ Boolean
Check whether input’s scriptPubkey is correct witness.
-
#valid_witness_input? ⇒ Boolean
Check whether input’s scriptPubkey is correct witness.
Constructor Details
#initialize(non_witness_utxo: nil, witness_utxo: nil) ⇒ Input
Returns a new instance of Input.
19 20 21 22 23 24 25 |
# File 'lib/bitcoin/psbt/input.rb', line 19 def initialize(non_witness_utxo: nil, witness_utxo: nil) @non_witness_utxo = non_witness_utxo @witness_utxo = witness_utxo @partial_sigs = {} @hd_key_paths = {} @unknowns = {} end |
Instance Attribute Details
#final_script_sig ⇒ Object
Returns the value of attribute final_script_sig.
12 13 14 |
# File 'lib/bitcoin/psbt/input.rb', line 12 def final_script_sig @final_script_sig end |
#final_script_witness ⇒ Object
Returns the value of attribute final_script_witness.
13 14 15 |
# File 'lib/bitcoin/psbt/input.rb', line 13 def final_script_witness @final_script_witness end |
#hd_key_paths ⇒ Object
Returns the value of attribute hd_key_paths.
14 15 16 |
# File 'lib/bitcoin/psbt/input.rb', line 14 def hd_key_paths @hd_key_paths end |
#non_witness_utxo ⇒ Object
Bitcoin::Tx
8 9 10 |
# File 'lib/bitcoin/psbt/input.rb', line 8 def non_witness_utxo @non_witness_utxo end |
#partial_sigs ⇒ Object
Returns the value of attribute partial_sigs.
15 16 17 |
# File 'lib/bitcoin/psbt/input.rb', line 15 def partial_sigs @partial_sigs end |
#redeem_script ⇒ Object
Returns the value of attribute redeem_script.
10 11 12 |
# File 'lib/bitcoin/psbt/input.rb', line 10 def redeem_script @redeem_script end |
#sighash_type ⇒ Object
Returns the value of attribute sighash_type.
16 17 18 |
# File 'lib/bitcoin/psbt/input.rb', line 16 def sighash_type @sighash_type end |
#unknowns ⇒ Object
Returns the value of attribute unknowns.
17 18 19 |
# File 'lib/bitcoin/psbt/input.rb', line 17 def unknowns @unknowns end |
#witness_script ⇒ Object
Returns the value of attribute witness_script.
11 12 13 |
# File 'lib/bitcoin/psbt/input.rb', line 11 def witness_script @witness_script end |
#witness_utxo ⇒ Object
Bitcoin::TxOut
9 10 11 |
# File 'lib/bitcoin/psbt/input.rb', line 9 def witness_utxo @witness_utxo end |
Class Method Details
.parse_from_buf(buf) ⇒ Bitcoin::PSBTInput
parse PSBT input data form buffer.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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 91 92 |
# File 'lib/bitcoin/psbt/input.rb', line 30 def self.parse_from_buf(buf) input = self.new found_sep = false until buf.eof? key_len = Bitcoin.unpack_var_int_from_io(buf) if key_len == 0 found_sep = true break end key_type = buf.read(1).unpack('C').first key = buf.read(key_len - 1) value = buf.read(Bitcoin.unpack_var_int_from_io(buf)) case key_type when PSBT_IN_TYPES[:non_witness_utxo] raise ArgumentError, 'Invalid non-witness utxo typed key.' unless key_len == 1 raise ArgumentError, 'Duplicate Key, input non-witness utxo already provided.' if input.non_witness_utxo input.non_witness_utxo = Bitcoin::Tx.parse_from_payload(value) when PSBT_IN_TYPES[:witness_utxo] raise ArgumentError, 'Invalid input witness utxo typed key.' unless key_len == 1 raise ArgumentError, 'Duplicate Key, input witness utxo already provided.' if input.witness_utxo input.witness_utxo = Bitcoin::TxOut.parse_from_payload(value) when PSBT_IN_TYPES[:partial_sig] if key.size != Bitcoin::Key::PUBLIC_KEY_SIZE && key.size != Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE raise ArgumentError, 'Size of key was not the expected size for the type partial signature pubkey.' end pubkey = Bitcoin::Key.new(pubkey: key.bth) raise ArgumentError, 'Invalid pubkey.' unless pubkey.fully_valid_pubkey? raise ArgumentError, 'Duplicate Key, input partial signature for pubkey already provided.' if input.partial_sigs[pubkey.pubkey] input.partial_sigs[pubkey.pubkey] = value when PSBT_IN_TYPES[:sighash] raise ArgumentError, 'Invalid input sighash type typed key.' unless key_len == 1 raise ArgumentError 'Duplicate Key, input sighash type already provided.' if input.sighash_type input.sighash_type = value.unpack('I').first when PSBT_IN_TYPES[:redeem_script] raise ArgumentError, 'Invalid redeemscript typed key.' unless key_len == 1 raise ArgumentError, 'Duplicate Key, input redeemScript already provided.' if input.redeem_script input.redeem_script = Bitcoin::Script.parse_from_payload(value) when PSBT_IN_TYPES[:witness_script] raise ArgumentError, 'Invalid witnessscript typed key.' unless key_len == 1 raise ArgumentError, 'Duplicate Key, input witnessScript already provided.' if input.witness_script input.witness_script = Bitcoin::Script.parse_from_payload(value) when PSBT_IN_TYPES[:bip32_derivation] raise ArgumentError, 'Invalid bip32 typed key.' unless key_len raise ArgumentError, 'Duplicate Key, pubkey derivation path already provided.' if input.hd_key_paths[key.bth] input.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.new(key, Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value)) when PSBT_IN_TYPES[:script_sig] raise ArgumentError, 'Invalid final scriptsig typed key.' unless key_len == 1 raise ArgumentError, 'Duplicate Key, input final scriptSig already provided.' if input.final_script_sig input.final_script_sig = Bitcoin::Script.parse_from_payload(value) when PSBT_IN_TYPES[:script_witness] raise ArgumentError, 'Invalid final script witness typed key.' unless key_len == 1 raise ArgumentError, 'Duplicate Key, input final scriptWitness already provided.' if input.final_script_witness input.final_script_witness = Bitcoin::ScriptWitness.parse_from_payload(value) else unknown_key = ([key_type].pack('C') + key).bth raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if input.unknowns[unknown_key] input.unknowns[unknown_key] = value end end raise ArgumentError, 'Separator is missing at the end of an input map.' unless found_sep input end |
Instance Method Details
#add_sig(pubkey, sig) ⇒ Object
add signature as partial sig.
158 159 160 161 162 |
# File 'lib/bitcoin/psbt/input.rb', line 158 def add_sig(pubkey, sig) raise ArgumentError, 'The sighash in signature is invalid.' unless sig.unpack('C*')[-1] == sighash_type raise ArgumentError, 'Duplicate Key, input partial signature for pubkey already provided.' if partial_sigs[pubkey] partial_sigs[pubkey] = sig end |
#finalize! ⇒ Bitcoin::PSBT::Input
finalize input. TODO This feature is experimental and support only multisig.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/bitcoin/psbt/input.rb', line 189 def finalize! if non_witness_utxo self.final_script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 if redeem_script.multisig? partial_sigs.values.each {|sig|final_script_sig << sig} final_script_sig << redeem_script.to_payload.bth self.partial_sigs = {} self.hd_key_paths = {} self.redeem_script = nil self.sighash_type = nil else if redeem_script self.final_script_sig = Bitcoin::Script.parse_from_payload(Bitcoin::Script.pack_pushdata(redeem_script.to_payload)) self.redeem_script = nil end if witness_script self.final_script_witness = Bitcoin::ScriptWitness.new final_script_witness.stack << '' if witness_script.multisig? partial_sigs.values.each {|sig| final_script_witness.stack << sig} final_script_witness.stack << witness_script.to_payload self.witness_script = nil end self.sighash_type = nil self.partial_sigs = {} self.hd_key_paths = {} end self end |
#merge(psbi) ⇒ Bitcoin::PSBT::Input
merge two PSBT inputs to create one PSBT.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/bitcoin/psbt/input.rb', line 167 def merge(psbi) raise ArgumentError, 'The argument psbt must be an instance of Bitcoin::PSBT::Input.' unless psbi.is_a?(Bitcoin::PSBT::Input) raise ArgumentError, 'The Partially Signed Input\'s non_witness_utxo are different.' unless non_witness_utxo == psbi.non_witness_utxo raise ArgumentError, 'The Partially Signed Input\'s witness_utxo are different.' unless witness_utxo == psbi.witness_utxo raise ArgumentError, 'The Partially Signed Input\'s sighash_type are different.' unless sighash_type == psbi.sighash_type raise ArgumentError, 'The Partially Signed Input\'s redeem_script are different.' unless redeem_script == psbi.redeem_script raise ArgumentError, 'The Partially Signed Input\'s witness_script are different.' unless witness_script == psbi.witness_script combined = Bitcoin::PSBT::Input.new(non_witness_utxo: non_witness_utxo, witness_utxo: witness_utxo) combined.unknowns = Hash[unknowns.merge(psbi.unknowns).sort] combined.redeem_script = redeem_script combined.witness_script = witness_script combined.sighash_type = sighash_type sigs = Hash[partial_sigs.merge(psbi.partial_sigs)] redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script && redeem_script.multisig? witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script && witness_script.multisig? combined.hd_key_paths = hd_key_paths.merge(psbi.hd_key_paths) combined end |
#ready_to_sign?(utxo) ⇒ Boolean
Check whether the signer can sign this input.
143 144 145 146 147 |
# File 'lib/bitcoin/psbt/input.rb', line 143 def ready_to_sign?(utxo) return false unless sane? return valid_witness_input? if witness_utxo valid_non_witness_input?(utxo) # non_witness_utxo end |
#sane? ⇒ Boolean
Sanity check
114 115 116 117 118 119 |
# File 'lib/bitcoin/psbt/input.rb', line 114 def sane? return false if non_witness_utxo && witness_utxo return false if witness_script && witness_utxo.nil? return false if final_script_witness && witness_utxo.nil? true end |
#signed? ⇒ Boolean
Checks whether a PSBTInput is already signed.
151 152 153 |
# File 'lib/bitcoin/psbt/input.rb', line 151 def signed? final_script_sig || final_script_witness end |
#to_payload ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/bitcoin/psbt/input.rb', line 94 def to_payload payload = '' payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value: non_witness_utxo.to_payload) if non_witness_utxo payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_utxo], value: witness_utxo.to_payload) if witness_utxo if final_script_sig.nil? && final_script_witness.nil? payload << partial_sigs.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:partial_sig], key: k.htb, value: v)}.join payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:sighash], value: [sighash_type].pack('I')) if sighash_type payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:redeem_script], value: redeem_script.to_payload) if redeem_script payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_script], value: witness_script.to_payload) if witness_script payload << hd_key_paths.values.map(&:to_payload).join end payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:script_sig], value: final_script_sig.to_payload) if final_script_sig payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:script_witness], value: final_script_witness.to_payload) if final_script_witness payload << unknowns.map {|k,v|Bitcoin.pack_var_int(k.htb.bytesize) << k.htb << Bitcoin.pack_var_int(v.bytesize) << v}.join payload << PSBT_SEPARATOR.itb payload end |
#valid_non_witness_input?(utxo) ⇒ Boolean
Check whether input’s scriptPubkey is correct witness.
136 137 138 |
# File 'lib/bitcoin/psbt/input.rb', line 136 def valid_non_witness_input?(utxo) utxo.script_pubkey.p2sh? && redeem_script.to_p2sh == utxo.script_pubkey end |
#valid_witness_input? ⇒ Boolean
Check whether input’s scriptPubkey is correct witness.
123 124 125 126 127 128 129 130 131 132 |
# File 'lib/bitcoin/psbt/input.rb', line 123 def valid_witness_input? return true if witness_utxo&.script_pubkey.p2wpkh? # P2WPKH return true if witness_utxo&.script_pubkey.p2wsh? && witness_utxo&.script_pubkey == redeem_script.to_p2wsh # P2WSH # segwit nested in P2SH if witness_utxo&.script_pubkey.p2sh? && redeem_script&.witness_program? && redeem_script.to_p2sh == witness_utxo&.script_pubkey return true if redeem_script.p2wpkh?# nested p2wpkh return true if witness_script&.to_sha256 == redeem_script.witness_data[1].bth # nested p2wsh end false end |