bitcoinrb 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,23 +21,59 @@ module Bitcoin
21
21
  script_sig = script_sig.htb
22
22
  hash_type = script_sig[-1].unpack('C').first
23
23
  sig = script_sig[0..-2]
24
- if sig_version == ScriptInterpreter::SIG_VERSION[:witness_v0]
25
- sighash = tx.sighash_for_input(input_index: input_index, hash_type: hash_type,
26
- script_code: script_code, amount: amount, sig_version: sig_version)
27
- else
28
- sighash = tx.sighash_for_input(input_index: input_index, hash_type: hash_type,
29
- script_code: script_code, sig_version: sig_version)
30
- end
31
- key = Bitcoin::Key.new(pubkey: pubkey)
24
+ sighash = tx.sighash_for_input(input_index, script_code, hash_type: hash_type,
25
+ amount: amount, sig_version: sig_version)
26
+ key = Key.new(pubkey: pubkey)
32
27
  key.verify(sig, sighash)
33
28
  end
34
29
 
35
- def check_locktime
36
- # TODO
30
+ def check_locktime(locktime)
31
+ # There are two kinds of nLockTime: lock-by-blockheight and lock-by-blocktime,
32
+ # distinguished by whether nLockTime < LOCKTIME_THRESHOLD.
33
+
34
+ # We want to compare apples to apples, so fail the script unless the type of nLockTime being tested is the same as the nLockTime in the transaction.
35
+ unless ((tx.lock_time < LOCKTIME_THRESHOLD && locktime < LOCKTIME_THRESHOLD) ||
36
+ (tx.lock_time >= LOCKTIME_THRESHOLD && locktime >= LOCKTIME_THRESHOLD))
37
+ return false
38
+ end
39
+
40
+ # Now that we know we're comparing apples-to-apples, the comparison is a simple numeric one.
41
+ return false if locktime > tx.lock_time
42
+
43
+ # Finally the nLockTime feature can be disabled and thus CHECKLOCKTIMEVERIFY bypassed if every txin has been finalized by setting nSequence to maxint.
44
+ # The transaction would be allowed into the blockchain, making the opcode ineffective.
45
+ # Testing if this vin is not final is sufficient to prevent this condition.
46
+ # Alternatively we could test all inputs, but testing just this input minimizes the data required to prove correct CHECKLOCKTIMEVERIFY execution.
47
+ return false if TxIn::SEQUENCE_FINAL == tx.inputs[input_index].sequence
48
+
49
+ true
37
50
  end
38
51
 
39
- def check_sequence
40
- # TODO
52
+ def check_sequence(sequence)
53
+ tx_sequence = tx.inputs[input_index].sequence
54
+ # Fail if the transaction's version number is not set high enough to trigger BIP 68 rules.
55
+ return false if tx.version < 2
56
+
57
+ # Sequence numbers with their most significant bit set are not consensus constrained.
58
+ # Testing that the transaction's sequence number do not have this bit set prevents using this property to get around a CHECKSEQUENCEVERIFY check.
59
+ return false unless tx_sequence & TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG == 0
60
+
61
+ # Mask off any bits that do not have consensus-enforced meaning before doing the integer comparisons
62
+ locktime_mask = TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | TxIn::SEQUENCE_LOCKTIME_MASK
63
+ tx_sequence_masked = tx_sequence & locktime_mask
64
+ sequence_masked = sequence & locktime_mask
65
+
66
+ # There are two kinds of nSequence: lock-by-blockheight and lock-by-blocktime,
67
+ # distinguished by whether sequence_masked < TxIn#SEQUENCE_LOCKTIME_TYPE_FLAG.
68
+ # We want to compare apples to apples, so fail the script
69
+ # unless the type of nSequenceMasked being tested is the same as the nSequenceMasked in the transaction.
70
+ unless ((tx_sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) ||
71
+ (tx_sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG))
72
+ return false
73
+ end
74
+
75
+ # Now that we know we're comparing apples-to-apples, the comparison is a simple numeric one.
76
+ sequence_masked <= tx_sequence_masked
41
77
  end
42
78
 
43
79
  end
@@ -1,19 +1,153 @@
1
1
  module Bitcoin
2
2
  module Secp256k1
3
3
 
4
- # secp256k1 module using libsecp256k1
4
+ # binding for secp256k1 (https://github.com/bitcoin/bitcoin/tree/v0.14.2/src/secp256k1)
5
+ # tag: v0.14.2
6
+ # this is not included by default, to enable set shared object path to ENV['SECP256K1_LIB_PATH']
7
+ # for linux, ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so'
8
+ # for mac,
5
9
  module Native
10
+ include ::FFI::Library
11
+ extend self
6
12
 
13
+ SECP256K1_FLAGS_TYPE_MASK = ((1 << 8) - 1)
14
+ SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
15
+ SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1)
16
+
17
+ SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
18
+ SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
19
+ SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8)
20
+
21
+ # Flags to pass to secp256k1_context_create.
22
+ SECP256K1_CONTEXT_VERIFY = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
23
+ SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
24
+
25
+ # Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export.
26
+ SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
27
+ SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
28
+
29
+ module_function
30
+
31
+ def init
32
+ raise 'secp256k1 library dose not found.' unless File.exist?(ENV['SECP256K1_LIB_PATH'])
33
+ ffi_lib(ENV['SECP256K1_LIB_PATH'])
34
+ load_functions
35
+ end
36
+
37
+ def load_functions
38
+ attach_function(:secp256k1_context_create, [:uint], :pointer)
39
+ attach_function(:secp256k1_context_destroy, [:pointer], :void)
40
+ attach_function(:secp256k1_context_randomize, [:pointer, :pointer], :int)
41
+ attach_function(:secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int)
42
+ attach_function(:secp256k1_ec_seckey_verify, [:pointer, :pointer], :int)
43
+ attach_function(:secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
44
+ attach_function(:secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int)
45
+ attach_function(:secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int)
46
+ attach_function(:secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int)
47
+ attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
48
+ attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
49
+ attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
50
+ end
51
+
52
+ def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
53
+ init
54
+ begin
55
+ context = secp256k1_context_create(flags)
56
+ ret, tries, max = 0, 0, 20
57
+ while ret != 1
58
+ raise 'secp256k1_context_randomize failed.' if tries >= max
59
+ tries += 1
60
+ ret = secp256k1_context_randomize(context, FFI::MemoryPointer.from_string(SecureRandom.random_bytes(32)))
61
+ end
62
+ yield(context) if block_given?
63
+ ensure
64
+ secp256k1_context_destroy(context)
65
+ end
66
+ end
67
+
68
+ # generate ec private key and public key
7
69
  def generate_key_pair(compressed: true)
8
- # TODO
70
+ with_context do |context|
71
+ ret, tries, max = 0, 0, 20
72
+ while ret != 1
73
+ raise 'secp256k1_ec_seckey_verify in generate_key_pair failed.' if tries >= max
74
+ tries += 1
75
+ priv_key = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32))
76
+ ret = secp256k1_ec_seckey_verify(context, priv_key)
77
+ end
78
+
79
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
80
+ result = secp256k1_ec_pubkey_create(context, internal_pubkey, priv_key)
81
+ raise 'error creating pubkey' unless result
82
+
83
+ pubkey = FFI::MemoryPointer.new(:uchar, 65)
84
+ pubkey_len = FFI::MemoryPointer.new(:uint64)
85
+ result = if compressed
86
+ pubkey_len.put_uint64(0, 33)
87
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
88
+ else
89
+ pubkey_len.put_uint64(0, 65)
90
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
91
+ end
92
+ raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
93
+
94
+ [ priv_key.read_string(32).bth, pubkey.read_string(pubkey_len.read_uint64).bth ]
95
+ end
9
96
  end
10
97
 
98
+ # generate bitcoin key object
11
99
  def generate_key(compressed: true)
12
- # TODO
100
+ privkey, pubkey = generate_key_pair(compressed: compressed)
101
+ Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
102
+ end
103
+
104
+ def sign_data(data, priv_key)
105
+ with_context do |context|
106
+ secret = FFI::MemoryPointer.new(:uchar, priv_key.htb.bytesize).put_bytes(0, priv_key.htb)
107
+ raise 'priv_key invalid' unless secp256k1_ec_seckey_verify(context, secret)
108
+
109
+ internal_signature = FFI::MemoryPointer.new(:uchar, 64)
110
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
111
+
112
+ ret, tries, max = 0, 0, 20
113
+ while ret != 1
114
+ raise 'secp256k1_ecdsa_sign failed.' if tries >= max
115
+ tries += 1
116
+ ret = secp256k1_ecdsa_sign(context, internal_signature, msg32, secret, nil, nil)
117
+ end
118
+
119
+ signature = FFI::MemoryPointer.new(:uchar, 72)
120
+ signature_len = FFI::MemoryPointer.new(:uint64).put_uint64(0, 72)
121
+ result = secp256k1_ecdsa_signature_serialize_der(context, signature, signature_len, internal_signature)
122
+ raise 'secp256k1_ecdsa_signature_serialize_der failed' unless result
123
+
124
+ signature.read_string(signature_len.read_uint64)
125
+ end
13
126
  end
14
127
 
15
- def sign_data(privkey, data)
16
- # TODO
128
+ def verify_sig(data, sig, pub_key)
129
+ with_context do |context|
130
+ return false if data.bytesize == 0
131
+
132
+ pubkey = FFI::MemoryPointer.new(:uchar, pub_key.htb.bytesize).put_bytes(0, pub_key.htb)
133
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
134
+ result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size)
135
+ return false unless result
136
+
137
+ signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
138
+ internal_signature = FFI::MemoryPointer.new(:uchar, 64)
139
+ result = secp256k1_ecdsa_signature_parse_der(context, internal_signature, signature, signature.size)
140
+ #result = ecdsa_signature_parse_der_lax(context, internal_signature, signature, signature.size)
141
+ return false unless result
142
+
143
+ # libsecp256k1's ECDSA verification requires lower-S signatures, which have not historically been enforced in Bitcoin, so normalize them first.
144
+ secp256k1_ecdsa_signature_normalize(context, internal_signature, internal_signature)
145
+
146
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
147
+ result = secp256k1_ecdsa_verify(context, internal_signature, msg32, internal_pubkey)
148
+
149
+ result ? true : false
150
+ end
17
151
  end
18
152
 
19
153
  end
@@ -9,7 +9,7 @@ module Bitcoin
9
9
 
10
10
  module_function
11
11
 
12
- # generate ecdsa private key and public key
12
+ # generate ec private key and public key
13
13
  def generate_key_pair(compressed: true)
14
14
  private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
15
15
  public_key = GROUP.generator.multiply_by_scalar(private_key)
@@ -18,23 +18,12 @@ module Bitcoin
18
18
  [privkey.bth, pubkey.bth]
19
19
  end
20
20
 
21
- # generate bitcoin key
21
+ # generate bitcoin key object
22
22
  def generate_key(compressed: true)
23
23
  privkey, pubkey = generate_key_pair(compressed: compressed)
24
24
  Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
25
25
  end
26
26
 
27
- # generate publick key from private key
28
- # @param [String] privkey a private key with string format
29
- # @param [Boolean] compressed pubkey compressed?
30
- # @return [String] a pubkey which generate from privkey
31
- def generate_pubkey(privkey, compressed: true)
32
- private_key = ECDSA::Format::IntegerOctetString.decode(privkey.htb)
33
- public_key = GROUP.generator.multiply_by_scalar(private_key)
34
- pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
35
- pubkey.bth
36
- end
37
-
38
27
  # sign data.
39
28
  # @param [String] data a data to be signed
40
29
  # @param [String] privkey a private key using sign
data/lib/bitcoin/tx.rb CHANGED
@@ -3,6 +3,11 @@ module Bitcoin
3
3
  # Transaction class
4
4
  class Tx
5
5
 
6
+ MAX_STANDARD_VERSION = 2
7
+
8
+ # The maximum weight for transactions we're willing to relay/mine
9
+ MAX_STANDARD_TX_WEIGHT = 400000
10
+
6
11
  MARKER = 0x00
7
12
  FLAG = 0x01
8
13
 
@@ -101,22 +106,90 @@ module Bitcoin
101
106
  inputs.map { |i| i.script_witness.to_payload }.join
102
107
  end
103
108
 
109
+ # check this tx is standard.
110
+ def standard?
111
+ return false if version > MAX_STANDARD_VERSION
112
+ return false if weight > MAX_STANDARD_TX_WEIGHT
113
+ inputs.each do |i|
114
+ # Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed keys (remember the 520 byte limit on redeemScript size).
115
+ # That works out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
116
+ # bytes of scriptSig, which we round off to 1650 bytes for some minor future-proofing.
117
+ # That's also enough to spend a 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not considered standard.
118
+ return false if i.script_sig.size > 1650
119
+ return false unless i.script_sig.push_only?
120
+ end
121
+ data_count = 0
122
+ outputs.each do |o|
123
+ return false unless o.script_pubkey.standard?
124
+ data_count += 1 if o.script_pubkey.op_return?
125
+ # TODO add non P2SH multisig relay(permitbaremultisig)
126
+ # TODO add dust relay check
127
+ end
128
+ return false if data_count > 1
129
+ true
130
+ end
131
+
132
+ # The serialized transaction size
133
+ def size
134
+ to_payload.bytesize
135
+ end
136
+
137
+ # The virtual transaction size (differs from size for witness transactions)
138
+ def vsize
139
+ (weight.to_f / 4).ceil
140
+ end
141
+
142
+ # calculate tx weight
143
+ # weight = (legacy tx payload) * 3 + (witness tx payload)
144
+ def weight
145
+ if witness?
146
+ serialize_old_format.bytesize * (WITNESS_SCALE_FACTOR - 1) + serialize_witness_format.bytesize
147
+ else
148
+ serialize_old_format.bytesize * WITNESS_SCALE_FACTOR
149
+ end
150
+ end
151
+
104
152
  # get signature hash
105
153
  # @param [Integer] input_index input index.
106
154
  # @param [Integer] hash_type signature hash type
107
- # @param [Bitcoin::Script] script_code script code
155
+ # @param [Bitcoin::Script] output_script script pubkey or script code. if script pubkey is P2WSH, set witness script to this.
108
156
  # @param [Integer] amount bitcoin amount locked in input. required for witness input only.
109
- def sighash_for_input(input_index: nil, hash_type: Script::SIGHASH_TYPE[:all], script_code: nil,
110
- sig_version: ScriptInterpreter::SIG_VERSION[:base], amount: nil)
157
+ # @param [Integer] skip_separator_index If output_script is P2WSH and output_script contains any OP_CODESEPARATOR,
158
+ # the script code needs is the witnessScript but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed.
159
+ def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all],
160
+ sig_version: :base, amount: nil, skip_separator_index: 0)
111
161
  raise ArgumentError, 'input_index must be specified.' unless input_index
112
162
  raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
113
- raise ArgumentError, 'script_pubkey must be specified.' unless script_code
163
+ raise ArgumentError, 'script_pubkey must be specified.' unless output_script
164
+ raise ArgumentError, 'unsupported sig version specified.' unless SIG_VERSION.include?(sig_version)
114
165
 
115
- if sig_version == ScriptInterpreter::SIG_VERSION[:witness_v0]
166
+ if sig_version == :witness_v0
116
167
  raise ArgumentError, 'amount must be specified.' unless amount
117
- sighash_for_witness(input_index, script_code, hash_type, amount)
168
+ sighash_for_witness(input_index, output_script, hash_type, amount, skip_separator_index)
169
+ else
170
+ sighash_for_legacy(input_index, output_script, hash_type)
171
+ end
172
+ end
173
+
174
+ # verify input signature.
175
+ # @param [Integer] input_index
176
+ # @param [Bitcoin::Script] script_pubkey the script pubkey for target input.
177
+ # @param [Integer] amount the amount of bitcoin, require for witness program only.
178
+ # @param [Array] flags the flags used when execute script interpreter.
179
+ def verify_input_sig(input_index, script_pubkey, amount: nil, flags: STANDARD_SCRIPT_VERIFY_FLAGS)
180
+ script_sig = inputs[input_index].script_sig
181
+ has_witness = inputs[input_index].has_witness?
182
+
183
+ if script_pubkey.p2sh?
184
+ flags << SCRIPT_VERIFY_P2SH
185
+ redeem_script = Script.parse_from_payload(script_sig.chunks.last)
186
+ script_pubkey = redeem_script if redeem_script.p2wpkh?
187
+ end
188
+
189
+ if has_witness
190
+ verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
118
191
  else
119
- sighash_for_legacy(input_index, script_code, hash_type)
192
+ verify_input_sig_for_legacy(input_index, script_pubkey, flags)
120
193
  end
121
194
  end
122
195
 
@@ -126,10 +199,10 @@ module Bitcoin
126
199
  def sighash_for_legacy(index, script_code, hash_type)
127
200
  ins = inputs.map.with_index do |i, idx|
128
201
  if idx == index
129
- i.to_payload(script_code)
202
+ i.to_payload(script_code.delete_opcode(Bitcoin::Opcodes::OP_CODESEPARATOR))
130
203
  else
131
204
  case hash_type & 0x1f
132
- when Script::SIGHASH_TYPE[:none], Script::SIGHASH_TYPE[:single]
205
+ when SIGHASH_TYPE[:none], SIGHASH_TYPE[:single]
133
206
  i.to_payload(Bitcoin::Script.new, 0)
134
207
  else
135
208
  i.to_payload(Bitcoin::Script.new)
@@ -141,16 +214,16 @@ module Bitcoin
141
214
  out_size = Bitcoin.pack_var_int(outputs.size)
142
215
 
143
216
  case hash_type & 0x1f
144
- when Script::SIGHASH_TYPE[:none]
217
+ when SIGHASH_TYPE[:none]
145
218
  outs = ''
146
219
  out_size = Bitcoin.pack_var_int(0)
147
- when Script::SIGHASH_TYPE[:single]
220
+ when SIGHASH_TYPE[:single]
148
221
  return "\x01".ljust(32, "\x00") if index >= outputs.size
149
222
  outs = outputs[0...(index + 1)].map.with_index { |o, idx| (idx == index) ? o.to_payload : o.to_empty_payload }.join
150
223
  out_size = Bitcoin.pack_var_int(index + 1)
151
224
  end
152
225
 
153
- if hash_type & Script::SIGHASH_TYPE[:anyonecanpay] != 0
226
+ if hash_type & SIGHASH_TYPE[:anyonecanpay] != 0
154
227
  ins = [ins[index]]
155
228
  end
156
229
 
@@ -162,7 +235,7 @@ module Bitcoin
162
235
 
163
236
  # generate sighash with BIP-143 format
164
237
  # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
165
- def sighash_for_witness(index, script_code, hash_type, amount)
238
+ def sighash_for_witness(index, script_pubkey_or_script_code, hash_type, amount, skip_separator_index)
166
239
  hash_prevouts = Bitcoin.double_sha256(inputs.map{|i|i.out_point.to_payload}.join)
167
240
  hash_sequence = Bitcoin.double_sha256(inputs.map{|i|[i.sequence].pack('V')}.join)
168
241
  outpoint = inputs[index].out_point.to_payload
@@ -170,22 +243,46 @@ module Bitcoin
170
243
  nsequence = [inputs[index].sequence].pack('V')
171
244
  hash_outputs = Bitcoin.double_sha256(outputs.map{|o|o.to_payload}.join)
172
245
 
246
+ script_code = script_pubkey_or_script_code.to_script_code(skip_separator_index)
247
+
173
248
  case (hash_type & 0x1f)
174
- when Script::SIGHASH_TYPE[:single]
249
+ when SIGHASH_TYPE[:single]
175
250
  hash_outputs = index >= outputs.size ? "\x00".ljust(32, "\x00") : Bitcoin.double_sha256(outputs[index].to_payload)
176
251
  hash_sequence = "\x00".ljust(32, "\x00")
177
- when Script::SIGHASH_TYPE[:none]
252
+ when SIGHASH_TYPE[:none]
178
253
  hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
179
254
  end
180
255
 
181
- if (hash_type & Script::SIGHASH_TYPE[:anyonecanpay]) != 0
256
+ if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
182
257
  hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
183
258
  end
184
- buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint, Bitcoin::Script.pack_pushdata(script_code.to_payload),
185
- amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
259
+ buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint,
260
+ script_code ,amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
186
261
  Bitcoin.double_sha256(buf)
187
262
  end
188
263
 
264
+ # verify input signature for legacy tx.
265
+ def verify_input_sig_for_legacy(input_index, script_pubkey, flags)
266
+ script_sig = inputs[input_index].script_sig
267
+ checker = Bitcoin::TxChecker.new(tx: self, input_index: input_index)
268
+ interpreter = Bitcoin::ScriptInterpreter.new(checker: checker, flags: flags)
269
+
270
+ interpreter.verify_script(script_sig, script_pubkey)
271
+ end
272
+
273
+ # verify input signature for witness tx.
274
+ def verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
275
+ flags << SCRIPT_VERIFY_WITNESS
276
+ flags << SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
277
+ checker = Bitcoin::TxChecker.new(tx: self, input_index: input_index, amount: amount)
278
+ interpreter = Bitcoin::ScriptInterpreter.new(checker: checker, flags: flags)
279
+ i = inputs[input_index]
280
+
281
+ script_sig = i.script_sig
282
+ witness = i.script_witness
283
+ interpreter.verify_script(script_sig, script_pubkey, witness)
284
+ end
285
+
189
286
  end
190
287
 
191
288
  end
data/lib/bitcoin/tx_in.rb CHANGED
@@ -8,9 +8,20 @@ module Bitcoin
8
8
  attr_accessor :sequence
9
9
  attr_accessor :script_witness
10
10
 
11
- DEFAULT_SEQUENCE = 0xFFFFFFFF
11
+ # Setting nSequence to this value for every input in a transaction disables nLockTime.
12
+ SEQUENCE_FINAL = 0xffffffff
12
13
 
13
- def initialize(out_point: nil, script_sig: nil, script_witness: ScriptWitness.new, sequence: DEFAULT_SEQUENCE)
14
+ # If this flag set, TxIn#sequence is NOT interpreted as a relative lock-time.
15
+ SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31)
16
+
17
+ # If TxIn#sequence encodes a relative lock-time and this flag is set, the relative lock-time has units of 512 seconds,
18
+ # otherwise it specifies blocks with a granularity of 1.
19
+ SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22)
20
+
21
+ # If TxIn#sequence encodes a relative lock-time, this mask is applied to extract that lock-time from the sequence field.
22
+ SEQUENCE_LOCKTIME_MASK = 0x0000ffff
23
+
24
+ def initialize(out_point: nil, script_sig: Bitcoin::Script.new, script_witness: ScriptWitness.new, sequence: SEQUENCE_FINAL)
14
25
  @out_point = out_point
15
26
  @script_sig = script_sig
16
27
  @script_witness = script_witness
@@ -24,8 +35,11 @@ module Bitcoin
24
35
  i.out_point = OutPoint.new(hash.reverse.bth, index)
25
36
  sig_length = Bitcoin.unpack_var_int_from_io(buf)
26
37
  sig = buf.read(sig_length)
27
- i.script_sig = Script.new
28
- i.script_sig.chunks[0] = sig
38
+ if i.coinbase?
39
+ i.script_sig.chunks[0] = sig
40
+ else
41
+ i.script_sig = Script.parse_from_payload(sig)
42
+ end
29
43
  i.sequence = buf.read(4).unpack('V').first
30
44
  i
31
45
  end
@@ -38,6 +52,11 @@ module Bitcoin
38
52
  p = out_point.to_payload
39
53
  p << Bitcoin.pack_var_int(script_sig.to_payload.bytesize)
40
54
  p << script_sig.to_payload << [sequence].pack('V')
55
+ p
56
+ end
57
+
58
+ def has_witness?
59
+ !script_witness.empty?
41
60
  end
42
61
 
43
62
  end
@@ -13,7 +13,7 @@ module Bitcoin
13
13
 
14
14
  def self.parse_from_payload(payload)
15
15
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
16
- value = buf.read(8).unpack('Q').first
16
+ value = buf.read(8).unpack('q').first
17
17
  script_size = Bitcoin.unpack_var_int_from_io(buf)
18
18
  new(value: value, script_pubkey: Script.parse_from_payload(buf.read(script_size)))
19
19
  end
@@ -0,0 +1,93 @@
1
+ module Bitcoin
2
+
3
+ class Validation
4
+
5
+ # check transaction validation
6
+ def check_tx(tx, state)
7
+ # Basic checks that don't depend on any context
8
+ if tx.inputs.empty?
9
+ return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-vin-empty')
10
+ end
11
+
12
+ if tx.outputs.empty?
13
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-vout-empty')
14
+ end
15
+
16
+ # Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
17
+ if tx.serialize_old_format.bytesize * Bitcoin::WITNESS_SCALE_FACTOR > Bitcoin::MAX_BLOCK_WEIGHT
18
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-oversize')
19
+ end
20
+
21
+ # Check for negative or overflow output values
22
+ amount = 0
23
+ tx.outputs.each do |o|
24
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-vout-negative') if o.value < 0
25
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-vout-toolarge') if MAX_MONEY < o.value
26
+ amount += o.value
27
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-vout-toolarge') if MAX_MONEY < amount
28
+ end
29
+
30
+ # Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
31
+ out_points = tx.inputs.map{|i|i.out_point.to_payload}
32
+ unless out_points.size == out_points.uniq.size
33
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-inputs-duplicate')
34
+ end
35
+
36
+ if tx.coinbase_tx?
37
+ if tx.inputs[0].script_sig.size < 2 || tx.inputs[0].script_sig.size > 100
38
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-cb-length')
39
+ end
40
+ else
41
+ tx.inputs.each do |i|
42
+ if i.out_point.nil? || !i.out_point.valid?
43
+ return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_resoin: 'bad-txns-prevout-null')
44
+ end
45
+ end
46
+ end
47
+ true
48
+ end
49
+
50
+ end
51
+
52
+ class ValidationState
53
+
54
+ MODE = {valid: 0, invlid: 1, error: 2}
55
+
56
+ attr_accessor :mode
57
+ attr_accessor :n_dos
58
+ attr_accessor :reject_reason
59
+ attr_accessor :reject_code
60
+ attr_accessor :corruption_possible
61
+ attr_accessor :debug_message
62
+
63
+ def initialize
64
+ @mode = MODE[:valid]
65
+ @n_dos = 0
66
+ @reject_code = 0
67
+ @corruption_possible = false
68
+ end
69
+
70
+ def DoS(level, ret: false, reject_code: 0, reject_resoin: '', corruption_in: false, debug_message: '')
71
+ @reject_code = reject_code
72
+ @reject_reason = reject_resoin
73
+ @corruption_possible = corruption_in
74
+ @debug_message = debug_message
75
+ return ret if mode == MODE[:error]
76
+ @n_dos += level
77
+ @mode = MODE[:invalid]
78
+ ret
79
+ end
80
+
81
+ def valid?
82
+ mode == MODE[:valid]
83
+ end
84
+
85
+ def invalid?
86
+ mode == MODE[:invalid]
87
+ end
88
+
89
+ def error?
90
+ mode == MODE[:error]
91
+ end
92
+ end
93
+ end
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
  end