Class: BTC::Script
- Inherits:
-
Object
- Object
- BTC::Script
- Includes:
- Opcodes
- Defined in:
- lib/btcruby/script/script.rb
Constant Summary
Constants included from Opcodes
Opcodes::OPCODE_NAME_TO_VALUE, Opcodes::OPCODE_VALUE_TO_NAME, Opcodes::OP_0, Opcodes::OP_0NOTEQUAL, Opcodes::OP_1, Opcodes::OP_10, Opcodes::OP_11, Opcodes::OP_12, Opcodes::OP_13, Opcodes::OP_14, Opcodes::OP_15, Opcodes::OP_16, Opcodes::OP_1ADD, Opcodes::OP_1NEGATE, Opcodes::OP_1SUB, Opcodes::OP_2, Opcodes::OP_2DIV, Opcodes::OP_2DROP, Opcodes::OP_2DUP, Opcodes::OP_2MUL, Opcodes::OP_2OVER, Opcodes::OP_2ROT, Opcodes::OP_2SWAP, Opcodes::OP_3, Opcodes::OP_3DUP, Opcodes::OP_4, Opcodes::OP_5, Opcodes::OP_6, Opcodes::OP_7, Opcodes::OP_8, Opcodes::OP_9, Opcodes::OP_ABS, Opcodes::OP_ADD, Opcodes::OP_AND, Opcodes::OP_BOOLAND, Opcodes::OP_BOOLOR, Opcodes::OP_CAT, Opcodes::OP_CHECKLOCKTIMEVERIFY, Opcodes::OP_CHECKMULTISIG, Opcodes::OP_CHECKMULTISIGVERIFY, Opcodes::OP_CHECKSIG, Opcodes::OP_CHECKSIGVERIFY, Opcodes::OP_CODESEPARATOR, Opcodes::OP_DEPTH, Opcodes::OP_DIV, Opcodes::OP_DROP, Opcodes::OP_DUP, Opcodes::OP_ELSE, Opcodes::OP_ENDIF, Opcodes::OP_EQUAL, Opcodes::OP_EQUALVERIFY, Opcodes::OP_FALSE, Opcodes::OP_FROMALTSTACK, Opcodes::OP_GREATERTHAN, Opcodes::OP_GREATERTHANOREQUAL, Opcodes::OP_HASH160, Opcodes::OP_HASH256, Opcodes::OP_IF, Opcodes::OP_IFDUP, Opcodes::OP_INVALIDOPCODE, Opcodes::OP_INVERT, Opcodes::OP_LEFT, Opcodes::OP_LESSTHAN, Opcodes::OP_LESSTHANOREQUAL, Opcodes::OP_LSHIFT, Opcodes::OP_MAX, Opcodes::OP_MIN, Opcodes::OP_MOD, Opcodes::OP_MUL, Opcodes::OP_NEGATE, Opcodes::OP_NIP, Opcodes::OP_NOP, Opcodes::OP_NOP1, Opcodes::OP_NOP10, Opcodes::OP_NOP2, Opcodes::OP_NOP3, Opcodes::OP_NOP4, Opcodes::OP_NOP5, Opcodes::OP_NOP6, Opcodes::OP_NOP7, Opcodes::OP_NOP8, Opcodes::OP_NOP9, Opcodes::OP_NOT, Opcodes::OP_NOTIF, Opcodes::OP_NUMEQUAL, Opcodes::OP_NUMEQUALVERIFY, Opcodes::OP_NUMNOTEQUAL, Opcodes::OP_OR, Opcodes::OP_OVER, Opcodes::OP_PICK, Opcodes::OP_PUSHDATA1, Opcodes::OP_PUSHDATA2, Opcodes::OP_PUSHDATA4, Opcodes::OP_RESERVED, Opcodes::OP_RESERVED1, Opcodes::OP_RESERVED2, Opcodes::OP_RETURN, Opcodes::OP_RIGHT, Opcodes::OP_RIPEMD160, Opcodes::OP_ROLL, Opcodes::OP_ROT, Opcodes::OP_RSHIFT, Opcodes::OP_SHA1, Opcodes::OP_SHA256, Opcodes::OP_SIZE, Opcodes::OP_SUB, Opcodes::OP_SUBSTR, Opcodes::OP_SWAP, Opcodes::OP_TOALTSTACK, Opcodes::OP_TRUE, Opcodes::OP_TUCK, Opcodes::OP_VER, Opcodes::OP_VERIF, Opcodes::OP_VERIFY, Opcodes::OP_VERNOTIF, Opcodes::OP_WITHIN, Opcodes::OP_XOR
Instance Attribute Summary collapse
-
#chunks ⇒ Object
readonly
Returns an array of chunks that constitute the script.
-
#data ⇒ Object
readonly
Serialized binary form of the script (payload).
-
#multisig_public_keys ⇒ Object
readonly
List of public keys if it is a multisig script.
-
#multisig_signatures_required ⇒ Object
readonly
Number of signatures required if it is a multisig script.
Class Method Summary collapse
-
.encode_pushdata(pushdata, opcode: nil) ⇒ Object
If opcode is nil, then the most compact encoding will be chosen.
-
.multisig(public_keys: [], signatures_required: 0) ⇒ Object
Initializes a multisignature script “OP_<M> <pubkey1> …
-
.simulated_compressed_pubkey ⇒ Object
Returns a dummy compressed pubkey (33 bytes).
-
.simulated_multisig_script(m, n) ⇒ Object
Returns a dummy script that simulates m-of-n multisig script.
-
.simulated_signature(hashtype: nil) ⇒ Object
Returns a simulated signature with an optional hashtype byte attached.
-
.simulated_uncompressed_pubkey ⇒ Object
Returns a dummy uncompressed pubkey (65 bytes).
Instance Method Summary collapse
-
#+(other) ⇒ Object
Returns a new instance with concatenation of two scripts.
- #<<(object) ⇒ Object
- #==(other) ⇒ Object (also: #eql?)
-
#append(object) ⇒ Object
Appends an opcode (Integer), pushdata (String) or Script and returns self.
-
#append_opcode(opcode) ⇒ Object
Appends a non-pushdata opcode to the script.
-
#append_pushdata(data, opcode: nil) ⇒ Object
Appends a pushdata opcode with the most compact encoding.
-
#append_script(script) ⇒ Object
Appends script to the current script.
-
#data_only? ⇒ Boolean
Returns true if the script consists of push data operations only (including OP_<N>).
-
#delete_opcode(opcode) ⇒ Object
Removes all occurences of opcode.
-
#delete_pushdata(pushdata) ⇒ Object
Removes all occurences of a given pushdata.
- #detect_multisig ⇒ Object
- #detect_multisig_if_needed ⇒ Object
-
#dropped_prefix_data ⇒ Object
Returns pushdata if script starts with <pushdata> OP_DROP Returns nil if the script starts with some other opcodes or shorter than 2 opcodes.
-
#dup ⇒ Object
Complete copy of a script.
-
#find_and_delete(subscript) ⇒ Object
Removes chunks matching subscript byte-for-byte and returns a new script instance with subscript removed.
-
#initialize(hex: nil, data: nil, op_return: nil, public_keys: nil, signatures_required: nil) ⇒ Script
constructor
raw script data in hex.
- #inspect ⇒ Object
-
#multisig_script? ⇒ Boolean
Returns true if the script is in form “<M> <pubkey1> …
-
#op_return_data ⇒ Object
Returns first data chunk if this script is ‘OP_RETURN <data>’.
-
#op_return_data_only_script? ⇒ Boolean
Returns true if this script is of form ‘OP_RETURN <data>’.
-
#op_return_items ⇒ Object
Returns all data chunks if this script is ‘OP_RETURN <data> [<data>…]’ Most commonly returned array contains one binary string.
-
#op_return_script? ⇒ Boolean
Returns true if this script’s first opcode is OP_RETURN.
-
#open_assets_marker? ⇒ Boolean
Returns
trueif this script may be a valid OpenAssets marker. - #p2pk? ⇒ Boolean
- #p2pkh? ⇒ Boolean
- #p2sh? ⇒ Boolean
-
#p2sh_script ⇒ Object
Wraps the recipient into an output P2SH script (OP_HASH160 <20-byte hash of the recipient> OP_EQUAL).
-
#public_key ⇒ Object
Returns a raw public key if this script is public_key_script?.
-
#public_key_hash ⇒ Object
Returns public key hash if this script is public_key_hash_script?.
-
#public_key_hash_script? ⇒ Boolean
Returns true if the script is a P2PKH script: “OP_DUP OP_HASH160 <20-byte hash> OP_EQUALVERIFY OP_CHECKSIG”.
-
#public_key_script? ⇒ Boolean
Returns true if the script is a pay-to-pubkey script: “<pubkey> OP_CHECKSIG”.
-
#script_hash ⇒ Object
Returns p2sh hash if this script is script_hash_script?.
-
#script_hash_script? ⇒ Boolean
Returns true if the script is a P2SH script: “OP_HASH160 <20-byte hash> OP_EQUAL”.
-
#simulated_signature_script(strict: true) ⇒ Object
Returns a dummy script matching this script on the input with the same size as an intended signature script.
-
#standard? ⇒ Boolean
Returns true if this is a standard script As of September 2014, standard = public_key_hash_script? || p2sh_script? || standard_multisig_script? || public_key_script? || standard_op_return_script?.
-
#standard_address(network: nil) ⇒ Object
Returns BTC::PublicKeyAddress or BTC::ScriptHashAddress if the script is a standard output script for these addresses.
-
#standard_multisig_script? ⇒ Boolean
Returns true if the script is “OP_<M> <pubkey1> …
-
#standard_op_return_script? ⇒ Boolean
Returns true if this script is a ‘OP_RETURN <data>’ script and data size is within 40 bytes.
-
#subscript(*args) ⇒ Object
(also: #[])
Same arguments as with Array#[].
-
#to_a ⇒ Object
Returns an array of opcodes or pushdata strings.
- #to_hex ⇒ Object
-
#to_s ⇒ Object
Human-readable textual representation of the script (e.g. “OP_DUP OP_HASH160 5a73e920b7836c74f9e740a5bb885e8580557038 OP_EQUALVERIFY OP_CHECKSIG”).
-
#versioned_script? ⇒ Boolean
Returns true if this is an output script wrapped in a versioned pushdata for segwit softfork.
-
#without_dropped_prefix_data ⇒ Object
If script starts with ‘<pushdata> OP_DROP`, these two opcodes are removed and a new script instance is returned.
Constructor Details
#initialize(hex: nil, data: nil, op_return: nil, public_keys: nil, signatures_required: nil) ⇒ Script
raw script data in hex
21 22 23 24 25 26 27 28 29 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 |
# File 'lib/btcruby/script/script.rb', line 21 def initialize(hex: nil, # raw script data in hex data: nil, # raw script data in binary op_return: nil, # binary string for OP_RETURN script (or array of binary string) public_keys: nil, signatures_required: nil # multisig ) if data || hex data ||= BTC.from_hex(hex) data = BTC::Data.ensure_binary_encoding(data) @chunks = [] offset = 0 while offset < data.bytesize chunk = ScriptChunk.with_data(data, offset: offset) if !chunk.canonical? Diagnostics.current.("BTC::Script: decoded non-canonical chunk at offset #{offset}: #{chunk.to_s}") end offset += chunk.size @chunks << chunk end elsif op_return @chunks = [] self << OP_RETURN << op_return elsif public_keys || signatures_required if !public_keys || public_keys.size < 1 raise ArgumentError, "Public keys must be an array of at least 1 pubkey" end if !signatures_required || signatures_required < 1 raise ArgumentError, "Number of required signatures must be greater than zero" end if signatures_required > public_keys.size raise ArgumentError, "Number of required signatures must not exceed number of pubkeys." end if public_keys.size > 16 raise ArgumentError, "Maximum number of public keys exceeded (16)" end n_opcode = Opcode.opcode_for_small_integer(public_keys.size) m_opcode = Opcode.opcode_for_small_integer(signatures_required) @chunks = [] self << m_opcode << public_keys << n_opcode << OP_CHECKMULTISIG else # Empty script @chunks = [] end end |
Instance Attribute Details
#chunks ⇒ Object (readonly)
Returns an array of chunks that constitute the script.
19 20 21 |
# File 'lib/btcruby/script/script.rb', line 19 def chunks @chunks end |
#data ⇒ Object (readonly)
Serialized binary form of the script (payload)
6 7 8 |
# File 'lib/btcruby/script/script.rb', line 6 def data @data end |
#multisig_public_keys ⇒ Object (readonly)
List of public keys if it is a multisig script. If it is not a multisig script, returns nil. See also #multisig_script?
11 12 13 |
# File 'lib/btcruby/script/script.rb', line 11 def multisig_public_keys @multisig_public_keys end |
#multisig_signatures_required ⇒ Object (readonly)
Number of signatures required if it is a multisig script. If it is not a multisig script, returns nil. See also #multisig_script?
16 17 18 |
# File 'lib/btcruby/script/script.rb', line 16 def multisig_signatures_required @multisig_signatures_required end |
Class Method Details
.encode_pushdata(pushdata, opcode: nil) ⇒ Object
If opcode is nil, then the most compact encoding will be chosen. Returns nil if opcode can’t be used for data, or data is nil or too big.
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 |
# File 'lib/btcruby/script/script.rb', line 513 def self.encode_pushdata(pushdata, opcode: nil) raise ArgumentError, "Pushdata is missing" if !pushdata if pushdata.bytesize < OP_PUSHDATA1 && opcode == nil return BTC::WireFormat.encode_uint8(pushdata.bytesize) + pushdata elsif pushdata.bytesize < 0xff && (opcode == nil || opcode == OP_PUSHDATA1) return BTC::WireFormat.encode_uint8(OP_PUSHDATA1) + BTC::WireFormat.encode_uint8(pushdata.bytesize) + pushdata elsif pushdata.bytesize < 0xffff && (opcode == nil || opcode == OP_PUSHDATA2) return BTC::WireFormat.encode_uint8(OP_PUSHDATA2) + BTC::WireFormat.encode_uint16le(pushdata.bytesize) + pushdata elsif pushdata.bytesize < 0xffffffff && (opcode == nil || opcode == OP_PUSHDATA4) return BTC::WireFormat.encode_uint8(OP_PUSHDATA4) + BTC::WireFormat.encode_uint32le(pushdata.bytesize) + pushdata else raise ArgumentError, "Invalid opcode or data is too big" end end |
.multisig(public_keys: [], signatures_required: 0) ⇒ Object
Initializes a multisignature script “OP_<M> <pubkey1> … <pubkeyN> OP_<N> OP_CHECKMULTISIG” N must be >= M, M and N should be from 1 to 16. If you need a more customized transaction with OP_CHECKMULTISIG, create it using other methods. public_keys is an array of binary strings.
71 72 73 |
# File 'lib/btcruby/script/script.rb', line 71 def self.multisig(public_keys: [], signatures_required: 0) self.new(public_keys: public_keys, signatures_required: signatures_required) end |
.simulated_compressed_pubkey ⇒ Object
Returns a dummy compressed pubkey (33 bytes).
375 376 377 |
# File 'lib/btcruby/script/script.rb', line 375 def self.simulated_compressed_pubkey "\x02" + "\xff"*32 end |
.simulated_multisig_script(m, n) ⇒ Object
Returns a dummy script that simulates m-of-n multisig script
380 381 382 383 384 385 386 |
# File 'lib/btcruby/script/script.rb', line 380 def self.simulated_multisig_script(m,n) self.new << Opcode.opcode_for_small_integer(m) << [simulated_uncompressed_pubkey]*n << # assuming non-compressed pubkeys to be conservative Opcode.opcode_for_small_integer(n) << OP_CHECKMULTISIG end |
.simulated_signature(hashtype: nil) ⇒ Object
Returns a simulated signature with an optional hashtype byte attached
365 366 367 |
# File 'lib/btcruby/script/script.rb', line 365 def self.simulated_signature(hashtype: nil) "\x30" + "\xff"*71 + (hashtype ? WireFormat.encode_uint8(hashtype) : "") end |
.simulated_uncompressed_pubkey ⇒ Object
Returns a dummy uncompressed pubkey (65 bytes).
370 371 372 |
# File 'lib/btcruby/script/script.rb', line 370 def self.simulated_uncompressed_pubkey "\x04" + "\xff"*64 end |
Instance Method Details
#+(other) ⇒ Object
Returns a new instance with concatenation of two scripts.
468 469 470 |
# File 'lib/btcruby/script/script.rb', line 468 def +(other) self.dup << other end |
#<<(object) ⇒ Object
463 464 465 |
# File 'lib/btcruby/script/script.rb', line 463 def <<(object) append(object) end |
#==(other) ⇒ Object Also known as: eql?
302 303 304 305 |
# File 'lib/btcruby/script/script.rb', line 302 def ==(other) return false if other == nil self.data == other.data end |
#append(object) ⇒ Object
Appends an opcode (Integer), pushdata (String) or Script and returns self. If Array is passed, this method is recursively called for each element in the array.
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/btcruby/script/script.rb', line 442 def append(object) if object.is_a?(BTC::Script) append_script(object) elsif object.is_a?(BTC::ScriptNumber) append_pushdata(object.data) elsif object.is_a?(Integer) append_opcode(object) elsif object.is_a?(String) append_pushdata(object.b) elsif object.is_a?(Array) object.each do |element| self << element end elsif object.is_a?(ScriptChunk) @chunks << object else raise ArgumentError, "Operand must be an integer, a string a BTC::Script instance or an array of any of those." end return self end |
#append_opcode(opcode) ⇒ Object
Appends a non-pushdata opcode to the script.
393 394 395 396 397 398 399 400 |
# File 'lib/btcruby/script/script.rb', line 393 def append_opcode(opcode) raise ArgumentError, "Invalid opcode value." if opcode > 0xff || opcode < 0 if opcode > 0 && opcode <= OP_PUSHDATA4 raise ArgumentError, "Cannot add pushdata opcode without data" end @chunks << ScriptChunk.new(opcode.chr) return self end |
#append_pushdata(data, opcode: nil) ⇒ Object
Appends a pushdata opcode with the most compact encoding. Optional opcode may be equal to OP_PUSHDATA1, OP_PUSHDATA2, or OP_PUSHDATA4. ArgumentError is raised if opcode does not represent a given data length.
405 406 407 408 409 410 411 412 413 |
# File 'lib/btcruby/script/script.rb', line 405 def append_pushdata(data, opcode: nil) raise ArgumentError, "No data provided" if !data encoded_pushdata = self.class.encode_pushdata(data, opcode: opcode) if !encoded_pushdata raise ArgumentError, "Cannot encode pushdata with opcode #{opcode}" end @chunks << ScriptChunk.new(encoded_pushdata) return self end |
#append_script(script) ⇒ Object
Appends script to the current script.
434 435 436 437 438 |
# File 'lib/btcruby/script/script.rb', line 434 def append_script(script) raise ArgumentError, "No script is given" if !script @chunks += script.chunks return self end |
#data_only? ⇒ Boolean
Returns true if the script consists of push data operations only (including OP_<N>). Aka isPushOnly in BitcoinQT. Used in BIP16 (P2SH) implementation.
267 268 269 270 271 272 273 |
# File 'lib/btcruby/script/script.rb', line 267 def data_only? # Include PUSHDATA ops and OP_0..OP_16 literals. @chunks.each do |chunk| return false if !chunk.data_only? end return true end |
#delete_opcode(opcode) ⇒ Object
Removes all occurences of opcode. Typically it’s OP_CODESEPARATOR.
416 417 418 419 420 421 422 |
# File 'lib/btcruby/script/script.rb', line 416 def delete_opcode(opcode) @chunks = @chunks.inject([]) do |list, chunk| list << chunk if chunk.opcode != opcode list end return self end |
#delete_pushdata(pushdata) ⇒ Object
Removes all occurences of a given pushdata.
425 426 427 428 429 430 431 |
# File 'lib/btcruby/script/script.rb', line 425 def delete_pushdata(pushdata) @chunks = @chunks.inject([]) do |list, chunk| list << chunk if chunk.pushdata != pushdata list end return self end |
#detect_multisig ⇒ Object
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
# File 'lib/btcruby/script/script.rb', line 539 def detect_multisig # multisig script must have at least 4 ops ("OP_1 <pubkey> OP_1 OP_CHECKMULTISIG") return false if @chunks.size < 4 return false if @chunks.last.opcode != OP_CHECKMULTISIG m_chunk = @chunks[0] n_chunk = @chunks[-2] m_opcode = m_chunk.opcode n_opcode = n_chunk.opcode m = Opcode.small_integer_from_opcode(m_opcode) n = Opcode.small_integer_from_opcode(n_opcode) # If m or n is not OP_<int>, but a pushdata with little-endian bignum. if !m return false if !m_chunk.pushdata? m = BTC::BigNumber.new(signed_little_endian: m_chunk.pushdata).integer end if !n return false if !n_chunk.pushdata? n = BTC::BigNumber.new(signed_little_endian: n_chunk.pushdata).integer end return false if m < 1 || n < 1 || m > n # We must have correct number of pubkeys in the script. 3 extra ops: OP_<M>, OP_<N> and OP_CHECKMULTISIG return false if @chunks.size != (3 + n) pubkeys = [] @chunks[1, n].each do |chunk| return false if !chunk.pushdata? || chunk.pushdata.bytesize == 0 pubkeys << chunk.pushdata end # Now we extracted all pubkeys and verified the numbers. @multisig_public_keys = pubkeys @multisig_signatures_required = m return true end |
#detect_multisig_if_needed ⇒ Object
534 535 536 537 |
# File 'lib/btcruby/script/script.rb', line 534 def detect_multisig_if_needed return if @is_multisig != nil @is_multisig = detect_multisig end |
#dropped_prefix_data ⇒ Object
Returns pushdata if script starts with <pushdata> OP_DROP Returns nil if the script starts with some other opcodes or shorter than 2 opcodes.
248 249 250 251 252 253 |
# File 'lib/btcruby/script/script.rb', line 248 def dropped_prefix_data if @chunks.size >= 2 && @chunks[0].pushdata? && @chunks[1].opcode == OP_DROP return @chunks[0].pushdata end nil end |
#dup ⇒ Object
Complete copy of a script.
298 299 300 |
# File 'lib/btcruby/script/script.rb', line 298 def dup Script.new(data: self.data) end |
#find_and_delete(subscript) ⇒ Object
Removes chunks matching subscript byte-for-byte and returns a new script instance with subscript removed. So if pushdata items are encoded differently, they won’t match. Consensus-critical code.
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/btcruby/script/script.rb', line 481 def find_and_delete(subscript) subscript.is_a?(Script) or raise ArgumentError,"Argument must be an instance of BTC::Script" # Replacing [a b a] # Script: a b b a b a b a c # Result: a b b b a c subscriptsize = subscript.chunks.size buf = [] i = 0 result = Script.new chunks.each do |chunk| if chunk == subscript.chunks[i] buf << chunk i+=1 if i == subscriptsize # matched the whole subscript i = 0 buf.clear end else i = 0 result << buf result << chunk end end result end |
#inspect ⇒ Object
308 309 310 |
# File 'lib/btcruby/script/script.rb', line 308 def inspect %{#<#{self.class.name} #{to_s.inspect} (#{self.data.bytesize} bytes)>} end |
#multisig_script? ⇒ Boolean
Returns true if the script is in form “<M> <pubkey1> … <pubkeyN> <N> OP_CHECKMULTISIG”
178 179 180 181 |
# File 'lib/btcruby/script/script.rb', line 178 def multisig_script? detect_multisig_if_needed @is_multisig end |
#op_return_data ⇒ Object
Returns first data chunk if this script is ‘OP_RETURN <data>’. Otherwise returns nil.
221 222 223 224 |
# File 'lib/btcruby/script/script.rb', line 221 def op_return_data return nil if !op_return_data_only_script? return @chunks[1].pushdata end |
#op_return_data_only_script? ⇒ Boolean
Returns true if this script is of form ‘OP_RETURN <data>’
213 214 215 216 217 |
# File 'lib/btcruby/script/script.rb', line 213 def op_return_data_only_script? return @chunks.size >= 2 && @chunks[0].opcode == OP_RETURN && @chunks[1..-1].all?{|c| c.pushdata? } end |
#op_return_items ⇒ Object
Returns all data chunks if this script is ‘OP_RETURN <data> [<data>…]’ Most commonly returned array contains one binary string.
228 229 230 231 |
# File 'lib/btcruby/script/script.rb', line 228 def op_return_items return nil if !op_return_data_only_script? return @chunks[1, @chunks.size-1].map{|c| c.pushdata} end |
#op_return_script? ⇒ Boolean
Returns true if this script’s first opcode is OP_RETURN.
207 208 209 210 |
# File 'lib/btcruby/script/script.rb', line 207 def op_return_script? return @chunks.size >= 1 && @chunks[0].opcode == OP_RETURN end |
#open_assets_marker? ⇒ Boolean
Returns true if this script may be a valid OpenAssets marker. Only checks the prefix and minimal length, does not validate the content. Use this method to quickly filter out non-asset transactions.
236 237 238 239 240 241 242 243 244 |
# File 'lib/btcruby/script/script.rb', line 236 def open_assets_marker? return false if !op_return_data_only_script? data = op_return_data return false if !data || data.bytesize < 6 if data[0, AssetMarker::PREFIX_V1.bytesize] == AssetMarker::PREFIX_V1 return true end false end |
#p2pk? ⇒ Boolean
110 111 112 |
# File 'lib/btcruby/script/script.rb', line 110 def p2pk? return public_key_script? end |
#p2pkh? ⇒ Boolean
130 131 132 |
# File 'lib/btcruby/script/script.rb', line 130 def p2pkh? return public_key_hash_script? end |
#p2sh? ⇒ Boolean
148 149 150 |
# File 'lib/btcruby/script/script.rb', line 148 def p2sh? return script_hash_script? end |
#p2sh_script ⇒ Object
Wraps the recipient into an output P2SH script (OP_HASH160 <20-byte hash of the recipient> OP_EQUAL).
334 335 336 |
# File 'lib/btcruby/script/script.rb', line 334 def p2sh_script Script.new << OP_HASH160 << BTC.hash160(self.data) << OP_EQUAL end |
#public_key ⇒ Object
Returns a raw public key if this script is public_key_script?
115 116 117 |
# File 'lib/btcruby/script/script.rb', line 115 def public_key @chunks[0] && @chunks[0].pushdata end |
#public_key_hash ⇒ Object
Returns public key hash if this script is public_key_hash_script?
135 136 137 |
# File 'lib/btcruby/script/script.rb', line 135 def public_key_hash @chunks[2] && @chunks[2].pushdata end |
#public_key_hash_script? ⇒ Boolean
Returns true if the script is a P2PKH script: “OP_DUP OP_HASH160 <20-byte hash> OP_EQUALVERIFY OP_CHECKSIG”
121 122 123 124 125 126 127 128 |
# File 'lib/btcruby/script/script.rb', line 121 def public_key_hash_script? return false if @chunks.size != 5 return @chunks[0].opcode == OP_DUP && @chunks[1].opcode == OP_HASH160 && @chunks[2].size == 21 && @chunks[3].opcode == OP_EQUALVERIFY && @chunks[4].opcode == OP_CHECKSIG end |
#public_key_script? ⇒ Boolean
Returns true if the script is a pay-to-pubkey script: “<pubkey> OP_CHECKSIG”
103 104 105 106 107 108 |
# File 'lib/btcruby/script/script.rb', line 103 def public_key_script? return false if @chunks.size != 2 return @chunks[0].pushdata? && @chunks[0].pushdata.size >= 33 && @chunks[1].opcode == OP_CHECKSIG end |
#script_hash ⇒ Object
Returns p2sh hash if this script is script_hash_script?
153 154 155 |
# File 'lib/btcruby/script/script.rb', line 153 def script_hash @chunks[1] && @chunks[1].pushdata end |
#script_hash_script? ⇒ Boolean
Returns true if the script is a P2SH script: “OP_HASH160 <20-byte hash> OP_EQUAL”
141 142 143 144 145 146 |
# File 'lib/btcruby/script/script.rb', line 141 def script_hash_script? return false if @chunks.size != 3 return @chunks[0].opcode == OP_HASH160 && @chunks[1].size == 21 && @chunks[2].opcode == OP_EQUAL end |
#simulated_signature_script(strict: true) ⇒ Object
Returns a dummy script matching this script on the input with the same size as an intended signature script. Only a few standard script types are supported. Set strict to false to allow imprecise guess for P2SH script. Returns nil if could not determine a matching script.
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
# File 'lib/btcruby/script/script.rb', line 344 def simulated_signature_script(strict: true) if public_key_hash_script? # assuming non-compressed pubkeys to be conservative return Script.new << Script.simulated_signature(hashtype: SIGHASH_ALL) << Script.simulated_uncompressed_pubkey elsif public_key_script? return Script.new << Script.simulated_signature(hashtype: SIGHASH_ALL) elsif script_hash_script? && !strict # This is a wild approximation, but works well if most p2sh scripts are 2-of-3 multisig scripts. # If you have a very particular smart contract scheme you should not use TransactionBuilder which estimates fees this way. return Script.new << OP_0 << [Script.simulated_signature(hashtype: SIGHASH_ALL)]*2 << Script.simulated_multisig_script(2,3).data elsif multisig_script? return Script.new << OP_0 << [Script.simulated_signature(hashtype: SIGHASH_ALL)]*self.multisig_signatures_required else return nil end end |
#standard? ⇒ Boolean
Returns true if this is a standard script As of September 2014, standard = public_key_hash_script? ||
p2sh_script? ||
standard_multisig_script? ||
public_key_script? ||
standard_op_return_script?
85 86 87 88 89 90 91 |
# File 'lib/btcruby/script/script.rb', line 85 def standard? public_key_hash_script? || script_hash_script? || standard_multisig_script? || public_key_script? || standard_op_return_script? end |
#standard_address(network: nil) ⇒ Object
Returns BTC::PublicKeyAddress or BTC::ScriptHashAddress if the script is a standard output script for these addresses. If the script is something different, returns nil.
321 322 323 324 325 326 327 328 329 330 |
# File 'lib/btcruby/script/script.rb', line 321 def standard_address(network: nil) if public_key_hash_script? return BTC::PublicKeyAddress.new(hash: @chunks[2].pushdata, network: network) elsif script_hash_script? return BTC::ScriptHashAddress.new(hash: @chunks[1].pushdata, network: network) elsif public_key_script? return BTC::PublicKeyAddress.new(hash: BTC.hash160(@chunks[0].pushdata), network: network) end nil end |
#standard_multisig_script? ⇒ Boolean
Returns true if the script is “OP_<M> <pubkey1> … <pubkeyN> OP_<N> OP_CHECKMULTISIG” where N is up to 15. Scripts with up to 15 signatures are considered standard and relayed quickly, but you are allowed to create more complex ones.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/btcruby/script/script.rb', line 161 def standard_multisig_script? return false if !multisig_script? # Check chunks directly so we make sure OP_<N> are used, not pushdata. # Bitcoin allows encoding multisig N and M parameters as pushdata # (which will be interpreted as a little-endian bignum) m_opcode = @chunks[0].opcode n_opcode = @chunks[-2].opcode if n_opcode >= OP_1 && n_opcode <= OP_15 if m_opcode >= OP_1 && m_opcode <= n_opcode return true end end return false end |
#standard_op_return_script? ⇒ Boolean
Returns true if this script is a ‘OP_RETURN <data>’ script and data size is within 40 bytes.
201 202 203 204 |
# File 'lib/btcruby/script/script.rb', line 201 def standard_op_return_script? retun false if !op_return_data_only_script? || @chunks.size != 2 @chunks[1].pushdata.bytesize <= 40 end |
#subscript(*args) ⇒ Object Also known as: []
Same arguments as with Array#[].
473 474 475 |
# File 'lib/btcruby/script/script.rb', line 473 def subscript(*args) Script.new << chunks[*args] end |
#to_a ⇒ Object
Returns an array of opcodes or pushdata strings. Integers are opcodes, strings are pushdata binary strings. OP_0 is treated as a zero-length pushdata.
293 294 295 |
# File 'lib/btcruby/script/script.rb', line 293 def to_a @chunks.map{|c| c.pushdata? ? c.pushdata : c.opcode } end |
#to_hex ⇒ Object
286 287 288 |
# File 'lib/btcruby/script/script.rb', line 286 def to_hex BTC.to_hex(self.data) end |
#to_s ⇒ Object
Human-readable textual representation of the script (e.g. “OP_DUP OP_HASH160 5a73e920b7836c74f9e740a5bb885e8580557038 OP_EQUALVERIFY OP_CHECKSIG”)
282 283 284 |
# File 'lib/btcruby/script/script.rb', line 282 def to_s @chunks.map{|c| c.to_s }.join(" ") end |
#versioned_script? ⇒ Boolean
Returns true if this is an output script wrapped in a versioned pushdata for segwit softfork.
94 95 96 97 98 99 |
# File 'lib/btcruby/script/script.rb', line 94 def versioned_script? return chunks.size == 1 && chunks[0].pushdata? && chunks[0].canonical? && chunks[0].pushdata.bytesize > 2 end |
#without_dropped_prefix_data ⇒ Object
If script starts with ‘<pushdata> OP_DROP`, these two opcodes are removed and a new script instance is returned.
257 258 259 260 261 262 |
# File 'lib/btcruby/script/script.rb', line 257 def without_dropped_prefix_data if dropped_prefix_data return Script.new << @chunks[2..-1] end self end |