Class: CoinOp::Bit::Transaction
- Inherits:
-
Object
- Object
- CoinOp::Bit::Transaction
- Includes:
- Encodings
- Defined in:
- lib/coin-op/bit/transaction.rb
Instance Attribute Summary collapse
-
#confirmations ⇒ Object
readonly
Returns the value of attribute confirmations.
-
#inputs ⇒ Object
readonly
Returns the value of attribute inputs.
-
#native ⇒ Object
readonly
Returns the value of attribute native.
-
#outputs ⇒ Object
readonly
Returns the value of attribute outputs.
Class Method Summary collapse
-
.build {|transaction| ... } ⇒ Object
Deprecated.
-
.data(data, network:) ⇒ Object
(also: from_data)
Construct a Transaction from a data structure of nested Hashes and Arrays.
-
.hex(hex) ⇒ Object
(also: from_hex)
Construct a Transaction from a hex representation of the raw bytes.
-
.native(tx) ⇒ Object
(also: from_native)
Construct a transaction from an instance of ::Bitcoin::Protocol::Tx.
-
.raw(raw_tx) ⇒ Object
(also: from_bytes)
Construct a Transaction from raw bytes.
Instance Method Summary collapse
-
#add_change(address, metadata = {}) ⇒ Object
Add an output to receive change for this transaction.
-
#add_input(input, network:) ⇒ Object
Takes one of.
-
#add_output(output) ⇒ Object
Takes either an Output or a Hash describing an output.
-
#binary_hash ⇒ Object
Returns the transaction hash as a string of bytes.
-
#change_value ⇒ Object
Returns the value that should be assigned to a change output.
-
#estimate_fee(tx_size = nil) ⇒ Object
Estimate the fee in satoshis for this transaction.
-
#fee ⇒ Object
Returns the transaction fee computed from the actual input and output values, as opposed to the requested override fee or the estimated fee.
- #fee_override ⇒ Object
-
#funded? ⇒ Boolean
Are the currently selected inputs sufficient to cover the current outputs and the desired fee?.
-
#hex_hash ⇒ Object
Returns the transaction hash encoded as hex.
-
#initialize(options = {}) ⇒ Transaction
constructor
A new Transaction contains no inputs or outputs; these can be added with #add_input and #add_output.
-
#input_value ⇒ Object
Total value of all inputs.
-
#input_value_for(addresses) ⇒ Object
Takes a set of Bitcoin addresses and returns the value expressed in the inputs for this transaction.
- #lock_time ⇒ Object
-
#output_value ⇒ Object
Total value of all outputs.
-
#output_value_for(addresses) ⇒ Object
Takes a set of Bitcoin addresses and returns the value expressed in the outputs for this transaction.
-
#set_script_sigs(*input_args, &block) ⇒ Object
A convenience method for authorizing inputs in a generic manner.
-
#sig_hash(input, script = nil) ⇒ Object
Compute the digest for a given input.
-
#to_hash ⇒ Object
Returns a custom data structure representing the full transaction.
-
#to_hex ⇒ Object
Returns the transaction payload encoded as hex.
- #to_json(*a) ⇒ Object
-
#update_native {|@native| ... } ⇒ Object
Update the “native” bitcoin-ruby instances for the transaction and all its inputs.
-
#validate_script_sigs ⇒ Object
Verify that the script_sigs for all inputs are valid.
-
#validate_syntax ⇒ Object
Validate that the transaction is plausibly signable.
-
#value_for(addresses) ⇒ Object
Takes a set of Bitcoin addresses and returns the net change in value expressed in this transaction.
- #version ⇒ Object
Methods included from Encodings
#base58, #decode_base58, #decode_hex, #hex
Constructor Details
#initialize(options = {}) ⇒ Transaction
A new Transaction contains no inputs or outputs; these can be added with #add_input and #add_output. FIXME: version and locktime options are ignored here.
101 102 103 104 105 106 107 108 |
# File 'lib/coin-op/bit/transaction.rb', line 101 def initialize(={}) @native = Bitcoin::Protocol::Tx.new @inputs = [] @outputs = [] @network = [:network] @fee_override = [:fee] @confirmations = [:confirmations] end |
Instance Attribute Details
#confirmations ⇒ Object (readonly)
Returns the value of attribute confirmations.
96 97 98 |
# File 'lib/coin-op/bit/transaction.rb', line 96 def confirmations @confirmations end |
#inputs ⇒ Object (readonly)
Returns the value of attribute inputs.
96 97 98 |
# File 'lib/coin-op/bit/transaction.rb', line 96 def inputs @inputs end |
#native ⇒ Object (readonly)
Returns the value of attribute native.
96 97 98 |
# File 'lib/coin-op/bit/transaction.rb', line 96 def native @native end |
#outputs ⇒ Object (readonly)
Returns the value of attribute outputs.
96 97 98 |
# File 'lib/coin-op/bit/transaction.rb', line 96 def outputs @outputs end |
Class Method Details
.build {|transaction| ... } ⇒ Object
Deprecated. Easier to use Transaction.from_data
7 8 9 10 11 |
# File 'lib/coin-op/bit/transaction.rb', line 7 def self.build(&block) transaction = self.new yield transaction transaction end |
.data(data, network:) ⇒ Object Also known as: from_data
Construct a Transaction from a data structure of nested Hashes and Arrays.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/coin-op/bit/transaction.rb', line 15 def self.data(data, network:) version, lock_time, fee, inputs, outputs, confirmations = data.values_at :version, :lock_time, :fee, :inputs, :outputs, :confirmations transaction = self.new( :fee => fee, :version => version, :lock_time => lock_time, :confirmations => confirmations, network: network ) outputs.each do |output_hash| transaction.add_output(Output.new(output_hash, network: network)) end #FIXME: we're not handling sig_scripts for already signed inputs. if inputs # TODO: use #each instead of #each_with_index inputs.each_with_index do |input_hash, index| transaction.add_input(input_hash, network: network) ## FIXME: verify that the supplied and computed sig_hashes match #puts :sig_hashes_match => (data[:sig_hash] == input.sig_hash) end end transaction end |
.hex(hex) ⇒ Object Also known as: from_hex
Construct a Transaction from a hex representation of the raw bytes.
51 52 53 |
# File 'lib/coin-op/bit/transaction.rb', line 51 def self.hex(hex) self.from_bytes CoinOp::Encodings.decode_hex(hex) end |
.native(tx) ⇒ Object Also known as: from_native
Construct a transaction from an instance of ::Bitcoin::Protocol::Tx
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 |
# File 'lib/coin-op/bit/transaction.rb', line 56 def self.native(tx) transaction = self.new() # TODO: reconsider use of instance_eval transaction.instance_eval do @native = tx tx.inputs.each_with_index do |input, i| # We use SparseInput because it does not require the retrieval # of the previous output. Its functionality should probably be # folded into the Input class. @inputs << SparseInput.new(input.prev_out, input.prev_out_index) end tx.outputs.each_with_index do |output, i| @outputs << Output.new( :transaction => transaction, :index => i, :value => output.value, :script => {:blob => output.pk_script} ) end end report = transaction.validate_syntax unless report[:valid] == true raise "Invalid syntax: #{report[:error].to_json}" end transaction end |
.raw(raw_tx) ⇒ Object Also known as: from_bytes
Construct a Transaction from raw bytes.
46 47 48 |
# File 'lib/coin-op/bit/transaction.rb', line 46 def self.raw(raw_tx) self.native ::Bitcoin::Protocol::Tx.new(raw_tx) end |
Instance Method Details
#add_change(address, metadata = {}) ⇒ Object
Add an output to receive change for this transaction. Takes a bitcoin address and optional metadata Hash.
362 363 364 365 366 367 368 |
# File 'lib/coin-op/bit/transaction.rb', line 362 def add_change(address, ={}) add_output( :value => change_value, :address => address, :metadata => {:memo => "change"}.merge() ) end |
#add_input(input, network:) ⇒ Object
Takes one of
-
an instance of Input
-
an instance of Output
-
a Hash describing an Output
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/coin-op/bit/transaction.rb', line 161 def add_input(input, network:) # TODO: allow specifying prev_tx and index with a Hash. # Possibly stop using SparseInput. unless input.is_a?(Input) input = Input.new input.merge( transaction: self, index: @inputs.size, network: network ) end @inputs << input self.update_native do |native| native.add_in input.native end input end |
#add_output(output) ⇒ Object
Takes either an Output or a Hash describing an output.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/coin-op/bit/transaction.rb', line 181 def add_output(output) unless output.is_a? Output output = Output.new(output, network: @network) end index = @outputs.size # TODO: stop using set_transaction and just pass self to Output.new # Then remove output.set_transaction output.set_transaction self, index @outputs << output self.update_native do |native| native.add_out(output.native) end end |
#binary_hash ⇒ Object
Returns the transaction hash as a string of bytes.
197 198 199 200 |
# File 'lib/coin-op/bit/transaction.rb', line 197 def binary_hash update_native @native.binary_hash end |
#change_value ⇒ Object
Returns the value that should be assigned to a change output.
356 357 358 |
# File 'lib/coin-op/bit/transaction.rb', line 356 def change_value input_value - (output_value + fee_override) end |
#estimate_fee(tx_size = nil) ⇒ Object
Estimate the fee in satoshis for this transaction. Takes an optional tx_size argument because it is impossible to determine programmatically the size of the scripts used to create P2SH outputs. Rough testing of the size of a 2of3 multisig p2sh input: 297
303 304 305 306 |
# File 'lib/coin-op/bit/transaction.rb', line 303 def estimate_fee(tx_size=nil) unspents = inputs.map(&:output) Fee.estimate(unspents, outputs, tx_size) end |
#fee ⇒ Object
Returns the transaction fee computed from the actual input and output values, as opposed to the requested override fee or the estimated fee.
310 311 312 |
# File 'lib/coin-op/bit/transaction.rb', line 310 def fee input_value - output_value rescue nil end |
#fee_override ⇒ Object
295 296 297 |
# File 'lib/coin-op/bit/transaction.rb', line 295 def fee_override @fee_override || self.estimate_fee(network: @network) end |
#funded? ⇒ Boolean
Are the currently selected inputs sufficient to cover the current outputs and the desired fee?
326 327 328 |
# File 'lib/coin-op/bit/transaction.rb', line 326 def funded? input_value >= (output_value + fee_override) end |
#hex_hash ⇒ Object
Returns the transaction hash encoded as hex
203 204 205 206 |
# File 'lib/coin-op/bit/transaction.rb', line 203 def hex_hash update_native @native.hash end |
#input_value ⇒ Object
Total value of all inputs.
331 332 333 |
# File 'lib/coin-op/bit/transaction.rb', line 331 def input_value inputs.inject(0) { |sum, input| sum += input.output.value } end |
#input_value_for(addresses) ⇒ Object
Takes a set of Bitcoin addresses and returns the value expressed in the inputs for this transaction.
343 344 345 346 |
# File 'lib/coin-op/bit/transaction.rb', line 343 def input_value_for(addresses) own = inputs.select { |input| addresses.include?(input.output.address) } own.inject(0) { |sum, input| input.output.value } end |
#lock_time ⇒ Object
212 213 214 |
# File 'lib/coin-op/bit/transaction.rb', line 212 def lock_time @native.lock_time end |
#output_value ⇒ Object
Total value of all outputs.
315 316 317 318 319 320 321 322 |
# File 'lib/coin-op/bit/transaction.rb', line 315 def output_value total = 0 @outputs.each do |output| total += output.value end total end |
#output_value_for(addresses) ⇒ Object
Takes a set of Bitcoin addresses and returns the value expressed in the outputs for this transaction.
350 351 352 353 |
# File 'lib/coin-op/bit/transaction.rb', line 350 def output_value_for(addresses) own = outputs.select { |output| addresses.include?(output.address) } own.inject(0) { |sum, output| output.value } end |
#set_script_sigs(*input_args, &block) ⇒ Object
A convenience method for authorizing inputs in a generic manner. Rather than iterating over the inputs manually, the user can provide this method with an array of values and a block that knows what to do with the values.
For example, if you happen to have the script sigs precomputed for some strange reason, you could do this:
tx.set_script_sigs sig_array do |input, sig|
sig
end
More realistically, if you have an array of the keypairs corresponding to the inputs:
tx.set_script_sigs keys do |input, key|
sig_hash = tx.sig_hash(input)
key.sign(sig_hash)
end
Each element of the array may be an array, which allows for easy handling of multisig situations.
281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/coin-op/bit/transaction.rb', line 281 def set_script_sigs(*input_args, &block) # No sense trying to authorize when the transaction isn't usable. report = validate_syntax unless report[:valid] == true raise "Invalid syntax: #{report[:errors].to_json}" end # Array#zip here allows us to iterate over the inputs in lockstep with any # number of sets of values. self.inputs.zip(*input_args) do |input, *input_arg| input.script_sig = yield input, *input_arg end end |
#sig_hash(input, script = nil) ⇒ Object
Compute the digest for a given input. This is the value that is actually signed in a transaction. e.g. I want to spend UTXO0 - I provide the UTXO0 as an input.
I provide as a script the script to which UTXO0 was paid.
If UTX0 was paid to a P2SH address, the script here needs to be the correct m-of-n structure, which may well not be part of the input.output object
-
This works here because we default to 2-of-3 and have consistent ordering
When we support multiple m-of-n’s, this may become a prollem.
249 250 251 252 253 254 255 256 257 |
# File 'lib/coin-op/bit/transaction.rb', line 249 def sig_hash(input, script=nil) # We only allow SIGHASH_ALL at this time # https://en.bitcoin.it/wiki/OP_CHECKSIG#Hashtype_SIGHASH_ALL_.28default.29 prev_out = input.output script ||= prev_out.script @native.signature_hash_for_input(input.index, script.to_blob) end |
#to_hash ⇒ Object
Returns a custom data structure representing the full transaction. Typically used only by #to_json.
225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/coin-op/bit/transaction.rb', line 225 def to_hash { :confirmations => self.confirmations.nil? ? 0 : self.confirmations, :version => self.version, :lock_time => self.lock_time, :hash => self.hex_hash, :fee => self.fee, :inputs => self.inputs, :outputs => self.outputs } end |
#to_hex ⇒ Object
Returns the transaction payload encoded as hex. This value can be used by other bitcoin tools for publishing to the network.
218 219 220 221 |
# File 'lib/coin-op/bit/transaction.rb', line 218 def to_hex payload = self.native.to_payload CoinOp::Encodings.hex(payload) end |
#to_json(*a) ⇒ Object
237 238 239 |
# File 'lib/coin-op/bit/transaction.rb', line 237 def to_json(*a) self.to_hash.to_json(*a) end |
#update_native {|@native| ... } ⇒ Object
Update the “native” bitcoin-ruby instances for the transaction and all its inputs. Will be removed when we rework the wrapper classes to be lazy, rather than eager.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/coin-op/bit/transaction.rb', line 113 def update_native yield @native if block_given? @native = Bitcoin::Protocol::Tx.new(@native.to_payload) @inputs.each_with_index do |input, i| native = @native.inputs[i] # Using instance_eval here because I really don't want to expose # Input#native=. As we consume more and more of the native # functionality, we can dispense with such ugliness. input.instance_eval do @native = native end # TODO: is this re-nativization necessary for outputs, too? end end |
#validate_script_sigs ⇒ Object
Verify that the script_sigs for all inputs are valid.
141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/coin-op/bit/transaction.rb', line 141 def validate_script_sigs bad_inputs = [] valid = true @inputs.each_with_index do |input, index| # TODO: confirm whether we need to mess with the block_timestamp arg unless self.native.verify_input_signature(index, input.output.transaction.native) valid = false bad_inputs << index end end {:valid => valid, :inputs => bad_inputs} end |
#validate_syntax ⇒ Object
Validate that the transaction is plausibly signable.
133 134 135 136 137 138 |
# File 'lib/coin-op/bit/transaction.rb', line 133 def validate_syntax update_native validator = Bitcoin::Validation::Tx.new(@native, nil) valid = validator.validate :rules => [:syntax] {:valid => valid, :error => validator.error} end |
#value_for(addresses) ⇒ Object
Takes a set of Bitcoin addresses and returns the net change in value expressed in this transaction.
337 338 339 |
# File 'lib/coin-op/bit/transaction.rb', line 337 def value_for(addresses) output_value_for(addresses) - input_value_for(addresses) end |
#version ⇒ Object
208 209 210 |
# File 'lib/coin-op/bit/transaction.rb', line 208 def version @native.ver end |