Class: Bitcoin::PSBT::Input

Inherits:
Object show all
Defined in:
lib/bitcoin/psbt/input.rb

Overview

Class for PSBTs which contain per-input information

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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_sigObject

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_witnessObject

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_pathsObject

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_utxoObject

Bitcoin::Tx



8
9
10
# File 'lib/bitcoin/psbt/input.rb', line 8

def non_witness_utxo
  @non_witness_utxo
end

#partial_sigsObject

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_scriptObject

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_typeObject

Returns the value of attribute sighash_type.



16
17
18
# File 'lib/bitcoin/psbt/input.rb', line 16

def sighash_type
  @sighash_type
end

#unknownsObject

Returns the value of attribute unknowns.



17
18
19
# File 'lib/bitcoin/psbt/input.rb', line 17

def unknowns
  @unknowns
end

#witness_scriptObject

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_utxoObject

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.

Parameters:

  • buf (StringIO)

    psbt buffer.

Returns:

  • (Bitcoin::PSBTInput)

    psbt input.



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
# File 'lib/bitcoin/psbt/input.rb', line 30

def self.parse_from_buf(buf)
  input = self.new
  until buf.eof?
    key_len = Bitcoin.unpack_var_int_from_io(buf)
    break if key_len == 0
    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, '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, '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 '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, '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, '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, 'Duplicate Key, pubkey derivation path already provided.' if input.hd_key_paths[key.bth]
      input.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.parse_from_payload(key, value)
    when PSBT_IN_TYPES[:script_sig]
      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, '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
  input
end

Instance Method Details

#add_sig(pubkey, sig) ⇒ Object

add signature as partial sig.

Parameters:

  • pubkey (String)

    a public key with hex format.

  • sig (String)

    a signature.

Raises:

  • (ArgumentError)


110
111
112
113
114
# File 'lib/bitcoin/psbt/input.rb', line 110

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.

Returns:



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/bitcoin/psbt/input.rb', line 141

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.bth}
    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.

Parameters:

  • psbi (Bitcoin::PSBT::Input)

    PSBT input to be combined which must have same property in PSBT Input.

Returns:

Raises:

  • (ArgumentError)


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/bitcoin/psbt/input.rb', line 119

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 = unknowns.merge(psbi.unknowns)
  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

#sane?Boolean

Sanity check

Returns:

  • (Boolean)


100
101
102
103
104
105
# File 'lib/bitcoin/psbt/input.rb', line 100

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

#to_payloadObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/bitcoin/psbt/input.rb', line 81

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.to_even_length_hex.htb
  payload
end