bitcoin-ruby 0.0.1 → 0.0.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.
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