btcruby 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ require_relative 'spec_helper'
2
+ require_relative '../lib/btcruby/secp256k1'
3
+ describe BTC::Secp256k1 do
4
+
5
+ def verify_rfc6979_signature(keyhex, msg, sighex)
6
+ key = BTC::Key.new(private_key: keyhex.from_hex)
7
+ hash = BTC.sha256(msg)
8
+ sig = BTC::Secp256k1.ecdsa_signature(hash, key.private_key)
9
+ sig.to_hex.must_equal sighex
10
+ end
11
+
12
+ it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979" do
13
+ verify_rfc6979_signature("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
14
+ "sample",
15
+ "3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124")
16
+ verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001",
17
+ "Satoshi Nakamoto",
18
+ "3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5")
19
+ verify_rfc6979_signature("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
20
+ "Satoshi Nakamoto",
21
+ "3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5")
22
+ verify_rfc6979_signature("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
23
+ "Alan Turing",
24
+ "304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea")
25
+ verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001",
26
+ "All those moments will be lost in time, like tears in rain. Time to die...",
27
+ "30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21")
28
+ verify_rfc6979_signature("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
29
+ "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
30
+ "3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6")
31
+ end
32
+
33
+ end
@@ -6,3 +6,118 @@ require_relative '../lib/btcruby/extensions'
6
6
 
7
7
  # So every test can access classes directly without prefixing them with BTC::
8
8
  include BTC
9
+
10
+ # Script helper used by transaction_spec and script_interpreter_spec
11
+ FLAGS_MAP = {
12
+ "" => ScriptFlags::SCRIPT_VERIFY_NONE,
13
+ "NONE" => ScriptFlags::SCRIPT_VERIFY_NONE,
14
+ "P2SH" => ScriptFlags::SCRIPT_VERIFY_P2SH,
15
+ "STRICTENC" => ScriptFlags::SCRIPT_VERIFY_STRICTENC,
16
+ "DERSIG" => ScriptFlags::SCRIPT_VERIFY_DERSIG,
17
+ "LOW_S" => ScriptFlags::SCRIPT_VERIFY_LOW_S,
18
+ "NULLDUMMY" => ScriptFlags::SCRIPT_VERIFY_NULLDUMMY,
19
+ "SIGPUSHONLY" => ScriptFlags::SCRIPT_VERIFY_SIGPUSHONLY,
20
+ "MINIMALDATA" => ScriptFlags::SCRIPT_VERIFY_MINIMALDATA,
21
+ "DISCOURAGE_UPGRADABLE_NOPS" => ScriptFlags::SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
22
+ "CLEANSTACK" => ScriptFlags::SCRIPT_VERIFY_CLEANSTACK,
23
+ "CHECKLOCKTIMEVERIFY" => ScriptFlags::SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
24
+ }
25
+
26
+ def parse_script(json_script, expected_result = true)
27
+ # Note: individual 0xXX bytes may not be individually valid pushdatas, but will be valid when composed together.
28
+ # Since Script parses binary string right away, we need to compose all distinct bytes in a single hex string.
29
+ orig_string = json_script
30
+ json_script = json_script.dup
31
+ oldsize = json_script.size + 1
32
+ while json_script.size != oldsize
33
+ oldsize = json_script.size
34
+ json_script.gsub!(/0x([0-9a-fA-F]+)\s+0x/, "0x\\1")
35
+ end
36
+ json_script.split(" ").inject(Script.new) do |parsed_script, x|
37
+ if x.size == 0
38
+ # Empty string, ignore.
39
+ parsed_script
40
+ elsif x =~ /^-?\d+$/
41
+ # Number
42
+ n = x.to_i
43
+ if (n == -1) || (n >= 1 and n <= 16)
44
+ parsed_script << Opcode.opcode_for_small_integer(n)
45
+ else
46
+ parsed_script << ScriptNumber.new(integer: n).data
47
+ end
48
+ elsif x[0,2] == "0x"
49
+ # Raw hex data, inserted NOT pushed onto stack:
50
+ data = BTC.from_hex(x[2..-1])
51
+ Script.new(data: parsed_script.data + data)
52
+ elsif x =~ /^'.*'$/
53
+ # Single-quoted string, pushed as data.
54
+ parsed_script << x[1..-2]
55
+ else
56
+ # opcode, e.g. OP_ADD or ADD:
57
+ opcode = Opcode.opcode_for_name("OP_" + x)
58
+ opcode = Opcode.opcode_for_name(x) if opcode == OP_INVALIDOPCODE
59
+ parsed_script << opcode
60
+ end
61
+ end
62
+ rescue => e
63
+ if expected_result
64
+ # puts "json_script = #{orig_string.inspect}"
65
+ # puts "json_script = #{json_script.inspect}"
66
+ # puts "EXCEPTION: #{e}"
67
+ end
68
+ return nil
69
+ end
70
+
71
+ def parse_flags(string)
72
+ string.split(",").inject(0) do |flags, x|
73
+ f = FLAGS_MAP[x] or raise RuntimeError, "unrecognized script flag: #{x.inspect}"
74
+ flags | f
75
+ end
76
+ end
77
+
78
+ def build_crediting_transaction(scriptPubKey)
79
+ txCredit = Transaction.new;
80
+ txCredit.version = 1;
81
+ txCredit.lock_time = 0;
82
+ txCredit.add_input(TransactionInput.new(
83
+ previous_hash: nil,
84
+ coinbase_data: (Script.new << ScriptNumber.new(integer:0) << ScriptNumber.new(integer:0)).data
85
+ ))
86
+ txCredit.add_output(TransactionOutput.new(
87
+ script: scriptPubKey,
88
+ value: 0
89
+ ))
90
+ txCredit
91
+ end
92
+
93
+ def build_spending_transaction(scriptSig, txCredit)
94
+ txSpend = Transaction.new
95
+ txSpend.version = 1;
96
+ txSpend.lock_time = 0;
97
+ txSpend.add_input(TransactionInput.new(
98
+ previous_hash: txCredit.transaction_hash,
99
+ previous_index: 0,
100
+ signature_script: scriptSig
101
+ ))
102
+ txSpend.add_output(TransactionOutput.new(
103
+ script: Script.new,
104
+ value: 0
105
+ ))
106
+ txSpend
107
+ end
108
+
109
+ # ctx = build_crediting_transaction(Script.new)
110
+ # stx = build_spending_transaction(Script.new, build_crediting_transaction(Script.new))
111
+ #
112
+ # puts "crediting tx: #{ctx.transaction_id}" # 7f33a2f5ace097f071010d5105e7fd01f22c83d8d5daa741a41f2a630a2af23b
113
+ # puts "spending tx: #{stx.transaction_id}" # add55eb99bb1f653ab822ea4177cb0f9673bcc5c2c4c729894ab0c626c8fa1e1
114
+ #
115
+ # puts "crediting tx: #{ctx.data.to_hex}"
116
+ # puts "spending tx: #{stx.data.to_hex}"
117
+ # From Bitcoin Core:
118
+ # ctxdummy: 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020000ffffffff0100000000000000000000000000;
119
+ # ID = 7f33a2f5ace097f071010d5105e7fd01f22c83d8d5daa741a41f2a630a2af23b
120
+ # stxdummy: 01000000013bf22a0a632a1fa441a7dad5d8832cf201fde705510d0171f097e0acf5a2337f0000000000ffffffff0100000000000000000000000000;
121
+ # ID = add55eb99bb1f653ab822ea4177cb0f9673bcc5c2c4c729894ab0c626c8fa1e1
122
+
123
+
@@ -1,4 +1,7 @@
1
1
  require_relative 'spec_helper'
2
+ require_relative 'data/tx_valid'
3
+ require_relative 'data/tx_invalid'
4
+
2
5
  describe BTC::Transaction do
3
6
 
4
7
  it "should have core attributes" do
@@ -19,6 +22,156 @@ describe BTC::Transaction do
19
22
  })
20
23
 
21
24
  end
25
+
26
+ describe "Bitcoin Core test vectors" do
27
+
28
+ module TxTestHelper
29
+ extend self
30
+
31
+ # Read tests from test/data/tx_valid.json
32
+ # Format is an array of arrays
33
+ # Inner arrays are either [ "comment" ]
34
+ # or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
35
+ # ... where all scripts are stringified scripts.
36
+ #
37
+ # verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
38
+ def parse_tests(records, expected_result)
39
+ comment = nil
40
+ records.each do |test|
41
+ if test[0].is_a?(Array)
42
+ if test.size != 3 || !test[1].is_a?(String) || !test[2].is_a?(String)
43
+ raise "Bad test: #{test.inspect} (#{test.size} #{test[1].class} #{test[2].class})"
44
+ end
45
+ mapprevOutScriptPubKeys = {} # Outpoint => Script
46
+ inputs = test[0]
47
+ inputs.each do |input|
48
+ raise "Bad test: input is not an array: #{test.inspect}" if !input.is_a?(Array)
49
+ raise "Bad test: input is an array of 3 items: #{test.inspect}" if input.size != 3
50
+ previd, previndex, scriptstring = input
51
+
52
+ outpoint = Outpoint.new(transaction_id: previd, index: previndex)
53
+
54
+ mapprevOutScriptPubKeys[outpoint] = parse_script(scriptstring)
55
+ end
56
+
57
+ tx = Transaction.new(hex: test[1])
58
+ flags = parse_flags(test[2])
59
+
60
+ if debug_filter(test)
61
+ validation_proc = lambda do
62
+ validation_passed = Validation.new.check_transaction(tx, ValidationState.new)
63
+ if expected_result
64
+ validation_passed.must_equal expected_result
65
+ end
66
+ script_passed = false
67
+
68
+ if validation_passed
69
+ tx.inputs.each do |txin|
70
+ output_script = mapprevOutScriptPubKeys[txin.outpoint]
71
+ raise "Bad test: output script not found: #{test.inspect}" if !output_script
72
+ sig_script = txin.signature_script
73
+ if !sig_script
74
+ sig_script = Script.new(data: txin.coinbase_data)
75
+ end
76
+
77
+ checker = TransactionSignatureChecker.new(transaction: tx, input_index: txin.index)
78
+ plugins = []
79
+ plugins << P2SHPlugin.new if (flags & ScriptFlags::SCRIPT_VERIFY_P2SH) != 0
80
+ plugins << CLTVPlugin.new if (flags & ScriptFlags::SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY) != 0
81
+ interpreter = ScriptInterpreter.new(
82
+ flags: flags,
83
+ plugins: plugins,
84
+ signature_checker: checker,
85
+ raise_on_failure: expected_result,
86
+ )
87
+ #Diagnostics.current.trace do
88
+ script_passed = interpreter.verify_script(signature_script: sig_script, output_script: output_script)
89
+ if !script_passed
90
+ break
91
+ end
92
+ #end
93
+ end
94
+ end
95
+ (script_passed && validation_passed).must_equal expected_result
96
+ end # proc
97
+
98
+ yield(comment || test.inspect, validation_proc)
99
+ end # if not filtered
100
+
101
+ comment = nil
102
+ else
103
+ comment ||= ""
104
+ comment += test[0].gsub(/\.$/,"") + ". "
105
+ comment.gsub!(/\. $/, "")
106
+ end
107
+ end
108
+ end
109
+
110
+ def debug_filter(test)
111
+
112
+ #return test.inspect[%{010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000}]
113
+
114
+
115
+ # !!! SIGHASH_SINGLE tx: afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae hashed for input 0: 1eccdc1f7a4783924a49113b491a847de2f89a1e7d73b1ae561d80f918035f46
116
+ # !!! SIGHASH_SINGLE tx: afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae hashed for input 1: 1943e87af64d0bde608a85330f09aa5c9887a4fdfd9ca6d7a139bef27fee8e3b
117
+ # !!! SIGHASH_SINGLE tx: afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae hashed for input 2: 1c1f068da6a721f2ecb0fdac3b8adcb4073fee34506971472d29d305507894d6
118
+ #return test.inspect[%{DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG}]
119
+
120
+ #return test.inspect[%{[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"]}]
121
+ true
122
+ end
123
+
124
+ # def verify_script(tx, txin, sig_script, output_script, flags, expected_result, record)
125
+ # checker = TransactionSignatureChecker.new(transaction: tx, input_index: txin.index)
126
+ # plugins = []
127
+ # plugins << P2SHPlugin.new if (flags & ScriptFlags::SCRIPT_VERIFY_P2SH) != 0
128
+ # plugins << CLTVPlugin.new if (flags & ScriptFlags::SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY) != 0
129
+ # interpreter = ScriptInterpreter.new(
130
+ # flags: flags,
131
+ # plugins: plugins,
132
+ # signature_checker: checker,
133
+ # raise_on_failure: expected_result,
134
+ # )
135
+ # #Diagnostics.current.trace do
136
+ # checked = Validation.new.check_transaction(tx, ValidationState.new)
137
+ # if expected_result
138
+ # checked.must_equal expected_result
139
+ # end
140
+ # result = false
141
+ # if checked
142
+ # result = interpreter.verify_script(signature_script: sig_script, output_script: output_script)
143
+ # if result != expected_result
144
+ # # puts "Failed scripts: #{sig_script.to_s.inspect} #{output_script.to_s.inspect} flags #{flags}, expected to #{expected_result ? 'succeed' : 'fail'}".gsub(/OP_/, "")
145
+ # # puts "Error: #{interpreter.error.inspect}"
146
+ # #debug("Failed #{expected_result ? 'valid' : 'invalid'} script: #{sig_script.to_s.inspect} #{output_script.to_s.inspect} flags #{flags} -- #{record.inspect}")
147
+ # end
148
+ # end
149
+ # puts
150
+ # puts record.inspect
151
+ # puts "---------------------------"
152
+ # puts (result && checked).inspect
153
+ # puts
154
+ # (result && checked).must_equal expected_result
155
+ # #end
156
+ # end
157
+
158
+
159
+ end
160
+
161
+ TxTestHelper.parse_tests(ValidTxs, true) do |comment, validation_proc|
162
+ it "should validate transaction: #{comment}" do
163
+ validation_proc.call
164
+ #TxTestHelper.verify_script(tx, txin, signature_script, output_script, flags, expected_result, record)
165
+ end
166
+ end
167
+
168
+ TxTestHelper.parse_tests(InvalidTxs, false) do |comment, validation_proc| # |helper, tx, txin, signature_script, output_script, flags, expected_result, record, comment|
169
+ it "should fail transaction: #{comment}" do
170
+ validation_proc.call
171
+ #TxTestHelper.verify_script(tx, txin, signature_script, output_script, flags, expected_result, record)
172
+ end
173
+ end
174
+ end
22
175
 
23
176
  describe "Hash <-> ID conversion" do
24
177
  before do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: btcruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleg Andreev
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-07-30 00:00:00.000000000 Z
12
+ date: 2015-08-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
@@ -101,18 +101,23 @@ files:
101
101
  - lib/btcruby/open_assets/asset_transaction_output.rb
102
102
  - lib/btcruby/open_assets/issuance_id.rb
103
103
  - lib/btcruby/openssl.rb
104
+ - lib/btcruby/outpoint.rb
104
105
  - lib/btcruby/proof_of_work.rb
105
106
  - lib/btcruby/safety.rb
107
+ - lib/btcruby/script/cltv_plugin.rb
106
108
  - lib/btcruby/script/opcode.rb
109
+ - lib/btcruby/script/p2sh_plugin.rb
107
110
  - lib/btcruby/script/script.rb
108
111
  - lib/btcruby/script/script_error.rb
109
112
  - lib/btcruby/script/script_flags.rb
110
113
  - lib/btcruby/script/script_interpreter.rb
114
+ - lib/btcruby/script/script_interpreter_plugin.rb
111
115
  - lib/btcruby/script/script_number.rb
112
116
  - lib/btcruby/script/signature_checker.rb
113
117
  - lib/btcruby/script/signature_hashtype.rb
114
118
  - lib/btcruby/script/test_signature_checker.rb
115
119
  - lib/btcruby/script/transaction_signature_checker.rb
120
+ - lib/btcruby/secp256k1.rb
116
121
  - lib/btcruby/transaction.rb
117
122
  - lib/btcruby/transaction_builder.rb
118
123
  - lib/btcruby/transaction_builder/errors.rb
@@ -120,8 +125,8 @@ files:
120
125
  - lib/btcruby/transaction_builder/result.rb
121
126
  - lib/btcruby/transaction_builder/signer.rb
122
127
  - lib/btcruby/transaction_input.rb
123
- - lib/btcruby/transaction_outpoint.rb
124
128
  - lib/btcruby/transaction_output.rb
129
+ - lib/btcruby/validation.rb
125
130
  - lib/btcruby/version.rb
126
131
  - lib/btcruby/wif.rb
127
132
  - lib/btcruby/wire_format.rb
@@ -131,6 +136,10 @@ files:
131
136
  - spec/block_header_spec.rb
132
137
  - spec/block_spec.rb
133
138
  - spec/currency_formatter_spec.rb
139
+ - spec/data/script_invalid.rb
140
+ - spec/data/script_valid.rb
141
+ - spec/data/tx_invalid.rb
142
+ - spec/data/tx_valid.rb
134
143
  - spec/data_spec.rb
135
144
  - spec/diagnostics_spec.rb
136
145
  - spec/key_spec.rb
@@ -145,7 +154,9 @@ files:
145
154
  - spec/open_assets/asset_transaction_spec.rb
146
155
  - spec/open_assets/issuance_id_spec.rb
147
156
  - spec/proof_of_work_spec.rb
157
+ - spec/script_interpreter_spec.rb
148
158
  - spec/script_spec.rb
159
+ - spec/secp256k1_spec.rb
149
160
  - spec/spec_helper.rb
150
161
  - spec/transaction_builder_spec.rb
151
162
  - spec/transaction_spec.rb
@@ -194,7 +205,9 @@ test_files:
194
205
  - spec/open_assets/asset_transaction_spec.rb
195
206
  - spec/open_assets/issuance_id_spec.rb
196
207
  - spec/proof_of_work_spec.rb
208
+ - spec/script_interpreter_spec.rb
197
209
  - spec/script_spec.rb
210
+ - spec/secp256k1_spec.rb
198
211
  - spec/transaction_builder_spec.rb
199
212
  - spec/transaction_spec.rb
200
213
  - spec/wire_format_spec.rb