bitcoin-ruby 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +21 -0
  3. data/README.rdoc +85 -25
  4. data/Rakefile +7 -3
  5. data/bin/bitcoin_node +39 -42
  6. data/bin/bitcoin_shell +1 -0
  7. data/bin/bitcoin_wallet +129 -53
  8. data/bitcoin-ruby.gemspec +4 -7
  9. data/concept-examples/blockchain-pow.rb +1 -1
  10. data/doc/CONFIG.rdoc +5 -5
  11. data/doc/EXAMPLES.rdoc +9 -5
  12. data/doc/NAMECOIN.rdoc +34 -0
  13. data/doc/NODE.rdoc +147 -10
  14. data/examples/balance.rb +10 -4
  15. data/examples/bbe_verify_tx.rb +7 -2
  16. data/examples/forwarder.rb +73 -0
  17. data/examples/generate_tx.rb +34 -0
  18. data/examples/simple_network_monitor_and_util.rb +187 -0
  19. data/examples/verify_tx.rb +1 -1
  20. data/lib/bitcoin.rb +308 -18
  21. data/lib/bitcoin/builder.rb +62 -36
  22. data/lib/bitcoin/config.rb +2 -0
  23. data/lib/bitcoin/connection.rb +11 -8
  24. data/lib/bitcoin/electrum/mnemonic.rb +162 -0
  25. data/lib/bitcoin/ffi/openssl.rb +187 -21
  26. data/lib/bitcoin/gui/addr_view.rb +2 -0
  27. data/lib/bitcoin/gui/conn_view.rb +2 -0
  28. data/lib/bitcoin/gui/connection.rb +2 -0
  29. data/lib/bitcoin/gui/em_gtk.rb +2 -0
  30. data/lib/bitcoin/gui/gui.rb +2 -0
  31. data/lib/bitcoin/gui/helpers.rb +2 -0
  32. data/lib/bitcoin/gui/tree_view.rb +2 -0
  33. data/lib/bitcoin/gui/tx_view.rb +2 -0
  34. data/lib/bitcoin/key.rb +77 -11
  35. data/lib/bitcoin/litecoin.rb +81 -0
  36. data/lib/bitcoin/logger.rb +20 -1
  37. data/lib/bitcoin/namecoin.rb +279 -0
  38. data/lib/bitcoin/network/command_client.rb +7 -6
  39. data/lib/bitcoin/network/command_handler.rb +229 -43
  40. data/lib/bitcoin/network/connection_handler.rb +182 -70
  41. data/lib/bitcoin/network/node.rb +231 -106
  42. data/lib/bitcoin/protocol.rb +44 -23
  43. data/lib/bitcoin/protocol/address.rb +5 -3
  44. data/lib/bitcoin/protocol/alert.rb +3 -4
  45. data/lib/bitcoin/protocol/aux_pow.rb +123 -0
  46. data/lib/bitcoin/protocol/block.rb +98 -18
  47. data/lib/bitcoin/protocol/handler.rb +6 -5
  48. data/lib/bitcoin/protocol/parser.rb +44 -19
  49. data/lib/bitcoin/protocol/tx.rb +105 -52
  50. data/lib/bitcoin/protocol/txin.rb +39 -19
  51. data/lib/bitcoin/protocol/txout.rb +28 -13
  52. data/lib/bitcoin/protocol/version.rb +16 -7
  53. data/lib/bitcoin/script.rb +579 -122
  54. data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
  55. data/lib/bitcoin/storage/models.rb +20 -7
  56. data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
  57. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
  58. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
  59. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
  60. data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
  61. data/lib/bitcoin/storage/storage.rb +233 -28
  62. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
  63. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
  64. data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
  65. data/lib/bitcoin/validation.rb +369 -0
  66. data/lib/bitcoin/version.rb +1 -1
  67. data/lib/bitcoin/wallet/coinselector.rb +3 -0
  68. data/lib/bitcoin/wallet/keygenerator.rb +3 -1
  69. data/lib/bitcoin/wallet/keystore.rb +6 -2
  70. data/lib/bitcoin/wallet/txdp.rb +6 -4
  71. data/lib/bitcoin/wallet/wallet.rb +54 -16
  72. data/spec/bitcoin/bitcoin_spec.rb +48 -3
  73. data/spec/bitcoin/builder_spec.rb +40 -17
  74. data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
  75. data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
  76. data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
  77. data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
  78. data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
  79. data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
  80. data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
  81. data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
  82. data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
  83. data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
  84. data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
  85. data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
  86. data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
  87. data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
  88. data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
  89. data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
  90. data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
  91. data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
  92. data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
  93. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  94. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
  95. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  96. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
  97. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
  98. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
  99. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
  100. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
  101. data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
  102. data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
  103. data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
  104. data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
  105. data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
  106. data/spec/bitcoin/key_spec.rb +128 -3
  107. data/spec/bitcoin/namecoin_spec.rb +182 -0
  108. data/spec/bitcoin/network_spec.rb +5 -3
  109. data/spec/bitcoin/node/command_api_spec.rb +376 -0
  110. data/spec/bitcoin/protocol/addr_spec.rb +2 -0
  111. data/spec/bitcoin/protocol/alert_spec.rb +2 -0
  112. data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
  113. data/spec/bitcoin/protocol/block_spec.rb +134 -39
  114. data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
  115. data/spec/bitcoin/protocol/inv_spec.rb +10 -8
  116. data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
  117. data/spec/bitcoin/protocol/ping_spec.rb +2 -0
  118. data/spec/bitcoin/protocol/tx_spec.rb +83 -17
  119. data/spec/bitcoin/protocol/version_spec.rb +7 -5
  120. data/spec/bitcoin/script/opcodes_spec.rb +412 -133
  121. data/spec/bitcoin/script/script_spec.rb +112 -13
  122. data/spec/bitcoin/spec_helper.rb +68 -0
  123. data/spec/bitcoin/storage/reorg_spec.rb +199 -0
  124. data/spec/bitcoin/storage/storage_spec.rb +337 -0
  125. data/spec/bitcoin/storage/validation_spec.rb +261 -0
  126. data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
  127. data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
  128. data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
  129. data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
  130. data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
  131. metadata +105 -51
  132. data/lib/bitcoin/storage/sequel.rb +0 -335
  133. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
  134. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
  135. data/spec/bitcoin/reorg_spec.rb +0 -129
  136. data/spec/bitcoin/storage_spec.rb +0 -229
@@ -0,0 +1,182 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require_relative 'spec_helper.rb'
4
+ require 'bitcoin/script'
5
+
6
+ include Bitcoin
7
+ include Bitcoin::Builder
8
+
9
+ describe 'Bitcoin::Namecoin' do
10
+
11
+ describe :script do
12
+
13
+ before do
14
+ Bitcoin.network = :namecoin
15
+ @name_new = Script.from_string("3045022023686b3584247c07f483de4048f3d5136c4faa2f961a6d1e487eb77437422b51022100b1ea62910f2dbb0533d32bd661e8a212d129057d3d71620572278895dbb5c7b501
16
+ 04b656d7be83e73344e298feba41b38c52ea50d4583ead0c947fd8019f75c906e0d810c5d167ee616c46d28d7cb5ca1d7a20a180470c9dad79524118bafe6cf569 1 820fa9c6d252d6773e4ef26a2feffa93f0237641 OP_2DROP OP_DUP OP_HASH160 eb86f8f23909e248d199192d8407881c3435a5db OP_EQUALVERIFY OP_CHECKSIG")
17
+ @name_firstupdate = Script.from_string("3044022054e1557304c504498f8d40b961373d7401a4e4c1650518db8a9c3a49ee9add7e022061573b2837598c346b21c01969081a309c055d5da23b0ac7e139a6290f2f0a4b01
18
+ 04d2628245cdfc6ccf5a762d303ba8a10bd54d597cfdcbf0ac3823bae666a36bb744ed44a2384f42525e37b5b1150c5321718806c0f941904336588dd624f4ce93 2 642f626974636f696e a8c22832fb0d40e900 7b22696e666f223a7b22726567697374726172223a22687474703a2f2f72656769737465722e646f742d6269742e6f7267227d2c22656d61696c223a2022726567697374657240646f742d6269742e6f7267222c226e73223a5b226e73302e7765622d73776565742d7765622e6e6574222c226e73312e7765622d73776565742d7765622e6e6574225d2c226d6170223a7b22223a7b226e73223a5b226e73302e7765622d73776565742d7765622e6e6574222c226e73312e7765622d73776565742d7765622e6e6574225d7d7d7d OP_2DROP OP_2DROP OP_DUP OP_HASH160 f3f4aee9d80da759a4a3547cf6aa95c09881decb OP_EQUALVERIFY OP_CHECKSIG")
19
+ @name_update = Script.from_string("304402206a8598a87aadd697732d0a187220023e2b54e542e144d0dee67660c8ca3d66f4022000de36f02afb9162f2d27e947d452d4e28baadfdb21637db2b1f252ad62d7fb201
20
+ 04b61d1529dbe912c84d0de898e88ab8a9d9fa4a13e76e7b60419dd3bd2b72021842bbf0f5eba1001d55f0c6f2c255289bedbe843d0164b545bbc056f150b4c3f2 3 642f626974636f696e 7b22696e666f223a7b22726567697374726172223a22687474703a2f2f72656769737465722e646f742d6269742e6f7267227d2c22656d61696c223a2022726567697374657240646f742d6269742e6f7267222c226e73223a5b226e73302e7765622d73776565742d7765622e6e6574222c226e73312e7765622d73776565742d7765622e6e6574225d2c226d6170223a7b22223a7b226e73223a5b226e73302e7765622d73776565742d7765622e6e6574222c226e73312e7765622d73776565742d7765622e6e6574225d7d7d7d OP_2DROP OP_DROP OP_DUP OP_HASH160 8f29c40b89ceda0b9176819e2bb5a15f592c6548 OP_EQUALVERIFY OP_CHECKSIG")
21
+ end
22
+
23
+ it 'should parse name_new script' do
24
+ @name_new.is_name_new?.should == true
25
+ @name_firstupdate.is_name_new?.should == false
26
+ @name_update.is_name_new?.should == false
27
+ end
28
+
29
+ it 'should parse name_firstupdate script' do
30
+ @name_new.is_name_firstupdate?.should == false
31
+ @name_firstupdate.is_name_firstupdate?.should == true
32
+ @name_update.is_name_firstupdate?.should == false
33
+ end
34
+
35
+ it 'should parse name_update script' do
36
+ @name_new.is_name_update?.should == false
37
+ @name_firstupdate.is_name_update?.should == false
38
+ @name_update.is_name_update?.should == true
39
+ end
40
+
41
+ it 'should run scripts' do
42
+ @name_new.run { true }.should == true
43
+ @name_firstupdate.run { true }.should == true
44
+ @name_update.run { true }.should == true
45
+ end
46
+
47
+ it 'should get name_hash' do
48
+ @name_new.get_namecoin_hash.should == "820fa9c6d252d6773e4ef26a2feffa93f0237641"
49
+ end
50
+
51
+ it 'should get name' do
52
+ @name_firstupdate.get_namecoin_name.should == "d/bitcoin"
53
+ @name_update.get_namecoin_name.should == "d/bitcoin"
54
+ end
55
+
56
+ it 'should get value' do
57
+ @name_firstupdate.get_namecoin_value.should == '{"info":{"registrar":"http://register.dot-bit.org"},"email": "register@dot-bit.org","ns":["ns0.web-sweet-web.net","ns1.web-sweet-web.net"],"map":{"":{"ns":["ns0.web-sweet-web.net","ns1.web-sweet-web.net"]}}}'
58
+ @name_update.get_namecoin_value.should == @name_firstupdate.get_namecoin_value
59
+ end
60
+
61
+ def set_rand rand; @rand = rand; end
62
+
63
+ it 'should create scripts' do
64
+ key = Key.generate
65
+ script = Script.to_name_new_script(self, "test/foo", key.addr)
66
+ Script.new(script).to_string.should =~
67
+ /^1 (.*?) OP_2DROP OP_DUP OP_HASH160 #{key.hash160} OP_EQUALVERIFY OP_CHECKSIG$/
68
+ @rand.should != nil
69
+
70
+ script = Script.to_name_firstupdate_script("test/foo", "1234", "testing", key.addr)
71
+ Script.new(script).to_string.should ==
72
+ "2 746573742f666f6f 1234 74657374696e67 OP_2DROP OP_2DROP " +
73
+ "OP_DUP OP_HASH160 #{key.hash160} OP_EQUALVERIFY OP_CHECKSIG"
74
+
75
+ script = Script.to_name_update_script("test/foo", "more testing", key.addr)
76
+ Script.new(script).to_string.should ==
77
+ "3 746573742f666f6f 6d6f72652074657374696e67 OP_2DROP OP_DROP " +
78
+ "OP_DUP OP_HASH160 #{key.hash160} OP_EQUALVERIFY OP_CHECKSIG"
79
+ end
80
+
81
+ end
82
+
83
+ [
84
+ { :name => :utxo, :db => 'sqlite:/', utxo_cache: 0 },
85
+ { :name => :sequel, :db => 'sqlite:/' },
86
+ ].each do |configuration|
87
+
88
+ describe "Namecoin (#{configuration[:name]} store)" do
89
+
90
+ before do
91
+ Bitcoin.network = :namecoin
92
+ class Bitcoin::Validation::Block; def difficulty; true; end; end
93
+ class Bitcoin::Validation::Block; def min_timestamp; true; end; end
94
+ Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
95
+ [:name_new, :name_firstupdate, :name_update].each {|type|
96
+ Bitcoin::Storage::Backends::SequelStore::SCRIPT_TYPES << type }
97
+ @store = Bitcoin::Storage.send(configuration[:name], configuration)
98
+ @store.reset
99
+ @store.log.level = :error
100
+ @key = Bitcoin::Key.generate
101
+ @block = create_block "00"*32, false, [], @key
102
+ Bitcoin.network[:genesis_hash] = @block.hash
103
+ @store.store_block(@block)
104
+ end
105
+
106
+ def set_rand r; @rand = r; end
107
+
108
+ it "should store names" do
109
+ # create name_new
110
+ @block = create_block @block.hash, true, [->(t) {
111
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @key }
112
+ t.output {|o| o.value 50e8; o.script {|s| s.type(:name_new)
113
+ s.recipient(self, "test", @key.addr) } } }], @key
114
+ @store.db[:names][hash: Bitcoin.hash160(@rand + "test".hth)].should != nil
115
+
116
+ # name_firstupdate should not be valid yet
117
+ @block = create_block @block.hash, true, [->(t) {
118
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @key }
119
+ t.output {|o| o.value 50e8; o.script {|s| s.type(:name_firstupdate)
120
+ s.recipient("test", @rand, "testvalue", @key.addr) } } }], @key
121
+ @store.name_show("test").should == nil
122
+
123
+ # create enough blocks for name_new to become valid
124
+ Namecoin::FIRSTUPDATE_LIMIT.times {
125
+ @block = create_block @block.hash, true, [], @key }
126
+
127
+ # name_firstupdate should be valid now
128
+ @block = create_block @block.hash, true, [->(t) {
129
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @key }
130
+ t.output {|o| o.value 50e8; o.script {|s|; s.type(:name_firstupdate)
131
+ s.recipient("test", @rand, "testvalue", @key.addr) } } }], @key
132
+
133
+ name = @store.name_show("test")
134
+ name.get_address.should == @key.addr
135
+ name.name.should == "test"
136
+ name.value.should == "testvalue"
137
+ name.hash.should == Bitcoin.hash160(@rand + "test".hth)
138
+
139
+ # create name_update
140
+ @new_key = Bitcoin::Key.generate
141
+ @block = create_block @block.hash, true, [->(t) {
142
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @key}
143
+ t.output {|o|o.value 50e8; o.script {|s| s.type(:name_update)
144
+ s.recipient("test", "testupdate", @new_key.addr) } } }], @new_key
145
+
146
+ name = @store.name_show("test")
147
+ name.get_address.should == @new_key.addr
148
+ name.value.should == "testupdate"
149
+
150
+ h = @store.name_history("test")
151
+ h.size.should == 2
152
+ h[0].value.should == "testvalue"
153
+ h[0].get_address.should == @key.addr
154
+ h[1].value.should == "testupdate"
155
+ h[1].get_address.should == @new_key.addr
156
+ end
157
+
158
+ it "should expire names" do
159
+ @block = create_block @block.hash, true, [->(t) {
160
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @key }
161
+ t.output {|o| o.value 50e8; o.script {|s| s.type(:name_new)
162
+ s.recipient(self, "test", @key.addr) } } }], @key
163
+ @store.db[:names][hash: Bitcoin.hash160(@rand + "test".hth)].should != nil
164
+
165
+ # create enough blocks for name_new to become valid
166
+ Namecoin::FIRSTUPDATE_LIMIT.times {
167
+ @block = create_block @block.hash, true, [], @key }
168
+
169
+ # name_firstupdate should be valid now
170
+ @block = create_block @block.hash, true, [->(t) {
171
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @key }
172
+ t.output {|o| o.value 50e8; o.script {|s|; s.type(:name_firstupdate)
173
+ s.recipient("test", @rand, "testvalue", @key.addr) } } }], @key
174
+
175
+ @store.name_show("test").expires_in.should == Namecoin::EXPIRATION_DEPTH
176
+ @block = create_block @block.hash, true, [], @key
177
+ @store.name_show("test").expires_in.should == Namecoin::EXPIRATION_DEPTH - 1
178
+ end
179
+
180
+ end
181
+ end
182
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require_relative 'spec_helper.rb'
2
4
 
3
5
  describe 'Bitcoin::network' do
@@ -9,19 +11,19 @@ describe 'Bitcoin::network' do
9
11
  net[:address_version].should == "00"
10
12
  net[:genesis_hash].should == "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
11
13
  end
12
-
14
+
13
15
  it 'can be set to main net' do
14
16
  Bitcoin::network = :bitcoin
15
17
  Bitcoin::network.should == Bitcoin::NETWORKS[:bitcoin]
16
18
  end
17
-
19
+
18
20
  class Test_Handler
19
21
  attr_reader :inv
20
22
  def on_inv_transaction inv
21
23
  @inv = inv
22
24
  end
23
25
  end
24
-
26
+
25
27
  it 'uses correct magic_head when parsing a message' do
26
28
  pkt = ["f9 be b4 d9 69 6e 76 00 00 00 00 00 00 00 00 00 49 00 00 00 11 ea 1c 91 02 01 00 00 00 e0 41 c2 38 f7 32 1a 68 0a 34 06 bf fd 72 12 e3 d1 2c b5 12 2a 8c 0b 52 76 de 82 30 b1 00 7a 42 01 00 00 00 33 00 09 71 a9 70 7b 6c 6d 6e 77 aa 2e ac 43 f3 e5 67 84 cb 61 b2 35 fb 8d fe e0 86 8b 40 7c f3".split(" ").join].pack("H*")
27
29
 
@@ -0,0 +1,376 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ include Bitcoin
4
+ include Builder
5
+
6
+ describe 'Node Command API' do
7
+
8
+ def test_command command, params = [], response = nil, &block
9
+ $responses = {}
10
+ EM.run do
11
+ @client = Bitcoin::Network::CommandClient.connect(*@config[:command]) do
12
+ on_connected do
13
+ request(command, *params)
14
+ end
15
+ on_response do |cmd, data|
16
+ $responses[cmd] = data
17
+ EM.stop
18
+ end
19
+ end
20
+ end
21
+
22
+ result = $responses[command]
23
+
24
+ return result unless response || block
25
+
26
+ if block
27
+ block.call(result)
28
+ else
29
+ result.should == response
30
+ end
31
+ end
32
+
33
+
34
+ before do
35
+ Bitcoin::Validation::Block::RULES.merge({
36
+ syntax: [:hash, :tx_list, :bits, :max_timestamp, :coinbase, :coinbase_scriptsig, :transactions_syntax],
37
+ context: [:prev_hash, :coinbase_value, :min_timestamp, :transactions_context]
38
+ })
39
+
40
+ Bitcoin.network = :spec
41
+ @config = {
42
+ listen: ["127.0.0.1", 38333],
43
+ command: ["127.0.0.1", 38332],
44
+ storage: "sequel::sqlite:/",
45
+ dns: false,
46
+ intervals: { queue: 0.01 },
47
+ log: { network: :warn, storage: :warn },
48
+ }
49
+
50
+ @node = Bitcoin::Network::Node.new(@config)
51
+ @pid = fork do
52
+ $stdout = StringIO.new
53
+ SimpleCov.running = false if defined?(SimpleCov)
54
+ @node.run
55
+ end
56
+
57
+ @genesis = P::Block.new("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".htb)
58
+
59
+ Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
60
+ @key = Bitcoin::Key.generate
61
+ @block = create_block @genesis.hash, false, [], @key
62
+
63
+ test_command "store_block", [@genesis.to_payload.hth]
64
+ sleep 0.1
65
+ end
66
+
67
+ after do
68
+ Process.kill("TERM", @pid)
69
+ end
70
+
71
+ it "should return error for unknown command" do
72
+ test_command("foo").should == {"error" => "unknown command: foo. send 'help' for help."}
73
+ end
74
+
75
+ it "should return error for wrong parameters" do
76
+ test_command("info", "foo").should == {"error" => "wrong number of arguments (1 for 0)"}
77
+ end
78
+
79
+ it "should query tslb" do
80
+ test_command("tslb") do |res|
81
+ res.keys.include?("tslb").should == true
82
+ res["tslb"].should >= 0
83
+ res["tslb"].should <= 1
84
+ end
85
+ end
86
+
87
+ it "should query info" do
88
+ info = test_command "info"
89
+ info.is_a?(Hash).should == true
90
+ info["blocks"].should == "0 (?)"
91
+ info["addrs"].should == "0 (0)"
92
+ info["connections"].should == "0 established (0 out, 0 in), 0 connecting"
93
+ info["queue"].should == 0
94
+ info["inv_queue"].should == 0
95
+ info["inv_cache"].should == 0
96
+ info["network"].should == "bitcoin"
97
+ info["storage"].should == "sequel::sqlite:/"
98
+ info["version"].should == 70001
99
+ info["external_ip"].should == "127.0.0.1"
100
+ info["uptime"].should =~ /00:00:00:0[0|1]/
101
+ end
102
+
103
+ it "should query config" do
104
+ test_command("config").should == JSON.load(@node.config.to_json)
105
+ end
106
+
107
+ # TODO
108
+ it "should query connections" do
109
+ test_command("connections").should == []
110
+ end
111
+
112
+ # TODO
113
+ it "should connect" do
114
+ test_command("connect", ["127.0.0.1:1234"])["state"].should == "Connecting..."
115
+ end
116
+
117
+ # TODO
118
+ it "should disconnect" do
119
+ test_command("disconnect", ["127.0.0.1:1234"])["state"].should == "Disconnected"
120
+ end
121
+
122
+ it "should store block" do
123
+ test_command("info")["blocks"].should == "0 (?)"
124
+ res = test_command "store_block", [ @block.to_payload.hth ]
125
+ res.should == { "queued" => [ "block", @block.hash ] }
126
+ sleep 0.1
127
+ test_command("info")["blocks"].should == "1 (?) sync"
128
+ end
129
+
130
+ describe :create_tx do
131
+
132
+ before do
133
+ @key2 = Key.generate
134
+ test_command("store_block", [@block.to_payload.hth])
135
+ sleep 0.1
136
+ end
137
+
138
+ it "should create transaction from given private keys" do
139
+ res = test_command("create_tx", [[@key.to_base58], [[@key2.addr, 10e8], [@key.addr, 40e8]]])
140
+ tx = P::Tx.new(res[0].htb)
141
+ tx.is_a?(P::Tx).should == true
142
+ tx.verify_input_signature(0, @block.tx[0]).should == true
143
+ end
144
+
145
+ it "should create transaction from given addresses" do
146
+ res = test_command("create_tx", [[@key.addr], [[@key2.addr, 10e8], [@key.addr, 40e8]]])
147
+ tx = P::Tx.new(res[0].htb)
148
+ tx.is_a?(P::Tx).should == true
149
+ tx.in[0].script_sig.should == ""
150
+ -> { tx.verify_input_signature(0, @block.tx[0]) }.should.raise(TypeError)
151
+
152
+ res[1].each.with_index do |sig_data, idx|
153
+ sig_hash, sig_addr = *sig_data
154
+ sig_addr.should == @key.addr
155
+ sig = @key.sign(sig_hash.htb)
156
+ script_sig = Script.to_signature_pubkey_script(sig, @key.pub.htb)
157
+ tx.in[idx].script_sig_length = script_sig.bytesize
158
+ tx.in[idx].script_sig = script_sig
159
+ end
160
+
161
+ tx.verify_input_signature(0, @block.tx[0]).should == true
162
+ end
163
+
164
+ it "should create transaction from given pubkeys" do
165
+ res = test_command("create_tx", [[@key.pub], [[@key2.addr, 10e8], [@key.addr, 40e8]]])
166
+ tx = P::Tx.new(res[0].htb)
167
+ tx.is_a?(P::Tx).should == true
168
+ -> { tx.verify_input_signature(0, @block.tx[0]) }.should.raise(TypeError)
169
+
170
+ res[1].each.with_index do |sig_data, idx|
171
+ sig_hash, sig_addr = *sig_data
172
+ sig_addr.should == @key.addr
173
+ sig = @key.sign(sig_hash.htb)
174
+ script_sig = Script.to_signature_pubkey_script(sig, @key.pub.htb)
175
+ tx.in[idx].script_sig_length = script_sig.bytesize
176
+ tx.in[idx].script_sig = script_sig
177
+ end
178
+
179
+ tx.verify_input_signature(0, @block.tx[0]).should == true
180
+ end
181
+
182
+ end
183
+
184
+ describe :assemble_tx do
185
+
186
+ it "should assemble tx from unsigned tx structure, signatures and pubkeys" do
187
+ tx = build_tx do |t|
188
+ t.input do |i|
189
+ i.prev_out @block.tx[0]
190
+ i.prev_out_index 0
191
+ end
192
+ t.output {|o| o.value 50e8; o.script {|s| s.recipient @key.addr } }
193
+ end
194
+ sig = @key.sign(tx.in[0].sig_hash)
195
+ test_command("store_block", [@block.to_payload.hth])
196
+ sleep 0.1
197
+ res = test_command("assemble_tx", [tx.to_payload.hth, [[sig.hth, @key.pub]]])
198
+ tx = Bitcoin::P::Tx.new(res.htb)
199
+ tx.verify_input_signature(0, @block.tx[0]).should == true
200
+ end
201
+
202
+ end
203
+
204
+ describe :relay_tx do
205
+
206
+ it "should handle decoding error" do
207
+ res = test_command("relay_tx", ["foobar"])
208
+ res["error"].should == "Error decoding transaction."
209
+ end
210
+
211
+ it "should handle syntax error" do
212
+ # create transaction with invalid output size
213
+ block = create_block(@block.hash, false, [->(t) {
214
+ create_tx(t, @block.tx[0], 0, [[22e14, @key]]) }], @key)
215
+ tx = block.tx[1]
216
+
217
+ error = test_command("relay_tx", [tx.to_payload.hth])
218
+ error["error"].should == "Transaction syntax invalid."
219
+ error["details"].should == ["output_values", [22e14, 21e14]]
220
+ end
221
+
222
+ it "should handle context error" do
223
+ # create transaction with invalid input
224
+ block = create_block(@block.hash, false, [->(t) {
225
+ create_tx(t, @block.tx[0], 0, [[25e8, @key]]) }], @key)
226
+ tx = block.tx[1]
227
+
228
+ error = test_command("relay_tx", [tx.to_payload.hth])
229
+ error["error"].should == "Transaction context invalid."
230
+ error["details"].should == ["prev_out", [[@block.tx[0].hash, 0]]]
231
+ end
232
+
233
+ it "should relay transaction" do
234
+ block = create_block(@block.hash, false, [->(t) {
235
+ create_tx(t, @block.tx[0], 0, [[25e8, @key]]) }], @key)
236
+ tx = block.tx[1]
237
+
238
+ test_command("store_block", [@block.to_payload.hth])
239
+ sleep 0.1
240
+ res = test_command("relay_tx", [tx.to_payload.hth, 1, 0])
241
+ res["success"].should == true
242
+ res["hash"].should == tx.hash
243
+ res["propagation"].should == { "sent" => 1, "received" => 0, "percent" => 0.0 }
244
+ end
245
+
246
+ end
247
+
248
+ describe :monitor do
249
+
250
+ before do
251
+ @client = TCPSocket.new(*@config[:command])
252
+
253
+ def send data
254
+ @client.write(data.to_json + "\x00")
255
+ end
256
+
257
+ def should_receive expected
258
+ buf = ""
259
+ while b = @client.read(1)
260
+ break if b == "\x00"
261
+ buf << b
262
+ end
263
+ resp = JSON.load(buf)
264
+ resp.should == expected
265
+ end
266
+
267
+ def store_block block
268
+ send ["store_block", [ block.to_payload.hth ]]
269
+ should_receive ["store_block", {"queued" => [ "block", block.hash ]}]
270
+ end
271
+
272
+ end
273
+
274
+ describe :block do
275
+
276
+ before do
277
+ send ["monitor", ["block"]]
278
+ should_receive ["monitor", ["block", [ @genesis.to_hash, 0 ]]]
279
+ store_block @block
280
+ should_receive ["monitor", ["block", [ @block.to_hash, 1 ]]]
281
+ end
282
+
283
+ it "should monitor block" do
284
+ @block = create_block @block.hash, false
285
+ store_block @block
286
+ should_receive ["monitor", ["block", [ @block.to_hash, 2 ]]]
287
+ end
288
+
289
+
290
+ it "should not monitor side or orphan blocks" do
291
+ @side = create_block @genesis.hash, false
292
+ store_block @side
293
+
294
+ @orphan = create_block "00" * 32, false
295
+ store_block @orphan
296
+
297
+ # should not send side or orphan block only the next main block
298
+ @block = create_block @block.hash, false
299
+ store_block @block
300
+ should_receive ["monitor", ["block", [ @block.to_hash, 2 ]]]
301
+ end
302
+
303
+ end
304
+
305
+ describe :tx do
306
+
307
+ it "should monitor unconfirmed tx" do
308
+ send ["monitor", ["tx"]]
309
+ tx = @block.tx[0]
310
+ send ["store_tx", [ tx.to_payload.hth ] ]
311
+ should_receive ["store_tx", { "queued" => [ "tx", tx.hash ]}]
312
+ should_receive ["monitor", ["tx", [ tx.to_hash, 0 ]]]
313
+ end
314
+
315
+ it "should monitor confirmed tx" do
316
+ send ["monitor", ["tx_1"]]
317
+ store_block @block
318
+ should_receive ["monitor", ["tx_1", [ @block.tx[0].to_hash, 1 ]]]
319
+ end
320
+
321
+ it "should monitor tx for given confirmation level" do
322
+ send ["monitor", ["tx_3"]]
323
+ @tx = @block.tx[0]
324
+ store_block @block
325
+ @block = create_block @block.hash, false
326
+ store_block @block
327
+ should_receive ["monitor", ["tx_3", [ @genesis.tx[0].to_hash, 3 ]]]
328
+ @block = create_block @block.hash, false
329
+ store_block @block
330
+ should_receive ["monitor", ["tx_3", [ @tx.to_hash, 3 ]]]
331
+ end
332
+
333
+ end
334
+
335
+ describe :output do
336
+
337
+ before do
338
+ @tx = @block.tx[0]; @out = @tx.out[0]
339
+ @addr = Bitcoin::Script.new(@out.pk_script).get_address
340
+ end
341
+
342
+ it "should monitor unconfirmed outputs" do
343
+ send ["monitor", ["output"]]
344
+ tx = @block.tx[0]
345
+ send ["store_tx", [ tx.to_payload.hth ]]
346
+ should_receive ["store_tx", { "queued" => [ "tx", tx.hash ]}]
347
+ addr = Bitcoin::Script.new(tx.out[0].pk_script).get_address
348
+ should_receive ["monitor", ["output", [ tx.hash, addr, tx.out[0].value, 0]]]
349
+ end
350
+
351
+ it "should monitor confirmed output" do
352
+ send ["monitor", ["output_1"]]
353
+ store_block @block
354
+ should_receive ["monitor", ["output_1", [ @tx.hash, @addr, @out.value, 1 ]]]
355
+ end
356
+
357
+ it "should monitor output for given confirmation level" do
358
+ send ["monitor", ["output_3"]]
359
+ store_block @block
360
+ @block = create_block @block.hash, false
361
+ store_block @block
362
+ tx = @genesis.tx[0]; out = tx.out[0]
363
+ addr = Bitcoin::Script.new(out.pk_script).get_address
364
+ should_receive ["monitor", ["output_3", [ tx.hash, addr, out.value, 3 ]]]
365
+
366
+ @block = create_block @block.hash, false
367
+ store_block @block
368
+ should_receive ["monitor", ["output_3", [ @tx.hash, @addr, @out.value, 3 ]]]
369
+
370
+ end
371
+
372
+ end
373
+
374
+ end
375
+
376
+ end