bitcoin-ruby 0.0.1

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.
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