Class: Bitcoin::Builder::TxBuilder
- Inherits:
-
Object
- Object
- Bitcoin::Builder::TxBuilder
- Defined in:
- lib/bitcoin/builder.rb
Overview
DSL to create Bitcoin::Protocol::Tx used by Builder#build_tx.
tx = tx do |t|
t.input do |i|
i.prev_out prev_tx, 0
i.signature_key key
end
t.output do |o|
o.value 12345 # 0.00012345 BTC
o.to key.addr
end
end
Signs every input that has a signature key and where the previous outputs pk_script is known. If unable to sign, the resulting txin will include the #sig_hash that needs to be signed.
See TxInBuilder and TxOutBuilder for details on how to build in/outputs.
Instance Method Summary collapse
- #add_empty_script_sig_to_input(i) ⇒ Object
- #get_script_sig(inc) ⇒ Object
-
#include_coinbase_data(i, inc) ⇒ Object
coinbase inputs don’t need to be signed, they only include the given
coinbase_data
. -
#initialize ⇒ TxBuilder
constructor
A new instance of TxBuilder.
-
#input {|c| ... } ⇒ Object
add an input to the transaction (see TxInBuilder).
-
#lock_time(n) ⇒ Object
specify tx lock_time.
-
#output(value = nil, recipient = nil, type = :address) {|c| ... } ⇒ Object
add an output to the transaction (see TxOutBuilder).
-
#randomize_outputs ⇒ Object
Randomize the outputs using SecureRandom.
- #sig_hash_and_all_keys_exist?(inc, sig_script) ⇒ Boolean
-
#sign_input(i, inc) ⇒ Object
Sign input number
i
with data from giveninc
object (a TxInBuilder). -
#tx(opts = {}) ⇒ Object
Create the transaction according to values specified via DSL.
-
#version(n) ⇒ Object
specify tx version.
Constructor Details
Instance Method Details
#add_empty_script_sig_to_input(i) ⇒ Object
231 232 233 234 235 236 237 238 |
# File 'lib/bitcoin/builder.rb', line 231 def add_empty_script_sig_to_input(i) @tx.in[i].script_sig_length = 0 @tx.in[i].script_sig = "" # add the sig_hash that needs to be signed, so it can be passed on to a signing device @tx.in[i].sig_hash = @sig_hash # add the address the sig_hash needs to be signed with as a convenience for the signing device @tx.in[i].sig_address = Script.new(@prev_script).get_address if @prev_script end |
#get_script_sig(inc) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/bitcoin/builder.rb', line 240 def get_script_sig(inc) if inc.has_multiple_keys? # multiple keys given, generate signature for each one sigs = inc.sign(@sig_hash) if redeem_script = inc.instance_eval { @redeem_script } # when a redeem_script was specified, assume we spend a p2sh multisig script script_sig = Script.to_p2sh_multisig_script_sig(redeem_script, sigs) else # when no redeem_script is given, do a regular multisig spend script_sig = Script.to_multisig_script_sig(*sigs) end else # only one key given, generate signature and script_sig sig = inc.sign(@sig_hash) script_sig = Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*")) end return script_sig end |
#include_coinbase_data(i, inc) ⇒ Object
coinbase inputs don’t need to be signed, they only include the given coinbase_data
215 216 217 218 219 |
# File 'lib/bitcoin/builder.rb', line 215 def include_coinbase_data i, inc script_sig = [inc.coinbase_data].pack("H*") @tx.in[i].script_sig_length = script_sig.bytesize @tx.in[i].script_sig = script_sig end |
#input {|c| ... } ⇒ Object
add an input to the transaction (see TxInBuilder).
150 151 152 153 154 |
# File 'lib/bitcoin/builder.rb', line 150 def input c = TxInBuilder.new yield c @ins << c end |
#lock_time(n) ⇒ Object
specify tx lock_time. this is usually not necessary. defaults to 0.
145 146 147 |
# File 'lib/bitcoin/builder.rb', line 145 def lock_time n @tx.lock_time = n end |
#output(value = nil, recipient = nil, type = :address) {|c| ... } ⇒ Object
add an output to the transaction (see TxOutBuilder).
157 158 159 160 161 162 163 |
# File 'lib/bitcoin/builder.rb', line 157 def output value = nil, recipient = nil, type = :address c = TxOutBuilder.new c.value(value) if value c.to(recipient, type) if recipient yield c if block_given? @outs << c end |
#randomize_outputs ⇒ Object
Randomize the outputs using SecureRandom
302 303 304 |
# File 'lib/bitcoin/builder.rb', line 302 def randomize_outputs @outs.sort_by!{ SecureRandom.random_bytes(4).unpack("I")[0] } end |
#sig_hash_and_all_keys_exist?(inc, sig_script) ⇒ Boolean
221 222 223 224 225 226 227 228 229 |
# File 'lib/bitcoin/builder.rb', line 221 def sig_hash_and_all_keys_exist?(inc, sig_script) return false unless @sig_hash && inc.has_keys? script = Bitcoin::Script.new(sig_script) return true if script.is_hash160? || script.is_pubkey? || script.is_witness_v0_keyhash? || (Bitcoin.namecoin? && script.is_namecoin?) if script.is_multisig? return inc.has_multiple_keys? && inc.key.size >= script.get_signatures_required end raise "Script type must be hash160, pubkey, p2wpkh or multisig" end |
#sign_input(i, inc) ⇒ Object
Sign input number i
with data from given inc
object (a TxInBuilder).
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/bitcoin/builder.rb', line 260 def sign_input i, inc if @tx.in[i].coinbase? include_coinbase_data(i, inc) else @prev_script = inc.instance_variable_get(:@prev_out_script) # get the signature script; use +redeem_script+ if given # (indicates spending a p2sh output), otherwise use the prev_script sig_script = inc.instance_eval { @redeem_script } sig_script ||= @prev_script # when a sig_script was found, generate the sig_hash to be signed if sig_script script = Script.new(sig_script) if script.is_witness_v0_keyhash? @sig_hash = @tx.signature_hash_for_witness_input(i, sig_script, inc.value) else @sig_hash = @tx.signature_hash_for_input(i, sig_script) end end # when there is a sig_hash and one or more signature_keys were specified if sig_hash_and_all_keys_exist?(inc, sig_script) # add the script_sig to the txin if script.is_witness_v0_keyhash? # for p2wpkh @tx.in[i].script_witness.stack << inc.sign(@sig_hash) + [Script::SIGHASH_TYPE[:all]].pack("C") @tx.in[i].script_witness.stack << inc.key.pub.htb else @tx.in[i].script_sig = get_script_sig(inc) end # double-check that the script_sig is valid to spend the given prev_script raise "Signature error" if @prev_script && !@tx.verify_input_signature(i, @prev_script) elsif inc.has_multiple_keys? raise "Keys missing for multisig signing" else # no sig_hash, add an empty script_sig. add_empty_script_sig_to_input(i) end end end |
#tx(opts = {}) ⇒ Object
Create the transaction according to values specified via DSL. Sign each input that has a signature key specified. If there is no key, store the sig_hash in the input, so it can easily be signed later.
When :change_address and :input_value options are given, it will automatically create a change output sending the remaining funds to the given address. The :leave_fee option can be used in this case to specify a tx fee that should be left unclaimed by the change output.
175 176 177 178 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 212 |
# File 'lib/bitcoin/builder.rb', line 175 def tx opts = {} return @tx if @tx.hash if opts[:change_address] && !opts[:input_value] raise "Must give 'input_value' when auto-generating change output!" end @ins.each {|i| @tx.add_in(i.txin) } @outs.each {|o| @tx.add_out(o.txout) } if opts[:change_address] output_value = @tx.out.map(&:value).inject(:+) || 0 change_value = opts[:input_value] - output_value if opts[:leave_fee] fee = @tx.minimum_block_fee + (opts[:extra_fee] || 0) if change_value >= fee change_value -= fee else change_value = 0 end end if change_value > 0 script = Script.to_address_script(opts[:change_address]) @tx.add_out(P::TxOut.new(change_value, script)) end end @ins.each_with_index do |inc, i| sign_input(i, inc) end # run our tx through an encode/decode cycle to make sure that the binary format is sane raise "Payload Error" unless P::Tx.new(@tx.to_witness_payload).to_payload == @tx.to_payload @tx.instance_eval do @payload = to_payload @hash = hash_from_payload(@payload) end @tx end |
#version(n) ⇒ Object
specify tx version. this is usually not necessary. defaults to 1.
140 141 142 |
# File 'lib/bitcoin/builder.rb', line 140 def version n @tx.ver = n end |