Class: Tapyrus::Tx

Inherits:
Object
  • Object
show all
Includes:
HexConverter
Defined in:
lib/tapyrus/tx.rb

Overview

Transaction class

Constant Summary collapse

MAX_STANDARD_VERSION =
2
MAX_STANDARD_TX_WEIGHT =

The maximum weight for transactions we’re willing to relay/mine

400_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HexConverter

#to_hex

Constructor Details

#initializeTx

Returns a new instance of Tx.



19
20
21
22
23
24
# File 'lib/tapyrus/tx.rb', line 19

def initialize
  @inputs = []
  @outputs = []
  @features = 1
  @lock_time = 0
end

Instance Attribute Details

#featuresObject

Returns the value of attribute features.



14
15
16
# File 'lib/tapyrus/tx.rb', line 14

def features
  @features
end

#inputsObject (readonly) Also known as: in

Returns the value of attribute inputs.



15
16
17
# File 'lib/tapyrus/tx.rb', line 15

def inputs
  @inputs
end

#lock_timeObject

Returns the value of attribute lock_time.



17
18
19
# File 'lib/tapyrus/tx.rb', line 17

def lock_time
  @lock_time
end

#outputsObject (readonly) Also known as: out

Returns the value of attribute outputs.



16
17
18
# File 'lib/tapyrus/tx.rb', line 16

def outputs
  @outputs
end

Class Method Details

.parse_from_payload(payload) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/tapyrus/tx.rb', line 29

def self.parse_from_payload(payload)
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
  tx = new
  tx.features = buf.read(4).unpack("V").first

  in_count = Tapyrus.unpack_var_int_from_io(buf)

  in_count.times { tx.inputs << TxIn.parse_from_payload(buf) }

  out_count = Tapyrus.unpack_var_int_from_io(buf)
  out_count.times { tx.outputs << TxOut.parse_from_payload(buf) }

  tx.lock_time = buf.read(4).unpack("V").first

  tx
end

Instance Method Details

#==(other) ⇒ Object



74
75
76
# File 'lib/tapyrus/tx.rb', line 74

def ==(other)
  to_payload == other.to_payload
end

#coinbase_tx?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/tapyrus/tx.rb', line 70

def coinbase_tx?
  inputs.length == 1 && inputs.first.coinbase?
end

#hashObject



46
47
48
# File 'lib/tapyrus/tx.rb', line 46

def hash
  to_hex.to_i(16)
end

#sighash_for_input(input_index, output_script, hash_type: ) ⇒ String

Generate signature hash

Parameters:

  • input_index (Integer)

    input index.

  • hash_type (Integer) (defaults to: )

    signature hash type

  • output_script (Tapyrus::Script)

    script pubkey or script code. if script pubkey is P2SH, set redeem script to this.

Returns:

Raises:

  • (ArgumentError)


111
112
113
114
115
116
# File 'lib/tapyrus/tx.rb', line 111

def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all])
  raise ArgumentError, "input_index must be specified." unless input_index
  raise ArgumentError, "does not exist input corresponding to input_index." if input_index >= inputs.size
  raise ArgumentError, "script_pubkey must be specified." unless output_script
  sighash_for_legacy(input_index, output_script, hash_type)
end

#sizeObject

The serialized transaction size



102
103
104
# File 'lib/tapyrus/tx.rb', line 102

def size
  to_payload.bytesize
end

#standard?Boolean

check this tx is standard.

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/tapyrus/tx.rb', line 79

def standard?
  return false if features > MAX_STANDARD_VERSION
  inputs.each do |i|
    # Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed keys (remember the 520 byte limit on redeemScript size).
    # That works out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
    # bytes of scriptSig, which we round off to 1650 bytes for some minor future-proofing.
    # That's also enough to spend a 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not considered standard.
    return false if i.script_sig.size > 1650
    return false unless i.script_sig.push_only?
  end
  data_count = 0
  outputs.each do |o|
    return false unless o.script_pubkey.standard?
    data_count += 1 if o.script_pubkey.op_return?

    # TODO add non P2SH multisig relay(permitbaremultisig)
    return false if o.dust?
  end
  return false if data_count > 1
  true
end

#to_hObject



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/tapyrus/tx.rb', line 127

def to_h
  {
    txid: txid,
    hash: tx_hash,
    features: features,
    size: size,
    locktime: lock_time,
    vin: inputs.map(&:to_h),
    vout: outputs.map.with_index { |tx_out, index| tx_out.to_h.merge({ n: index }) }
  }
end

#to_payloadObject



62
63
64
65
66
67
68
# File 'lib/tapyrus/tx.rb', line 62

def to_payload
  buf = [features].pack("V")
  buf << Tapyrus.pack_var_int(inputs.length) << inputs.map(&:to_payload).join
  buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
  buf << [lock_time].pack("V")
  buf
end

#tx_hashObject



50
51
52
# File 'lib/tapyrus/tx.rb', line 50

def tx_hash
  Tapyrus.double_sha256(to_payload).bth
end

#txidObject



54
55
56
57
58
59
60
# File 'lib/tapyrus/tx.rb', line 54

def txid
  buf = [features].pack("V")
  buf << Tapyrus.pack_var_int(inputs.length) << inputs.map { |i| i.to_payload(use_malfix: true) }.join
  buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
  buf << [lock_time].pack("V")
  Tapyrus.double_sha256(buf).reverse.bth
end

#valid?Boolean

Verify transaction validity.

Returns:

  • (Boolean)

    whether this tx is valid or not.



141
142
143
144
145
# File 'lib/tapyrus/tx.rb', line 141

def valid?
  state = Tapyrus::ValidationState.new
  validation = Tapyrus::Validation.new
  validation.check_tx(self, state) && state.valid?
end

#verify_input_sig(input_index, script_pubkey, flags: STANDARD_SCRIPT_VERIFY_FLAGS) ⇒ Object

verify input signature.

Parameters:

  • input_index (Integer)
  • script_pubkey (Tapyrus::Script)

    the script pubkey for target input.

  • flags (Array) (defaults to: STANDARD_SCRIPT_VERIFY_FLAGS)

    the flags used when execute script interpreter.



122
123
124
125
# File 'lib/tapyrus/tx.rb', line 122

def verify_input_sig(input_index, script_pubkey, flags: STANDARD_SCRIPT_VERIFY_FLAGS)
  flags << SCRIPT_VERIFY_P2SH if script_pubkey.p2sh?
  verify_input_sig_for_legacy(input_index, script_pubkey, flags)
end