Class: Tapyrus::PSTT::Input
- Inherits:
-
Object
- Object
- Tapyrus::PSTT::Input
- Defined in:
- lib/tapyrus/pstt/input.rb
Overview
An input map of a PSTT.
Instance Attribute Summary collapse
-
#bip32_derivations ⇒ Object
readonly
Returns the value of attribute bip32_derivations.
-
#final_script_sig ⇒ Tapyrus::Script
The complete scriptSig for this input.
-
#hash160_preimages ⇒ Object
readonly
Returns the value of attribute hash160_preimages.
-
#hash256_preimages ⇒ Object
readonly
Returns the value of attribute hash256_preimages.
-
#out_point ⇒ Tapyrus::OutPoint
PSTT_IN_PREVIOUS_TXID and PSTT_IN_OUTPUT_INDEX.
-
#partial_sigs ⇒ Object
readonly
Returns the value of attribute partial_sigs.
-
#proprietaries ⇒ Object
readonly
Returns the value of attribute proprietaries.
-
#record_order ⇒ Array[String]
The complete keys of the map this input was parsed from.
-
#redeem_script ⇒ Tapyrus::Script
The redeem script, if the output being spent is P2SH or CP2SH.
-
#required_height_locktime ⇒ Integer
The minimum block height the transaction's locktime must be.
-
#required_time_locktime ⇒ Integer
The minimum Unix timestamp the transaction's locktime must be.
-
#ripemd160_preimages ⇒ Object
readonly
Returns the value of attribute ripemd160_preimages.
-
#sequence ⇒ Integer
The sequence number.
-
#sha256_preimages ⇒ Object
readonly
Returns the value of attribute sha256_preimages.
-
#sighash_type ⇒ Integer
The sighash type signers must use for this input.
-
#unknowns ⇒ Object
readonly
Returns the value of attribute unknowns.
-
#utxo ⇒ Tapyrus::Tx
The complete previous transaction which contains the output being spent.
Class Method Summary collapse
-
.commits_to_all_sequences?(sig) ⇒ Boolean
Whether the given signature commits to the sequence number of every input.
-
.parse_from_records(records) ⇒ Tapyrus::PSTT::Input
Build an input map from the records of an input map.
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#final_sequence ⇒ Integer
The sequence number used in the transaction.
-
#finalize!(verified_sigs) ⇒ Tapyrus::PSTT::Input
Assemble the complete scriptSig from the collected records and store it in PSTT_IN_FINAL_SCRIPTSIG.
-
#finalized? ⇒ Boolean
Whether this input has been finalized.
-
#initialize(out_point: nil, utxo: nil, sequence: nil) ⇒ Input
constructor
A new instance of Input.
-
#preimages_for(type) ⇒ Hash
The preimages of the type.
-
#script_code ⇒ Tapyrus::Script
The scriptCode used to compute the signature hash of this input.
-
#signed? ⇒ Boolean
Whether this input has at least one signature.
-
#spent_script_pubkey ⇒ Tapyrus::Script
The scriptPubKey of the output being spent.
- #to_payload ⇒ Object
-
#to_records ⇒ Array[Tapyrus::PSTT::KeyValue]
The records of this input map.
-
#utxo_output ⇒ Tapyrus::TxOut
The output being spent, or nil if PSTT_IN_UTXO is absent.
-
#validate_for_sign! ⇒ Object
Check everything a Signer must verify before signing this input.
-
#validate_redeem_script! ⇒ Object
Check that the redeem script hashes to the value committed in the scriptPubKey being spent.
-
#validate_sighash_type! ⇒ Object
Check that this input requests a sighash type TIP-0174 defines, and that every signature it carries uses it.
-
#validate_signature_algo!(algo) ⇒ Object
Check that
algodoes not conflict with the scheme of the signatures already collected. -
#validate_utxo! ⇒ Object
Check that PSTT_IN_UTXO is present and identifies the output being spent.
Constructor Details
#initialize(out_point: nil, utxo: nil, sequence: nil) ⇒ Input
Returns a new instance of Input.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/tapyrus/pstt/input.rb', line 75 def initialize(out_point: nil, utxo: nil, sequence: nil) @out_point = out_point @utxo = utxo @sequence = sequence @partial_sigs = {} @bip32_derivations = {} @ripemd160_preimages = {} @sha256_preimages = {} @hash160_preimages = {} @hash256_preimages = {} @proprietaries = [] @unknowns = {} @record_order = [] end |
Instance Attribute Details
#bip32_derivations ⇒ Object (readonly)
Returns the value of attribute bip32_derivations.
43 44 45 |
# File 'lib/tapyrus/pstt/input.rb', line 43 def bip32_derivations @bip32_derivations end |
#final_script_sig ⇒ Tapyrus::Script
Returns the complete scriptSig for this input.
23 24 25 |
# File 'lib/tapyrus/pstt/input.rb', line 23 def final_script_sig @final_script_sig end |
#hash160_preimages ⇒ Object (readonly)
Returns the value of attribute hash160_preimages.
55 56 57 |
# File 'lib/tapyrus/pstt/input.rb', line 55 def hash160_preimages @hash160_preimages end |
#hash256_preimages ⇒ Object (readonly)
Returns the value of attribute hash256_preimages.
59 60 61 |
# File 'lib/tapyrus/pstt/input.rb', line 59 def hash256_preimages @hash256_preimages end |
#out_point ⇒ Tapyrus::OutPoint
Returns PSTT_IN_PREVIOUS_TXID and PSTT_IN_OUTPUT_INDEX.
7 8 9 |
# File 'lib/tapyrus/pstt/input.rb', line 7 def out_point @out_point end |
#partial_sigs ⇒ Object (readonly)
Returns the value of attribute partial_sigs.
39 40 41 |
# File 'lib/tapyrus/pstt/input.rb', line 39 def partial_sigs @partial_sigs end |
#proprietaries ⇒ Object (readonly)
Returns the value of attribute proprietaries.
63 64 65 |
# File 'lib/tapyrus/pstt/input.rb', line 63 def proprietaries @proprietaries end |
#record_order ⇒ Array[String]
Returns the complete keys of the map this input was parsed from. See Tapyrus::PSTT.order_records.
72 73 74 |
# File 'lib/tapyrus/pstt/input.rb', line 72 def record_order @record_order end |
#redeem_script ⇒ Tapyrus::Script
Returns the redeem script, if the output being spent is P2SH or CP2SH.
19 20 21 |
# File 'lib/tapyrus/pstt/input.rb', line 19 def redeem_script @redeem_script end |
#required_height_locktime ⇒ Integer
Returns the minimum block height the transaction's locktime must be.
35 36 37 |
# File 'lib/tapyrus/pstt/input.rb', line 35 def required_height_locktime @required_height_locktime end |
#required_time_locktime ⇒ Integer
Returns the minimum Unix timestamp the transaction's locktime must be.
31 32 33 |
# File 'lib/tapyrus/pstt/input.rb', line 31 def required_time_locktime @required_time_locktime end |
#ripemd160_preimages ⇒ Object (readonly)
Returns the value of attribute ripemd160_preimages.
47 48 49 |
# File 'lib/tapyrus/pstt/input.rb', line 47 def ripemd160_preimages @ripemd160_preimages end |
#sequence ⇒ Integer
Returns the sequence number. nil means the default 0xFFFFFFFF.
27 28 29 |
# File 'lib/tapyrus/pstt/input.rb', line 27 def sequence @sequence end |
#sha256_preimages ⇒ Object (readonly)
Returns the value of attribute sha256_preimages.
51 52 53 |
# File 'lib/tapyrus/pstt/input.rb', line 51 def sha256_preimages @sha256_preimages end |
#sighash_type ⇒ Integer
Returns the sighash type signers must use for this input.
15 16 17 |
# File 'lib/tapyrus/pstt/input.rb', line 15 def sighash_type @sighash_type end |
#unknowns ⇒ Object (readonly)
Returns the value of attribute unknowns.
67 68 69 |
# File 'lib/tapyrus/pstt/input.rb', line 67 def unknowns @unknowns end |
#utxo ⇒ Tapyrus::Tx
Returns the complete previous transaction which contains the output being spent.
11 12 13 |
# File 'lib/tapyrus/pstt/input.rb', line 11 def utxo @utxo end |
Class Method Details
.commits_to_all_sequences?(sig) ⇒ Boolean
Whether the given signature commits to the sequence number of every input. A SIGHASH_ALL signature without SIGHASH_ANYONECANPAY does.
329 330 331 332 333 |
# File 'lib/tapyrus/pstt/input.rb', line 329 def self.commits_to_all_sequences?(sig) return false if sig.nil? || sig.empty? hash_type = sig[-1].unpack1("C") (hash_type & SIGHASH_TYPE[:anyonecanpay]).zero? && (hash_type & 0x1f) == SIGHASH_TYPE[:all] end |
.parse_from_records(records) ⇒ Tapyrus::PSTT::Input
Build an input map from the records of an input map.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/tapyrus/pstt/input.rb', line 94 def self.parse_from_records(records) input = new txid = nil index = nil records.each do |record| if InputTypes::RESERVED.include?(record.type) raise Error, format("Input key type 0x%02x is reserved.", record.type) end case record.type when InputTypes::UTXO PSTT.validate_empty_keydata!(record) input.utxo = parse_utxo(record.value) when InputTypes::PARTIAL_SIG PSTT.validate_pubkey_keydata!(record) if record.value.empty? raise Error, "PSTT_IN_PARTIAL_SIG must carry a signature followed by the 1-byte sighash type." end input.partial_sigs[record.keydata.bth] = record.value when InputTypes::SIGHASH_TYPE PSTT.validate_empty_keydata!(record) input.sighash_type = PSTT.read_u32(record) PSTT.validate_sighash_type!(input.sighash_type) when InputTypes::REDEEM_SCRIPT PSTT.validate_empty_keydata!(record) input.redeem_script = PSTT.parse_field("PSTT_IN_REDEEM_SCRIPT") { Tapyrus::Script.parse_from_payload(record.value) } when InputTypes::BIP32_DERIVATION PSTT.validate_pubkey_keydata!(record) input.bip32_derivations[record.keydata.bth] = KeyOriginInfo.parse_from_payload(record.value) when InputTypes::FINAL_SCRIPTSIG PSTT.validate_empty_keydata!(record) input.final_script_sig = PSTT.parse_field("PSTT_IN_FINAL_SCRIPTSIG") { Tapyrus::Script.parse_from_payload(record.value) } when InputTypes::RIPEMD160, InputTypes::HASH160 raise Error, "The preimage hash must be 20 bytes." unless record.keydata.bytesize == 20 input.preimages_for(record.type)[record.keydata.bth] = record.value when InputTypes::SHA256, InputTypes::HASH256 raise Error, "The preimage hash must be 32 bytes." unless record.keydata.bytesize == 32 input.preimages_for(record.type)[record.keydata.bth] = record.value when InputTypes::PREVIOUS_TXID PSTT.validate_empty_keydata!(record) PSTT.validate_value_size!(record, 32) txid = record.value.bth when InputTypes::OUTPUT_INDEX PSTT.validate_empty_keydata!(record) index = PSTT.read_u32(record) when InputTypes::SEQUENCE PSTT.validate_empty_keydata!(record) input.sequence = PSTT.read_u32(record) when InputTypes::REQUIRED_TIME_LOCKTIME PSTT.validate_empty_keydata!(record) input.required_time_locktime = PSTT.read_u32(record) PSTT.validate_time_locktime!(input.required_time_locktime) when InputTypes::REQUIRED_HEIGHT_LOCKTIME PSTT.validate_empty_keydata!(record) input.required_height_locktime = PSTT.read_u32(record) PSTT.validate_height_locktime!(input.required_height_locktime) when InputTypes::PROPRIETARY input.proprietaries << Proprietary.parse_from_record(record) else input.unknowns[record.key] = record.value end end raise Error, "PSTT_IN_PREVIOUS_TXID is required." unless txid raise Error, "PSTT_IN_OUTPUT_INDEX is required." unless index input.out_point = Tapyrus::OutPoint.new(txid, index) input.record_order = records.map(&:key) input.validate_sighash_type! input end |
Instance Method Details
#==(other) ⇒ Object
372 373 374 |
# File 'lib/tapyrus/pstt/input.rb', line 372 def ==(other) other.is_a?(Input) && to_payload == other.to_payload end |
#final_sequence ⇒ Integer
Returns the sequence number used in the transaction.
218 219 220 |
# File 'lib/tapyrus/pstt/input.rb', line 218 def final_sequence sequence || Tapyrus::TxIn::SEQUENCE_FINAL end |
#finalize!(verified_sigs) ⇒ Tapyrus::PSTT::Input
Assemble the complete scriptSig from the collected records and store it in PSTT_IN_FINAL_SCRIPTSIG. The records which are no longer needed are removed.
342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/tapyrus/pstt/input.rb', line 342 def finalize!(verified_sigs) return self if finalized? validate_for_sign! self.final_script_sig = build_final_script_sig(verified_sigs) partial_sigs.clear bip32_derivations.clear ripemd160_preimages.clear sha256_preimages.clear hash160_preimages.clear hash256_preimages.clear self.sighash_type = nil self.redeem_script = nil self end |
#finalized? ⇒ Boolean
Returns whether this input has been finalized.
234 235 236 |
# File 'lib/tapyrus/pstt/input.rb', line 234 def finalized? !final_script_sig.nil? end |
#preimages_for(type) ⇒ Hash
Returns the preimages of the type.
359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/tapyrus/pstt/input.rb', line 359 def preimages_for(type) case type when InputTypes::RIPEMD160 ripemd160_preimages when InputTypes::SHA256 sha256_preimages when InputTypes::HASH160 hash160_preimages when InputTypes::HASH256 hash256_preimages end end |
#script_code ⇒ Tapyrus::Script
The scriptCode used to compute the signature hash of this input.
241 242 243 244 245 246 247 |
# File 'lib/tapyrus/pstt/input.rb', line 241 def script_code script = spent_script_pubkey return script unless script.p2sh? || script.cp2sh? raise Error, "PSTT_IN_REDEEM_SCRIPT is required to spend a P2SH or CP2SH output." unless redeem_script validate_redeem_script! redeem_script end |
#signed? ⇒ Boolean
Returns whether this input has at least one signature.
229 230 231 |
# File 'lib/tapyrus/pstt/input.rb', line 229 def signed? !partial_sigs.empty? end |
#spent_script_pubkey ⇒ Tapyrus::Script
The scriptPubKey of the output being spent.
252 253 254 255 |
# File 'lib/tapyrus/pstt/input.rb', line 252 def spent_script_pubkey validate_utxo! utxo_output.script_pubkey end |
#to_payload ⇒ Object
213 214 215 |
# File 'lib/tapyrus/pstt/input.rb', line 213 def to_payload PSTT.serialize_map(to_records, record_order) end |
#to_records ⇒ Array[Tapyrus::PSTT::KeyValue]
Returns the records of this input map.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/tapyrus/pstt/input.rb', line 179 def to_records validate_sighash_type! records = [] records << KeyValue.new(InputTypes::UTXO, "".b, utxo.to_payload) if utxo partial_sigs.each { |pubkey, sig| records << KeyValue.new(InputTypes::PARTIAL_SIG, pubkey.htb, sig) } records << KeyValue.new(InputTypes::SIGHASH_TYPE, "".b, [sighash_type].pack("V")) if sighash_type records << KeyValue.new(InputTypes::REDEEM_SCRIPT, "".b, redeem_script.to_payload) if redeem_script bip32_derivations.each do |pubkey, info| records << KeyValue.new(InputTypes::BIP32_DERIVATION, pubkey.htb, info.to_payload) end records << KeyValue.new(InputTypes::FINAL_SCRIPTSIG, "".b, final_script_sig.to_payload) if final_script_sig { InputTypes::RIPEMD160 => ripemd160_preimages, InputTypes::SHA256 => sha256_preimages, InputTypes::HASH160 => hash160_preimages, InputTypes::HASH256 => hash256_preimages }.each { |type, preimages| preimages.each { |hash, image| records << KeyValue.new(type, hash.htb, image) } } raise Error, "PSTT_IN_PREVIOUS_TXID is required." unless out_point records << KeyValue.new(InputTypes::PREVIOUS_TXID, "".b, out_point.tx_hash.htb) records << KeyValue.new(InputTypes::OUTPUT_INDEX, "".b, [out_point.index].pack("V")) records << KeyValue.new(InputTypes::SEQUENCE, "".b, [sequence].pack("V")) if sequence if required_time_locktime PSTT.validate_time_locktime!(required_time_locktime) records << KeyValue.new(InputTypes::REQUIRED_TIME_LOCKTIME, "".b, [required_time_locktime].pack("V")) end if required_height_locktime PSTT.validate_height_locktime!(required_height_locktime) records << KeyValue.new(InputTypes::REQUIRED_HEIGHT_LOCKTIME, "".b, [required_height_locktime].pack("V")) end proprietaries.each { |p| records << p.to_record(InputTypes::PROPRIETARY) } unknowns.each { |key, value| records << unknown_record(key, value) } records end |
#utxo_output ⇒ Tapyrus::TxOut
Returns the output being spent, or nil if PSTT_IN_UTXO is absent.
223 224 225 226 |
# File 'lib/tapyrus/pstt/input.rb', line 223 def utxo_output return nil unless utxo utxo.outputs[out_point.index] end |
#validate_for_sign! ⇒ Object
Check everything a Signer must verify before signing this input.
307 308 309 310 311 |
# File 'lib/tapyrus/pstt/input.rb', line 307 def validate_for_sign! validate_utxo! validate_redeem_script! validate_sighash_type! end |
#validate_redeem_script! ⇒ Object
Check that the redeem script hashes to the value committed in the scriptPubKey being spent.
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/tapyrus/pstt/input.rb', line 267 def validate_redeem_script! return unless redeem_script script = spent_script_pubkey committed = if script.p2sh? script.chunks[1].pushed_data elsif script.cp2sh? script.chunks[3].pushed_data else raise Error, "PSTT_IN_REDEEM_SCRIPT is set but the output being spent is neither P2SH nor CP2SH." end unless redeem_script.to_hash160 == committed.bth raise Error, "PSTT_IN_REDEEM_SCRIPT does not hash to the value committed in the scriptPubKey." end end |
#validate_sighash_type! ⇒ Object
Check that this input requests a sighash type TIP-0174 defines, and that every signature it carries uses it. TIP-0174 obliges a Signer to use PSTT_IN_SIGHASH_TYPE, so an input which carries a signature made with another type contradicts itself: the signature covers a different transaction than the input asks for.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/tapyrus/pstt/input.rb', line 288 def validate_sighash_type! return unless sighash_type PSTT.validate_sighash_type!(sighash_type) partial_sigs.each do |pubkey, sig| next if sig.empty? hash_type = sig[-1].unpack1("C") next if hash_type == sighash_type raise Error, format( "PSTT_IN_PARTIAL_SIG for %s uses sighash type 0x%x, but PSTT_IN_SIGHASH_TYPE requires 0x%x.", pubkey, hash_type, sighash_type ) end end |
#validate_signature_algo!(algo) ⇒ Object
Check that algo does not conflict with the scheme of the signatures already collected.
Tapyrus does not allow mixing ECDSA and Schnorr signatures within a single OP_CHECKMULTISIG.
317 318 319 320 321 322 323 |
# File 'lib/tapyrus/pstt/input.rb', line 317 def validate_signature_algo!(algo) return if partial_sigs.empty? current = partial_sigs.values.first.bytesize == 65 ? :schnorr : :ecdsa unless current == algo raise Error, "This input already has #{current} signatures. #{algo} signatures must not be mixed." end end |
#validate_utxo! ⇒ Object
Check that PSTT_IN_UTXO is present and identifies the output being spent.
259 260 261 262 263 |
# File 'lib/tapyrus/pstt/input.rb', line 259 def validate_utxo! raise Error, "PSTT_IN_UTXO is required." unless utxo raise Error, "The txid of PSTT_IN_UTXO does not match PSTT_IN_PREVIOUS_TXID." unless utxo.txid == out_point.txid raise Error, "PSTT_IN_OUTPUT_INDEX does not exist in PSTT_IN_UTXO." unless utxo.outputs[out_point.index] end |