bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. metadata +295 -0
@@ -0,0 +1,77 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe 'Bitcoin::Protocol::Parser (version)' do
4
+
5
+ class Version_Handler < Bitcoin::Protocol::Handler
6
+ attr_reader :pkt
7
+ def on_version(pkt)
8
+ @pkt = pkt
9
+ end
10
+ end
11
+
12
+ it 'parses version packets' do
13
+ pkt = Bitcoin::Protocol.pkt("version",
14
+ ["60ea00000100000000000000b3c1424f00000000010000000000000000000000000000000000ffff7f000001e1ca010000000000000000000000000000000000ffff7f000001479d9525d0c7b30688ae122f626974636f696e2d71743a302e362e302f82b60000"].pack("H*"))
15
+
16
+ parser = Bitcoin::Protocol::Parser.new( handler = Version_Handler.new )
17
+ parser.parse(pkt + "AAAA").should == "AAAA"
18
+
19
+ pkt = handler.pkt
20
+ pkt.fields.should == {
21
+ :version => 60000,
22
+ :services => "\x01\x00\x00\x00\x00\x00\x00\x00",
23
+ :time => 1329775027,
24
+ :from => "127.0.0.1:18333",
25
+ :to => "127.0.0.1:57802",
26
+ :nonce => 12576309328653329813,
27
+ :user_agent => "/bitcoin-qt:0.6.0/",
28
+ :last_block => 46722
29
+ }
30
+
31
+ pkt = [
32
+ "f9 be b4 d9 76 65 72 73 69 6f 6e 00 00 00 00 00 55 00 00 00 a4 c2 08 cb 40 9c 00 00 01 00 00 00 00 00 00 00 10 42 c9 4e 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff 7f 00 00 01 04 d2 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff 7f 00 00 01 47 9d 4b b8 bb 21 ae d7 f0 71 00 fa 00 00 00"
33
+ .split(" ").join].pack("H*")
34
+
35
+
36
+ parser = Bitcoin::Protocol::Parser.new( handler = Version_Handler.new )
37
+ parser.parse(pkt + "AAAA").should == "AAAA"
38
+
39
+ pkt = handler.pkt
40
+ pkt.fields.should == {
41
+ :version => 40000,
42
+ :services => "\x01\x00\x00\x00\x00\x00\x00\x00",
43
+ :time => 1321812496,
44
+ :from => "127.0.0.1:18333",
45
+ :to => "127.0.0.1:1234",
46
+ :nonce => 8210299263586646091,
47
+ :user_agent => nil,
48
+ :last_block => 250
49
+ }
50
+ end
51
+
52
+ it 'creates version packets' do
53
+ version = Bitcoin::Protocol::Version.new({
54
+ :time => 1337,
55
+ :from => "127.0.0.1:8333",
56
+ :to => "127.0.0.1:1234",
57
+ :nonce => 123,
58
+ :last_block => 188617,
59
+ })
60
+
61
+ parser = Bitcoin::Protocol::Parser.new( handler = Version_Handler.new )
62
+ parser.parse( version.to_pkt )
63
+
64
+ pkt = handler.pkt
65
+ pkt.fields.should == {
66
+ :version => Bitcoin::Protocol::VERSION,
67
+ :services => "\x01\x00\x00\x00\x00\x00\x00\x00",
68
+ :time => 1337,
69
+ :to => "127.0.0.1:8333",
70
+ :from => "127.0.0.1:1234",
71
+ :nonce => 123,
72
+ :user_agent => "/bitcoin-ruby:#{Bitcoin::VERSION}/",
73
+ :last_block => 188617
74
+ }
75
+ end
76
+
77
+ end
@@ -0,0 +1,129 @@
1
+ require_relative 'spec_helper'
2
+
3
+ include Bitcoin::Builder
4
+
5
+ describe "reorg" do
6
+
7
+ def create_block prev, store = true
8
+ block = blk do |b|
9
+ b.prev_block prev
10
+ b.tx do |t|
11
+ t.input {|i| i.coinbase }
12
+ t.output do |o|
13
+ o.value 5000000000
14
+ o.script {|s| s.type :address; s.recipient Bitcoin::Key.generate.addr }
15
+ end
16
+ end
17
+ end
18
+ @store.store_block(block) if store
19
+ block
20
+ end
21
+
22
+ def balance addr
23
+ @store.get_balance(Bitcoin.hash160_from_address(addr))
24
+ end
25
+
26
+ before do
27
+ Bitcoin.network = :testnet
28
+ @store = Bitcoin::Storage.sequel(:db => "sqlite:/")
29
+ @store.log.level = :warn
30
+ @block0 = Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin'))
31
+ @store.store_block(@block0)
32
+ end
33
+
34
+ it "should reorg a single side block" do
35
+ @store.get_head.should == @block0
36
+
37
+ block1 = create_block @block0.hash
38
+ @store.get_head.should == block1
39
+
40
+ block2_0 = create_block block1.hash
41
+ @store.get_head.should == block2_0
42
+
43
+ block2_1 = create_block block1.hash
44
+ @store.get_head.should == block2_0
45
+
46
+ block3 = create_block block2_1.hash
47
+ @store.get_head.should == block3
48
+ @store.get_block_by_depth(2).hash.should == block2_1.hash
49
+ end
50
+
51
+ it "should reorg two side blocks" do
52
+ block1 = create_block @block0.hash
53
+ @store.get_head.should == block1
54
+
55
+ block2_0 = create_block block1.hash
56
+ @store.get_head.should == block2_0
57
+
58
+ block2_1 = create_block block1.hash
59
+ @store.get_head.should == block2_0
60
+
61
+ block3_1 = create_block block2_1.hash
62
+ @store.get_head.should == block3_1
63
+
64
+ block3_0 = create_block block2_0.hash
65
+ @store.get_head.should == block3_1
66
+
67
+ block4 = create_block block3_0.hash
68
+ @store.get_head.should == block4
69
+ end
70
+
71
+ it "should reconnect orphans" do
72
+ blocks = [@block0]
73
+ 3.times { blocks << create_block(blocks.last.hash, false) }
74
+
75
+ {
76
+ [0, 1, 2, 3] => [0, 1, 2, 3],
77
+ [0, 1, 3, 2] => [0, 1, 1, 3],
78
+ [0, 3, 2, 1] => [0, 0, 0, 3],
79
+ [0, 3, 1, 2] => [0, 0, 1, 3],
80
+ [0, 2, 3, 1] => [0, 0, 0, 3],
81
+ }.each do |order, result|
82
+ @store.reset
83
+ order.each_with_index do |n, i|
84
+ @store.store_block(blocks[n])
85
+ @store.get_head.should == blocks[result[i]]
86
+ end
87
+ end
88
+
89
+ i = 3; (0..i).to_a.permutation.each do |order|
90
+ @store.reset
91
+ order.each {|n| @store.store_block(blocks[n]) }
92
+ @store.get_head.should == blocks[i]
93
+ end
94
+ end
95
+
96
+ it "should handle existing blocks" do
97
+ Bitcoin.network = :testnet
98
+ blocks = [@block0]
99
+ 3.times { blocks << create_block(blocks.last.hash, false) }
100
+ 3.times do
101
+ @store.store_block(blocks[1]).should == [1, 0]
102
+ @store.store_block(blocks[2]).should == [2, 0]
103
+ @store.get_head.should == blocks[2]
104
+ end
105
+ end
106
+
107
+ # see https://bitcointalk.org/index.php?topic=46370.0
108
+ it "should pass reorg unit tests" do
109
+ Bitcoin.network = :bitcoin
110
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_0_to_4.dat"
111
+ @store.get_depth.should == 4
112
+ @store.get_head.hash.should =~ /000000002f264d65040/
113
+ balance("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").should == 10000000000
114
+ balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 0
115
+ balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 5000000000
116
+ balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 10000000000
117
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_3A.dat"
118
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_4A.dat"
119
+ @store.get_head.hash.should =~ /000000002f264d65040/
120
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_5A.dat"
121
+ @store.get_depth.should == 5
122
+ @store.get_head.hash.should =~ /00000000195f85184e7/
123
+ balance("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").should == 15000000000
124
+ balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 1000000000
125
+ balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 0
126
+ balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 14000000000
127
+ end
128
+
129
+ end
@@ -0,0 +1,417 @@
1
+ require_relative '../spec_helper.rb'
2
+ require 'bitcoin/script'
3
+
4
+ describe "Bitcoin::Script OPCODES" do
5
+
6
+ before do
7
+ @script = Bitcoin::Script.new("")
8
+ @script.class.instance_eval { attr_accessor :stack, :stack_alt }
9
+ @script.stack << "foobar"
10
+ end
11
+
12
+ def op(op, stack)
13
+ @script.stack = stack
14
+ @script.send("op_#{op}")
15
+ @script.stack
16
+ end
17
+
18
+ it "should do OP_NOP" do
19
+ @script.op_nop
20
+ @script.stack.should == ["foobar"]
21
+ end
22
+
23
+ it "should do OP_DUP" do
24
+ @script.op_dup
25
+ @script.stack.should == ["foobar", "foobar"]
26
+ end
27
+
28
+ it "should do OP_SHA256" do
29
+ @script.op_sha256
30
+ @script.stack.should ==
31
+ [["c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"].pack("H*")]
32
+ end
33
+
34
+ it "should do OP_SHA1" do
35
+ @script.op_sha1
36
+ @script.stack.should ==
37
+ [["8843d7f92416211de9ebb963ff4ce28125932878"].pack("H*")]
38
+ end
39
+
40
+ it "should do OP_HASH160" do
41
+ @script.op_hash160
42
+ @script.stack.should ==
43
+ [["f6c97547d73156abb300ae059905c4acaadd09dd"].pack("H*")]
44
+ end
45
+
46
+ it "should do OP_RIPEMD160" do
47
+ @script.op_ripemd160
48
+ @script.stack.should ==
49
+ [["a06e327ea7388c18e4740e350ed4e60f2e04fc41"].pack("H*")]
50
+ end
51
+
52
+ it "should do OP_HASH256" do
53
+ @script.op_hash256
54
+ @script.stack.should ==
55
+ [["3f2c7ccae98af81e44c0ec419659f50d8b7d48c681e5d57fc747d0461e42dda1"].pack("H*")]
56
+ end
57
+
58
+ it "should do OP_TOALTSTACK" do
59
+ @script.op_toaltstack
60
+ @script.stack.should == []
61
+ @script.stack_alt.should == ["foobar"]
62
+ end
63
+
64
+ it "should do OP_FROMALTSTACK" do
65
+ @script.instance_eval { @stack_alt << "barfoo" }
66
+ @script.op_fromaltstack
67
+ @script.stack.should == ["foobar", "barfoo"]
68
+ @script.stack_alt.should == []
69
+ end
70
+
71
+ it "should do OP_TUCK" do
72
+ @script.instance_eval { @stack += ["foo", "bar"] }
73
+ @script.op_tuck
74
+ @script.stack.should == ["foobar", "bar", "foo", "bar"]
75
+ end
76
+
77
+ it "should do OP_SWAP" do
78
+ @script.instance_eval { @stack << "barfoo" }
79
+ @script.op_swap
80
+ @script.stack.should == ["barfoo", "foobar"]
81
+ end
82
+
83
+ it "should do OP_BOOLAND" do
84
+ op(:booland, [0, 0]).should == [0]
85
+ op(:booland, [0, 1]).should == [0]
86
+ op(:booland, [1, 0]).should == [0]
87
+ op(:booland, [1, 1]).should == [1]
88
+ end
89
+
90
+ it "should do OP_ADD" do
91
+ op(:add, [0, 1]).should == [1]
92
+ op(:add, [3, 4]).should == [7]
93
+ op(:add, [5,-4]).should == [1]
94
+ end
95
+
96
+ it "should do OP_SUB" do
97
+ op(:sub, [2, 3]).should == [1]
98
+ op(:sub, [1, 9]).should == [8]
99
+ op(:sub, [3, 1]).should == [-2]
100
+ end
101
+
102
+ it "should do OP_GREATERTHANOREQUAL" do
103
+ op(:greaterthanorequal, [1, 2]).should == [1]
104
+ op(:greaterthanorequal, [2, 2]).should == [1]
105
+ op(:greaterthanorequal, [2, 1]).should == [0]
106
+ end
107
+
108
+ it "should do OP_DROP" do
109
+ @script.op_drop
110
+ @script.stack.should == []
111
+ end
112
+
113
+ it "should do OP_EQUAL" do
114
+ op(:equal, [1,2]).should == [0]
115
+ op(:equal, [1,1]).should == [1]
116
+ end
117
+
118
+ it "should do OP_VERIFY" do
119
+ op(:verify, [1]).should == []
120
+ op(:verify, [0]).should == [0]
121
+ end
122
+
123
+ it "should do OP_EQUALVERIFY" do
124
+ op(:equalverify, [1,2]).should == [0]
125
+ @script.invalid?.should == true
126
+ op(:equalverify, [1,1]).should == []
127
+ @script.invalid?.should == false
128
+ end
129
+
130
+ it "should do OP_0" do
131
+ @script.op_0
132
+ @script.stack.should == ["foobar", ""]
133
+ end
134
+
135
+ it "should do OP_1" do
136
+ @script.op_1
137
+ @script.stack.should == ["foobar", 1]
138
+ end
139
+
140
+ it "should do OP_MIN" do
141
+ [
142
+ [4, 5], [5, 4], [4, 4]
143
+ ].each{|s|
144
+ @script.instance_eval { @stack = s }
145
+ @script.op_min
146
+ @script.stack.should == [4]
147
+ }
148
+ end
149
+
150
+ it "should do OP_MAX" do
151
+ [
152
+ [4, 5], [5, 4], [5, 5]
153
+ ].each{|s|
154
+ @script.instance_eval { @stack = s }
155
+ @script.op_max
156
+ @script.stack.should == [5]
157
+ }
158
+ end
159
+
160
+ it "should do op_2over" do
161
+ @script.instance_eval { @stack = [1,2,3,4] }
162
+ @script.op_2over
163
+ @script.stack.should == [1,2,3,4,1,2]
164
+ end
165
+
166
+ it "should do op_2swap" do
167
+ @script.instance_eval { @stack = [1,2,3,4] }
168
+ @script.op_2swap
169
+ @script.stack.should == [3,4,1,2]
170
+ end
171
+
172
+ it "should do op_ifdup" do
173
+ @script.instance_eval { @stack = [1] }
174
+ @script.op_ifdup
175
+ @script.stack.should == [1,1]
176
+
177
+ @script.instance_eval { @stack = ['a'] }
178
+ @script.op_ifdup
179
+ @script.stack.should == ['a','a']
180
+
181
+ @script.instance_eval { @stack = [0] }
182
+ @script.op_ifdup
183
+ @script.stack.should == [0]
184
+ end
185
+
186
+ it "should do op_1negate" do
187
+ @script.instance_eval { @stack = [] }
188
+ @script.op_1negate
189
+ @script.stack.should == [ -1 ]
190
+ end
191
+
192
+ it "should do op_depth" do
193
+ @script.instance_eval { @stack = [] }
194
+ @script.op_depth
195
+ @script.stack.should == [0]
196
+
197
+ @script.instance_eval { @stack = [1,2,3] }
198
+ @script.op_depth
199
+ @script.stack.should == [1,2,3,3]
200
+ end
201
+
202
+ it "should do OP_CHECKSIG" do
203
+ @script.stack = ["bar", "foo"]
204
+ verify_callback = proc{|pubkey,signature,type|
205
+ pubkey .should == "foo"
206
+ signature.should == "ba"
207
+ type .should == "r".ord
208
+ true
209
+ }
210
+ @script.op_checksig(verify_callback).should == [1]
211
+
212
+ @script.stack = ["bar", "foo"]
213
+ verify_callback = proc{ true }
214
+ @script.op_checksig(verify_callback).should == [1]
215
+
216
+ @script.stack = ["bar", "foo"]
217
+ verify_callback = proc{ false }
218
+ @script.op_checksig(verify_callback).should == [0]
219
+
220
+ @script.stack = ["foo"]
221
+ verify_callback = proc{ false }
222
+ @script.op_checksig(verify_callback).should == nil
223
+
224
+
225
+ pubkey = ["04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3"].pack("H*")
226
+ signature = ["304402202c2fb840b527326f9bbc7ce68c6c196a368a38864b5a47681352c4b2f416f7ed02205c4801cfa8aed205f26c7122ab5a5934fcf7a2f038fd130cdd8bcc56bdde0a00"].pack("H*")
227
+ hash_type = [1].pack("C")
228
+ signature_data = ["20245059adb84acaf1aa942b5d8a586da7ba76f17ecb5de4e7543e1ce1b94bc3"].pack("H*")
229
+
230
+ @script.stack = [signature + hash_type, pubkey]
231
+ verify_callback = proc{|pub,sig,hash_type|
232
+ pub .should == pubkey
233
+ sig .should == signature
234
+ hash_type.should == 1
235
+
236
+ hash = signature_data
237
+ Bitcoin.verify_signature( hash, sig, pub.unpack("H*")[0] )
238
+ }
239
+ @script.op_checksig(verify_callback).should == [1]
240
+
241
+
242
+ @script.stack = [signature + hash_type, pubkey]
243
+ verify_callback = proc{|pub,sig,hash_type|
244
+ hash = "foo" + signature_data
245
+ Bitcoin.verify_signature( hash, sig, pub.unpack("H*")[0] )
246
+ }
247
+ @script.op_checksig(verify_callback).should == [0]
248
+
249
+ @script.stack = [signature + hash_type, pubkey]
250
+ verify_callback = proc{|pub,sig,hash_type|
251
+ hash = signature_data
252
+ Bitcoin.verify_signature( hash, "foo", pub.unpack("H*")[0] )
253
+ }
254
+ @script.op_checksig(verify_callback).should == [0]
255
+
256
+ @script.stack = [signature + hash_type, pubkey]
257
+ verify_callback = proc{|pub,sig,hash_type|
258
+ hash = signature_data
259
+ Bitcoin.verify_signature( hash, sig, "foo" )
260
+ }
261
+ @script.op_checksig(verify_callback).should == [0]
262
+
263
+ # Bitcoin::Key API
264
+ key = Bitcoin::Key.new; key.generate
265
+ sig = (key.sign("foobar") + "\x01").unpack("H*")[0]
266
+ script = Bitcoin::Script.from_string("#{sig} #{key.pub} OP_CHECKSIG")
267
+ script.run{|pk, sig, hash_type|
268
+ k = Bitcoin::Key.new nil, pk.unpack("H*")[0]
269
+ k.verify("foobar", sig)
270
+ }.should == true
271
+ script.stack.should == []
272
+ end
273
+
274
+ def run_script(string, hash)
275
+ script = Bitcoin::Script.from_string(string)
276
+ script.run do |pk, sig, hash_type|
277
+ k = Bitcoin::Key.new nil, pk.unpack("H*")[0]
278
+ k.verify(hash, sig) rescue false
279
+ end == true
280
+ end
281
+
282
+ it "should do OP_CHECKMULTISIG" do
283
+ k1 = Bitcoin::Key.new; k1.generate
284
+ k2 = Bitcoin::Key.new; k2.generate
285
+ k3 = Bitcoin::Key.new; k3.generate
286
+ sig1 = (k1.sign("foobar") + "\x01").unpack("H*")[0]
287
+ sig2 = (k2.sign("foobar") + "\x01").unpack("H*")[0]
288
+ sig3 = (k3.sign("foobar") + "\x01").unpack("H*")[0]
289
+
290
+ script = "0 #{sig1} #{sig2} 2 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
291
+ run_script(script, "foobar").should == true
292
+
293
+ script = "0 #{sig1} #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
294
+ run_script(script, "foobar").should == true
295
+
296
+ script = "0 #{sig2} #{sig3} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
297
+ run_script(script, "foobar").should == true
298
+
299
+ script = "0 #{sig1} #{sig2} #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
300
+ run_script(script, "foobar").should == true
301
+
302
+ script = "#{sig1} #{sig2} #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG" # without OP_NOP
303
+ run_script(script, "foobar").should == true
304
+
305
+ script = "0 #{sig2} 1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
306
+ run_script(script, "foobar").should == true
307
+
308
+ script = "0 #{sig2} OP_TRUE #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
309
+ run_script(script, "foobar").should == true
310
+
311
+ script = "0 #{sig1} #{sig2} #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
312
+ run_script(script, "foobar").should == false
313
+
314
+ script = "0 #{sig1} #{sig2} #{sig3} 3 #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
315
+ run_script(script, "foobar").should == false
316
+
317
+ script = "0 #{sig1} #{sig2} #{sig3} 3 2 #{k3.pub} 2 OP_CHECKMULTISIG"
318
+ run_script(script, "foobar").should == false
319
+
320
+ script = "0 #{sig1} #{sig2} #{sig3} 3 0 #{k3.pub} 2 OP_CHECKMULTISIG"
321
+ run_script(script, "foobar").should == false
322
+
323
+ script = "0 #{sig1} #{sig2} 2 3 #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
324
+ run_script(script, "foobar").should == false
325
+
326
+ script = "0 #{sig1} #{sig2} 0 3 #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
327
+ run_script(script, "foobar").should == false
328
+
329
+ script = "0 #{sig2} f0f0f0f0 2 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
330
+ run_script(script, "foobar").should == false
331
+
332
+ script = "0 afafafaf #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
333
+ run_script(script, "foobar").should == false
334
+
335
+ script = "0 #{sig1} f0f0f0f0 #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
336
+ run_script(script, "foobar").should == false
337
+
338
+ # # TODO: check signature order; these assertions should fail:
339
+ # script = "0 #{sig2} #{sig1} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
340
+ # run_script(script, "foobar").should == false
341
+ # script = "0 #{sig3} #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
342
+ # run_script(script, "foobar").should == false
343
+ # script = "0 #{sig1} #{sig3} #{sig2} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
344
+ # run_script(script, "foobar").should == false
345
+ end
346
+
347
+
348
+ it "should do OP_CHECKHASHVERIFY" do # https://en.bitcoin.it/wiki/BIP_0017
349
+ k1 = Bitcoin::Key.new; k1.generate
350
+ k2 = Bitcoin::Key.new; k2.generate
351
+ k3 = Bitcoin::Key.new; k2.generate
352
+ sig1 = (k1.sign("foobar") + "\x01").unpack("H*")[0]
353
+ sig2 = (k2.sign("foobar") + "\x01").unpack("H*")[0]
354
+ sig3 = (k2.sign("foobar") + "\x01").unpack("H*")[0]
355
+
356
+
357
+ # scriptSig: [signatures...] OP_CODESEPARATOR 1 [pubkey1] [pubkey2] 2 OP_CHECKMULTISIG
358
+ # scriptPubKey: [20-byte-hash of {1 [pubkey1] [pubkey2] 2 OP_CHECKMULTISIG} ] OP_CHECKHASHVERIFY OP_DROP
359
+ script = "1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
360
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
361
+ script = "0 #{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
362
+ run_script(script, "foobar").should == true
363
+
364
+ script = "1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
365
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
366
+ script = "0 #{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_NOP2 OP_DROP" # tests OP_NOP2 as OP_CHECKHASHVERIFY
367
+ run_script(script, "foobar").should == true
368
+
369
+ # invalid checkhashverify
370
+ script = "1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
371
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
372
+ script = "1 #{k1.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
373
+ script = "0 #{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_NOP2 OP_DROP" # tests OP_NOP2 as OP_CHECKHASHVERIFY
374
+ run_script(script, "foobar").should == false
375
+
376
+
377
+ # scriptSig: [signature] OP_CODESEPARATOR [pubkey] OP_CHECKSIG
378
+ # scriptPubKey: [20-byte-hash of {[pubkey] OP_CHECKSIG} ] OP_CHECKHASHVERIFY OP_DROP
379
+ script = "#{k1.pub} OP_CHECKSIG"
380
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
381
+ script = "#{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
382
+ run_script(script, "foobar").should == true
383
+
384
+ # invalid checkhashverify
385
+ script = "#{k2.pub} OP_CHECKSIG"
386
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
387
+ script = "#{k1.pub} OP_CHECKSIG"
388
+ script = "#{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
389
+ run_script(script, "foobar").should == false
390
+
391
+ # invalid signature in checksig
392
+ script = "#{k1.pub} OP_CHECKSIG"
393
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
394
+ script = "#{sig2} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
395
+ run_script(script, "foobar").should == false
396
+ end
397
+
398
+ it "should do P2SH" do
399
+ k1 = Bitcoin::Key.new; k1.generate
400
+ sig = (k1.sign("foobar") + "\x01").unpack("H*")[0]
401
+ inner_script = Bitcoin::Script.from_string("#{k1.pub} OP_CHECKSIG").raw.unpack("H*")[0]
402
+ script_hash = Bitcoin.hash160(inner_script)
403
+ script = Bitcoin::Script.from_string("#{sig} #{inner_script} OP_HASH160 #{script_hash} OP_EQUAL")
404
+ script.is_p2sh?.should == true
405
+ run_script(script.to_string, "foobar").should == true
406
+ run_script(script.to_string, "barbaz").should == false
407
+
408
+ script = Bitcoin::Script.from_string("OP_HASH160 #{script_hash} OP_EQUAL")
409
+ script.is_p2sh?.should == true
410
+ run_script(script.to_string, "foobar").should == false
411
+
412
+ address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
413
+ script = Bitcoin::Script.new(Bitcoin::Script.to_address_script(address))
414
+ script.type.should == :p2sh
415
+ end
416
+
417
+ end