Class: BTC::TransactionInput

Inherits:
Object
  • Object
show all
Defined in:
lib/btcruby/transaction_input.rb

Constant Summary collapse

INVALID_INDEX =

aka "(unsigned int) -1" in BitcoinQT.

0xFFFFFFFF
MAX_SEQUENCE =
0xFFFFFFFF
ZERO_HASH256 =
"\x00".b*32

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data: nil, stream: nil, dictionary: nil, previous_hash: ZERO_HASH256, previous_id: nil, previous_index: INVALID_INDEX, coinbase_data: nil, signature_script: BTC::Script.new, sequence: MAX_SEQUENCE, transaction: nil, transaction_output: nil, value: nil) ⇒ TransactionInput

Initializes transaction input with its attributes. Every attribute has a valid default value.



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
93
94
95
96
97
98
99
# File 'lib/btcruby/transaction_input.rb', line 59

def initialize(data: nil,
               stream: nil,
               dictionary: nil,
               previous_hash: ZERO_HASH256,
               previous_id: nil,
               previous_index: INVALID_INDEX,
               coinbase_data: nil,
               signature_script: BTC::Script.new,
               sequence: MAX_SEQUENCE,

               # optional attributes
               transaction: nil,
               transaction_output: nil,
               value: nil)
  if stream || data
    init_with_stream(stream || StringIO.new(data))
  elsif dictionary
    init_with_dictionary(dictionary)
  else
    @previous_hash    = previous_hash    || ZERO_HASH256
    @previous_hash    = BTC.hash_from_id(previous_id) if previous_id
    @previous_index   = previous_index   || INVALID_INDEX
    @coinbase_data    = coinbase_data    || "".b
    @signature_script = signature_script || BTC::Script.new
    @sequence         = sequence         || MAX_SEQUENCE
  end

  @transaction = transaction

  @transaction_output = transaction_output
  # Try to set outpoint data based on transaction output.
  if @transaction_output
    if @previous_hash == ZERO_HASH256
      @previous_hash = @transaction_output.transaction_hash || ZERO_HASH256
    end
    if @previous_index == INVALID_INDEX
      @previous_index = @transaction_output.index || INVALID_INDEX
    end
  end
  @value = value
end

Instance Attribute Details

#coinbase_dataObject

Binary String contained in signature script in coinbase input. Returns nil if it is not a coinbase input.



28
29
30
# File 'lib/btcruby/transaction_input.rb', line 28

def coinbase_data
  @coinbase_data
end

#dataObject (readonly)

Binary representation of the input in wire format (aka payload).



36
37
38
# File 'lib/btcruby/transaction_input.rb', line 36

def data
  @data
end

#dictionaryObject (readonly)

Dictionary representation of transaction ready to be encoded in JSON, PropertyList etc.



39
40
41
# File 'lib/btcruby/transaction_input.rb', line 39

def dictionary
  @dictionary
end

#indexObject

Optional index within owning transaction. It is set in tx.add_input and reset to nil in tx.remove_all_inputs. Default is nil.



49
50
51
# File 'lib/btcruby/transaction_input.rb', line 49

def index
  @index
end

#previous_hashObject

Hash of the previous transaction (raw binary hash)



13
14
15
# File 'lib/btcruby/transaction_input.rb', line 13

def previous_hash
  @previous_hash
end

#previous_idObject

ID of a previous transaction (reversed hash in hex)



16
17
18
# File 'lib/btcruby/transaction_input.rb', line 16

def previous_id
  @previous_id
end

#previous_indexObject

Index of the previous transaction's output (uint32_t).



19
20
21
# File 'lib/btcruby/transaction_input.rb', line 19

def previous_index
  @previous_index
end

#sequenceObject

Input sequence (uint32_t). Default is maximum value 0xFFFFFFFF. Sequence is used to update a timelocked tx stored in memory of the nodes. It is only relevant when tx lock_time > 0. Currently, for DoS and security reasons, nodes do not store timelocked transactions making the sequence number meaningless.



33
34
35
# File 'lib/btcruby/transaction_input.rb', line 33

def sequence
  @sequence
end

#signature_scriptObject

BTC::Script instance that proves ownership of the previous transaction output. We intentionally do not call it "script" to avoid accidental confusion with TransactionOutput#script.



24
25
26
# File 'lib/btcruby/transaction_input.rb', line 24

def signature_script
  @signature_script
end

#transactionObject

Optional reference to the owning transaction. It is set in tx.add_input and reset to nil in tx.remove_all_inputs. Default is nil.



44
45
46
# File 'lib/btcruby/transaction_input.rb', line 44

def transaction
  @transaction
end

#transaction_outputObject

Optional attribute referencing an output that this input is spending.



52
53
54
# File 'lib/btcruby/transaction_input.rb', line 52

def transaction_output
  @transaction_output
end

#valueObject

Optional attribute containing a value in the corresponding output (in satoshis). Default is transaction_output.value or nil.



56
57
58
# File 'lib/btcruby/transaction_input.rb', line 56

def value
  @value
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



266
267
268
269
270
# File 'lib/btcruby/transaction_input.rb', line 266

def ==(other)
  return true if super(other)
  return true if self.data == other.data
  return false
end

#coinbase?Boolean

Returns true if this input is a coinbase input.

Returns:

  • (Boolean)


192
193
194
# File 'lib/btcruby/transaction_input.rb', line 192

def coinbase?
  return self.previous_index == INVALID_INDEX && self.previous_hash == ZERO_HASH256
end

#dupObject

Makes a deep copy of a transaction input



274
275
276
277
278
279
280
281
282
283
# File 'lib/btcruby/transaction_input.rb', line 274

def dup
  TransactionInput.new(previous_hash: @previous_hash.dup,
                      previous_index: @previous_index,
                    signature_script: @signature_script ? @signature_script.dup : nil,
                       coinbase_data: @coinbase_data ? @coinbase_data.dup : nil,
                            sequence: @sequence,
                         transaction: @transaction,
                  transaction_output: @transaction_output, # not dup-ing txout because it's a transient object without #==
                               value: @value)
end

#final?Boolean

Returns:

  • (Boolean)


213
214
215
# File 'lib/btcruby/transaction_input.rb', line 213

def final?
  self.sequence == MAX_SEQUENCE
end

#init_with_dictionary(dict) ⇒ Object

Raises:

  • (ArgumentError)


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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/btcruby/transaction_input.rb', line 134

def init_with_dictionary(dict)
  raise ArgumentError, "Dictionary is missing" if !dict

  # Supports bitcoin-QT RPC format

  if dict["prev_out"] && !dict["prev_out"].is_a?(Hash)
    raise ArgumentError, "prev_out is not a dictionary."
  end

  prevhash = ZERO_HASH256
  previndex = INVALID_INDEX
  script = nil
  seq = MAX_SEQUENCE

  if dict["prev_out"]
    if hashhex = dict["prev_out"]["hash"]
      prevhash = BTC.from_hex(hashhex)
      if prevhash.bytesize != 32
        raise ArgumentError, "prev_out.hash is not 32 bytes long."
      end
    end

    if n = dict["prev_out"]["n"]
      index = n.to_i
      if index < 0 || index > 0xffffffff
        raise ArgumentError, "prev_out.n is out of bounds (#{index})."
      end
      previndex = index
    end
  end

  coinbase_data = nil
  script = nil
  if hex = dict["coinbase"]
    coinbase_data = BTC.from_hex(hex)
  elsif dict["scriptSig"]
    if dict["scriptSig"].is_a?(Hash)
      if hex = dict["scriptSig"]["hex"]
        script = BTC::Script.new(data: BTC.from_hex(hex))
      end
    end
  end

  if dict["sequence"]
    seq = dict["sequence"].to_i
    if seq < 0 || seq > MAX_SEQUENCE
      raise ArgumentError, "sequence is out of bounds (#{index})."
    end
  end

  @previous_hash = prevhash
  @previous_index = previndex,
  @coinbase_data = coinbase_data
  @signature_script = script
  @sequence = seq
end

#init_with_stream(stream) ⇒ Object



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
# File 'lib/btcruby/transaction_input.rb', line 101

def init_with_stream(stream)
  if stream.eof?
    raise ArgumentError, "Can't parse transaction input from stream because it is already closed."
  end

  if !(@previous_hash = stream.read(32)) || @previous_hash.bytesize != 32
    raise ArgumentError, "Failed to read 32-byte previous_hash from stream."
  end

  if !(@previous_index = BTC::WireFormat.read_uint32le(stream: stream).first)
    raise ArgumentError, "Failed to read previous_index from stream."
  end

  is_coinbase = (@previous_hash == ZERO_HASH256 && @previous_index == INVALID_INDEX)

  if !(scriptdata = BTC::WireFormat.read_string(stream: stream).first)
    raise ArgumentError, "Failed to read signature_script data from stream."
  end

  @coinbase_data = nil
  @signature_script = nil

  if is_coinbase
    @coinbase_data = scriptdata
  else
    @signature_script = BTC::Script.new(data: scriptdata)
  end

  if !(@sequence = BTC::WireFormat.read_uint32le(stream: stream).first)
    raise ArgumentError, "Failed to read sequence from stream."
  end
end

#inspect(style = :full) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/btcruby/transaction_input.rb', line 285

def inspect(style = :full)
  if style == :full
    %{#<#{self.class.name} prev:#{self.previous_id}[#{self.previous_index}]} +
    %{ script:#{self.signature_script.to_s.inspect}} +
    %{ seq:#{self.sequence}} +
    %{>}
  else
    %{#<#{self.class.name} prev:#{self.previous_id[0,10]}[#{self.previous_index}]} +
    %{ script:#{self.signature_script.to_s.inspect}} +
    (self.sequence != MAX_SEQUENCE ? %{ seq:#{self.sequence}} : %{}) +
    %{>}
  end
end

#outpointObject



204
205
206
# File 'lib/btcruby/transaction_input.rb', line 204

def outpoint
  Outpoint.new(transaction_hash: previous_hash, index: previous_index)
end

#outpoint=(outpoint) ⇒ Object



208
209
210
211
# File 'lib/btcruby/transaction_input.rb', line 208

def outpoint=(outpoint)
  self.previous_hash = outpoint.transaction_hash
  self.previous_index = outpoint.index
end

#to_hObject



258
259
260
# File 'lib/btcruby/transaction_input.rb', line 258

def to_h
  self.dictionary
end

#to_sObject



262
263
264
# File 'lib/btcruby/transaction_input.rb', line 262

def to_s
  BTC.to_hex(self.data)
end