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
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require_relative '../spec_helper.rb'
2
4
  require 'bitcoin/script'
3
5
 
@@ -24,10 +26,70 @@ describe 'Bitcoin::Script' do
24
26
  Script.new(SCRIPT[1]).to_string.should ==
25
27
  "304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"
26
28
 
27
- Script.new([123].pack("C")).to_string.should == "(opcode 123)"
28
- Script.new([176].pack("C")).to_string.should == "OP_EVAL"
29
-
29
+ #Script.new([123].pack("C")).to_string.should == "(opcode 123)"
30
+ Script.new([176].pack("C")).to_string.should == "OP_NOP1"
30
31
  Script.from_string("1 OP_DROP 2").to_string.should == "1 OP_DROP 2"
32
+
33
+ Script.from_string("4b").to_string.should == "4b"
34
+ Script.from_string("4b").to_payload.should == "\x01\x4b"
35
+ Script.from_string("ff").to_string.should == "ff"
36
+ Script.from_string("ff").to_payload.should == "\x01\xff"
37
+ Script.from_string("ffff").to_string.should == "ffff"
38
+
39
+ Script.from_string( "ff"*(Script::OP_PUSHDATA1-1) ).to_payload[0] .should == [Script::OP_PUSHDATA1-1].pack("C*")
40
+ Script.from_string( "ff"*Script::OP_PUSHDATA1 ).to_payload[0..1].should == [Script::OP_PUSHDATA1, Script::OP_PUSHDATA1].pack("C*")
41
+ Script.from_string( "ff"*(Script::OP_PUSHDATA1+1) ).to_payload[0..1].should == [Script::OP_PUSHDATA1, Script::OP_PUSHDATA1+1].pack("C*")
42
+ Script.from_string( "ff"*0xff ).to_payload[0..1].should == [Script::OP_PUSHDATA1, 0xff].pack("C*")
43
+ Script.from_string( "ff"*(0xff+1) ).to_payload[0..2].should == [Script::OP_PUSHDATA2, 0x00, 0x01].pack("C*")
44
+ Script.from_string( "ff"*0xffff ).to_payload[0..2].should == [Script::OP_PUSHDATA2, 0xff, 0xff].pack("C*")
45
+ Script.from_string( "ff"*(0xffff+1) ).to_payload[0..4].should == [Script::OP_PUSHDATA4, 0x00, 0x00, 0x01, 0x00].pack("C*")
46
+
47
+ Script.from_string("16").to_string.should == "16"
48
+ Script::OP_2_16.include?(Script.from_string("16").chunks.first).should == true
49
+ Script.from_string("16").to_payload.should == "\x60"
50
+ Script.new("\x60").to_string.should == "16"
51
+
52
+ Script.from_string("0:1:16").to_string.should == "0:1:16"
53
+ Script::OP_2_16.include?(Script.from_string("0:1:16").chunks.first).should == false
54
+ Script.from_string("0:1:16").to_payload.should == "\x01\x16"
55
+ Script.new("\x01\x16").to_string.should == "0:1:16"
56
+
57
+ Script.new("\x4d\x01\x00\x02").to_string.should == "77:1:02"
58
+ Script.from_string("77:1:02").to_payload.should == "\x4d\x01\x00\x02"
59
+ Script.from_string("77:1:01").to_string.should == "77:1:01"
60
+ Script.from_string("77:2:0101").to_string.should == "77:2:0101"
61
+ Script.from_string("78:1:01").to_string.should == "78:1:01"
62
+ Script.from_string("78:2:0101").to_string.should == "78:2:0101"
63
+ Script.new("\x4e\x01\x00\x00\x00\x02").to_string.should == "78:1:02"
64
+ Script.from_string("78:1:02").to_payload.should == "\x4e\x01\x00\x00\x00\x02"
65
+
66
+ Script.new("\x4d\x01\x00").to_string.should == "77:1:"
67
+ Script.from_string("77:1:").to_payload.should == "\x4d\x01\x00"
68
+
69
+ [ # mainnet tx: ebc9fa1196a59e192352d76c0f6e73167046b9d37b8302b6bb6968dfd279b767 outputs
70
+ ["\x01", "238:1:01", true],
71
+ ["\x02\x01", "238:2:0201", true],
72
+ ["L", "238:1:4c", true],
73
+ ["L\x02\x01", "76:2:01", nil],
74
+ ["M", "238:1:4d", true],
75
+ ["M\xff\xff\x01", "238:4:4dffff01", true],
76
+ ["N", "238:1:4e", true],
77
+ ["N\xff\xff\xff\xff\x01", "238:6:4effffffff01", true],
78
+ ].each{|payload,string,parse_invalid|
79
+ Script.new(payload).to_string.should == string
80
+ Script.new(payload).instance_eval{ @parse_invalid }.should == parse_invalid
81
+ Script.from_string(string).to_payload == payload
82
+ }
83
+
84
+ Bitcoin::Script.from_string("(opcode-230) 4 1 2").to_string.should == "(opcode-230) 4 1 2"
85
+ Bitcoin::Script.from_string("(opcode 230) 4 1 2").to_string.should == "(opcode-230) 4 1 2"
86
+ Bitcoin::Script.from_string("(opcode-65449) 4 1 2").to_string.should == "(opcode-255) OP_HASH160 4 1 2"
87
+
88
+ # found in testnet3 block 0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab transactions
89
+ Script.new("\xff\xff\xff\xff").to_string.should == "(opcode-255) (opcode-255) (opcode-255) (opcode-255)"
90
+ Script.from_string(Script.new("\xff\xff\xff\xff").to_string).raw.should == "\xFF\xFF\xFF\xFF"
91
+ Script.new("\xff\xff\xff").to_string.should == "(opcode-255) (opcode-255) (opcode-255)"
92
+ Script.from_string(Script.new("\xff\xff\xff").to_string).raw.should == "\xFF\xFF\xFF"
31
93
  end
32
94
 
33
95
  it 'Script#binary_from_string' do
@@ -45,8 +107,8 @@ describe 'Bitcoin::Script' do
45
107
 
46
108
  Script.from_string("0 OP_DROP 2 3 4").to_string.should == "0 OP_DROP 2 3 4"
47
109
 
48
- Script.from_string("OP_EVAL").to_string.should == "OP_EVAL"
49
- Script.from_string("OP_NOP1").to_string.should == "OP_EVAL" # test opcodes_alias table
110
+ Script.from_string("OP_EVAL").to_string.should == "OP_NOP1"
111
+ Script.from_string("OP_NOP1").to_string.should == "OP_NOP1" # test opcodes_alias table
50
112
  Script.from_string("OP_NOP").to_string.should == "OP_NOP"
51
113
  Script.from_string("1").to_string.should == "1"
52
114
 
@@ -56,6 +118,9 @@ describe 'Bitcoin::Script' do
56
118
  Bitcoin::Script.binary_from_string(script) == Bitcoin::Script.binary_from_string( Bitcoin::Script.from_string(script).to_string )
57
119
  }.should == true
58
120
 
121
+ #Script.from_string("-100").to_string.should == "OP_NOP"
122
+ #Script.from_string("100").to_string.should == "100"
123
+
59
124
  proc{ Script.from_string("OP_NOP OP_UNKOWN") }.should.raise(Script::ScriptOpcodeError).message.should == "OP_UNKOWN not defined!"
60
125
  end
61
126
  end
@@ -101,6 +166,10 @@ describe 'Bitcoin::Script' do
101
166
  "1B6k6g1d2L975i7beAbiBRxfBWhxomPxvy"]
102
167
  Script.new(SCRIPT[4]).get_multisig_addresses.should == [
103
168
  "1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5", "1EE7JGimkV7QqyHwXDJvk3b1yEN4ZUWeqx"]
169
+
170
+ # from tx 274f8be3b7b9b1a220285f5f71f61e2691dd04df9d69bb02a8b3b85f91fb1857, second pubkey has invalid encoding.
171
+ output = "1 0351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da78 00f2b7816db49d55d24df7bdffdbc1e203b424e8cd39f5651ab938e5e4a193569e 2 OP_CHECKMULTISIG"
172
+ Bitcoin::Script.from_string(output).get_multisig_addresses.should == ["1NdB761LmTmrJixxp93nz7pEiCx5cKPW44"]
104
173
  end
105
174
 
106
175
  it "#get_address" do
@@ -109,9 +178,9 @@ describe 'Bitcoin::Script' do
109
178
  Script.new(SCRIPT[1]).get_address.should == nil
110
179
  Script.new(SCRIPT[2]).get_address.should ==
111
180
  "139k1g5rtTsL4aGZbcASH3Fv3fUh9yBEdW"
112
- Script.new(SCRIPT[3]).get_address.should ==
181
+ Script.new(SCRIPT[3]).get_address.should ==
113
182
  "1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj"
114
- Script.new(SCRIPT[4]).get_address.should ==
183
+ Script.new(SCRIPT[4]).get_address.should ==
115
184
  "1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5"
116
185
  end
117
186
 
@@ -198,7 +267,7 @@ describe 'Bitcoin::Script' do
198
267
  it "should generate p2sh script" do
199
268
  address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
200
269
  hash160 = Bitcoin.hash160_from_address address
201
- Script.to_p2sh_script(hash160).should ==
270
+ Script.to_p2sh_script(hash160).should ==
202
271
  Script.from_string("OP_HASH160 #{hash160} OP_EQUAL").raw
203
272
  end
204
273
 
@@ -210,21 +279,38 @@ describe 'Bitcoin::Script' do
210
279
 
211
280
  address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
212
281
  hash160 = Bitcoin.hash160_from_address address
213
- Script.to_p2sh_script(hash160).should ==
282
+ Script.to_p2sh_script(hash160).should ==
214
283
  Script.from_string("OP_HASH160 #{hash160} OP_EQUAL").raw
215
284
  end
216
285
 
217
286
  end
218
287
 
219
288
  describe "generate script sigs" do
289
+ before do
290
+ @sig = '3045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec'.htb
291
+ end
220
292
 
221
293
  it "should generate pubkey script sig" do
222
- sig = ["3045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec"].pack("H*")
223
- pub = ["04bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c"].pack("H*")
224
- Script.to_pubkey_script_sig(sig, pub)
225
- .should == ["483045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec014104bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c"].pack("H*")
294
+ pub = '04bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c'.htb
295
+ expected_script = '483045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec014104bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c'.htb
296
+
297
+ Script.to_pubkey_script_sig(@sig, pub).should == expected_script
226
298
  end
227
299
 
300
+ it "should accept a compressed public key as input" do
301
+ pub = '02bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41'.htb
302
+ expected_script = '483045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec012102bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41'.htb
303
+
304
+ Script.to_pubkey_script_sig(@sig, pub).should == expected_script
305
+ end
306
+ it "should reject an improperly encoding public key" do
307
+ # Not binary encoded, like it's supposed to be.
308
+ pub = '02bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41'
309
+
310
+ lambda {
311
+ Script.to_pubkey_script_sig(@sig, pub)
312
+ }.should.raise
313
+ end
228
314
  end
229
315
 
230
316
  it '#run' do
@@ -241,6 +327,19 @@ describe 'Bitcoin::Script' do
241
327
  .run.should == false
242
328
 
243
329
  Script.from_string("1 OP_DROP 2").run.should == true
330
+
331
+ # testnet3 tx: 5dea81f9d9d2ea6d06ce23ff225d1e240392519017643f75c96fa2e4316d948a
332
+ script = Script.new( ["0063bac0d0e0f0f1f2f3f3f4ff675168"].pack("H*") )
333
+ script.to_string.should == "0 OP_IF (opcode-186) (opcode-192) (opcode-208) (opcode-224) (opcode-240) (opcode-241) (opcode-242) (opcode-243) (opcode-243) (opcode-244) (opcode-255) OP_ELSE 1 OP_ENDIF"
334
+ script.run.should == true
335
+
336
+ # mainnet tx: 61a078472543e9de9247446076320499c108b52307d8d0fafbe53b5c4e32acc4 redeeming output from 5342c96b946ea2c5e497de5dbf7762021f94aba2c8222c17ed28492fdbb4a6d9
337
+ script = Bitcoin::Script.from_string("16cfb9bc7654ef1d7723e5c2722fc0c3d505045e OP_SIZE OP_DUP 1 OP_GREATERTHAN OP_VERIFY OP_NEGATE OP_HASH256 OP_HASH160 OP_SHA256 OP_SHA1 OP_RIPEMD160 OP_EQUAL")
338
+ script.run.should == true
339
+
340
+ # mainnet tx: 340aa9f72206d600b7e89c9137e4d2d77a920723f83e34707ff452121fd48492 redeeming output from f2d72a7bf22e29e3f2dc721afbf0a922860f81db9fc7eb397937f9d7e87cc438
341
+ script = Bitcoin::Script.from_string("027ce87f6f41dd4d7d874b40889f7df6b288f77f OP_DEPTH OP_HASH256 OP_HASH160 OP_SHA256 OP_SHA1 OP_RIPEMD160 OP_EQUAL")
342
+ script.run.should == true
244
343
  end
245
344
 
246
345
  end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  $: << File.expand_path(File.join(File.dirname(__FILE__), '/../../lib'))
2
4
 
3
5
  begin
@@ -23,8 +25,74 @@ def fixtures_file(relative_path)
23
25
  Bitcoin::Protocol.read_binary_file( File.join(basedir, relative_path) )
24
26
  end
25
27
 
28
+ include Bitcoin::Builder
29
+
30
+ # create block for given +prev+ block
31
+ # if +store+ is true, save it to @store
32
+ # accepts an array of +tx+ callbacks
33
+ def create_block prev, store = true, tx = [], key = Bitcoin::Key.new, coinbase_value = 50e8, opts = {}
34
+ opts[:bits] ||= Bitcoin.network[:proof_of_work_limit]
35
+ block = build_block(Bitcoin.decode_compact_bits(opts[:bits])) do |b|
36
+ b.time opts[:time] if opts[:time]
37
+ b.prev_block prev
38
+ b.tx do |t|
39
+ t.input {|i| i.coinbase }
40
+ t.output {|o| o.value coinbase_value; o.script {|s| s.recipient key.addr } }
41
+ end
42
+ tx.each {|cb| b.tx {|t| cb.call(t) } }
43
+ end
44
+ @store.store_block(block) if store
45
+ block
46
+ end
47
+
48
+ # create transaction given builder +tx+
49
+ # +outputs+ is an array of [value, key] pairs
50
+ def create_tx(tx, prev_tx, prev_out_index, outputs)
51
+ tx.input {|i| i.prev_out prev_tx; i.prev_out_index prev_out_index; i.signature_key @key }
52
+ outputs.each do |value, key|
53
+ tx.output {|o| o.value value; o.script {|s| s.recipient key.addr } }
54
+ end
55
+ end
56
+
57
+ # create a chain of +n+ blocks, based on +prev_hash+ block.
58
+ # influence chain properties via options:
59
+ # time: start time all other times are based on
60
+ # interval: time between blocks
61
+ # bits: target bits each block must match
62
+ def create_blocks prev_hash, n, opts = {}
63
+ interval = opts[:interval] || 600
64
+ time = opts[:time] || Time.now.to_i
65
+ bits = opts[:bits] || 553713663
66
+ block = @store.get_block(prev_hash)
67
+ n.times do |i|
68
+ block = create_block block.hash, true, [], @key, 50e8, {
69
+ time: time += interval, bits: bits }
70
+ # block = @store.get_block(block.hash)
71
+ # puts "#{i} #{block.hash[0..8]} #{block.prev_block.reverse_hth[0..8]} #{Time.at(block.time).strftime('%Y-%m-%d %H:%M:%S')} c: #{block.chain} b: #{block.bits} n: #{block.nonce} w: #{block.work}"
72
+ end
73
+ block
74
+ end
75
+
76
+
26
77
  Bitcoin::network = :bitcoin
27
78
 
79
+ Bitcoin::NETWORKS[:spec] = {
80
+ :project => :bitcoin,
81
+ :magic_head => "spec",
82
+ :address_version => "6f",
83
+ :p2sh_version => "c4",
84
+ :privkey_version => "ef",
85
+ :default_port => 48333,
86
+ :protocol_version => 70001,
87
+ :max_money => 21_000_000 * 100_000_000,
88
+ :dns_seeds => [],
89
+ :genesis_hash => "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
90
+ :proof_of_work_limit => 553713663,
91
+ :alert_pubkeys => [],
92
+ :known_nodes => [],
93
+ :checkpoints => {}
94
+ }
95
+
28
96
  begin
29
97
  require 'bacon'
30
98
  rescue LoadError
@@ -0,0 +1,199 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ include Bitcoin::Builder
6
+
7
+
8
+
9
+ [ { :name => :utxo, :db => 'sqlite:/', :index_all_addrs => true },
10
+ { :name => :sequel, :db => 'sqlite:/' } ] .each do |configuration|
11
+
12
+ describe "reorg (#{configuration[:name].capitalize}Store)" do
13
+
14
+ def balance addr
15
+ @store.get_balance(Bitcoin.hash160_from_address(addr))
16
+ end
17
+
18
+ before do
19
+ Bitcoin.network = :testnet
20
+ @store = Bitcoin::Storage.send(configuration[:name], configuration)
21
+ @store.reset
22
+ def @store.in_sync?; true; end
23
+ @store.log.level = :warn
24
+ Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
25
+ @key = Bitcoin::Key.generate
26
+ @block0 = create_block "00"*32, false, [], @key
27
+ Bitcoin.network[:genesis_hash] = @block0.hash
28
+ @store.store_block(@block0)
29
+ @store.get_head.should == @block0
30
+ end
31
+
32
+ it "should retarget" do
33
+ @store.reset
34
+ time = Time.now.to_i - 3000*600
35
+
36
+ Bitcoin::Validation::Block::RETARGET = 10
37
+
38
+ # create genesis block
39
+ block = create_block "00"*32, false, [], @key, 50e8, {time: time}
40
+ Bitcoin.network[:genesis_hash] = block.hash
41
+ @store.store_block(block)
42
+ time += 600
43
+
44
+ # create too fast blocks
45
+ block = create_blocks block.hash, 9, time: time, interval: 10
46
+ time += 90
47
+
48
+ -> { create_blocks block.hash, 1, time: time }
49
+ .should.raise(Bitcoin::Validation::ValidationError).message.should =~ /difficulty/
50
+
51
+ block = create_blocks block.hash, 1, time: time, bits: bits = 541065152
52
+ @store.get_head.should == block
53
+ time += 600
54
+
55
+ # create too slow blocks
56
+ block = create_blocks block.hash, 9, time: time, interval: 6000, bits: bits
57
+ time += 8*6000
58
+ -> { create_blocks block.hash, 1, time: time, bits: bits }
59
+ .should.raise(Bitcoin::Validation::ValidationError).message.should =~ /difficulty/
60
+
61
+ block = create_blocks block.hash, 1, bits: 553713663
62
+ @store.get_head.should == block
63
+ end
64
+
65
+ it "should reorg across a retargetting boundary correctly" do
66
+ @store.reset
67
+ time = Time.now.to_i - 3000*600
68
+
69
+ # create genesis block
70
+ block = create_block "00"*32, false, [], @key, 50e8, {time: time}
71
+ time += 600
72
+ Bitcoin.network[:genesis_hash] = block.hash
73
+ @store.store_block(block)
74
+
75
+ # create first regular block
76
+ split_block = create_blocks block.hash, 1, time: time
77
+ split_time = time + 600
78
+
79
+ # create branch A with target interval
80
+ block_a = create_blocks split_block.hash, 8, time: split_time
81
+ time_a = split_time + 8 * 600
82
+
83
+ # create branch B with faster-than-target interval
84
+ block_b = create_blocks split_block.hash, 8, time: split_time, interval: 60
85
+ time_b = split_time + 8 * 60
86
+
87
+ # create 2 blocks for branch A with regular difficulty
88
+ block_a = create_blocks block_a.hash, 2, time: time_a
89
+
90
+ # create 1 block for branch B at higher difficulty
91
+ block_b = create_blocks block_b.hash, 1, time: time_b, bits: 541568460
92
+
93
+ # check that shorter branch B has overtaken longer branch A due to more work
94
+ @store.get_head.hash.should == block_b.hash
95
+ end
96
+
97
+ it "should reorg a single side block" do
98
+ @store.get_head.should == @block0
99
+
100
+ block1 = create_block @block0.hash
101
+ @store.get_head.should == block1
102
+
103
+ block2_0 = create_block block1.hash
104
+ @store.get_head.should == block2_0
105
+
106
+ block2_1 = create_block block1.hash
107
+ @store.get_head.should == block2_0
108
+
109
+ block3 = create_block block2_1.hash
110
+ @store.get_head.should == block3
111
+ @store.get_block_by_depth(2).hash.should == block2_1.hash
112
+ end
113
+
114
+ it "should reorg two side blocks" do
115
+ block1 = create_block @block0.hash
116
+ @store.get_head.should == block1
117
+
118
+ block2_0 = create_block block1.hash
119
+ @store.get_head.should == block2_0
120
+
121
+ block2_1 = create_block block1.hash
122
+ @store.get_head.should == block2_0
123
+
124
+ block3_1 = create_block block2_1.hash
125
+ @store.get_head.should == block3_1
126
+
127
+ block3_0 = create_block block2_0.hash
128
+ @store.get_head.should == block3_1
129
+
130
+ block4 = create_block block3_0.hash
131
+ @store.get_head.should == block4
132
+ end
133
+
134
+ it "should reconnect orphans" do
135
+ next(true.should == true) if @store.class.name =~ /Utxo/
136
+ blocks = [@block0]
137
+ 3.times { blocks << create_block(blocks.last.hash, false) }
138
+
139
+ {
140
+ [0, 1, 2, 3] => [0, 1, 2, 3],
141
+ [0, 1, 3, 2] => [0, 1, 1, 3],
142
+ [0, 3, 2, 1] => [0, 0, 0, 3],
143
+ [0, 3, 1, 2] => [0, 0, 1, 3],
144
+ [0, 2, 3, 1] => [0, 0, 0, 3],
145
+ }.each do |order, result|
146
+ @store.reset
147
+ order.each_with_index do |n, i|
148
+ @store.store_block(blocks[n])
149
+ @store.get_head.should == blocks[result[i]]
150
+ end
151
+ end
152
+
153
+ i = 3; (0..i).to_a.permutation.each do |order|
154
+ @store.reset
155
+ order.each {|n| @store.store_block(blocks[n]) }
156
+ @store.get_head.should == blocks[i]
157
+ end
158
+ end
159
+
160
+ it "should handle existing blocks" do
161
+ Bitcoin.network = :testnet
162
+ blocks = [@block0]
163
+ 3.times { blocks << create_block(blocks.last.hash, false) }
164
+ blocks[1..-1].each.with_index {|b, idx| @store.store_block(b).should == [idx+1, 0] }
165
+ 3.times {|i| @store.store_block(blocks[i]).should == [i] }
166
+ @store.get_head.should == blocks[-1]
167
+ end
168
+
169
+ # see https://bitcointalk.org/index.php?topic=46370.0
170
+ it "should pass reorg unit tests" do
171
+ class Bitcoin::Validation::Block; def difficulty; true; end; end
172
+ Bitcoin.network = :bitcoin
173
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_0_to_4.dat"
174
+ @store.get_depth.should == 4
175
+ @store.get_head.hash.should =~ /000000002f264d65040/
176
+ balance("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").should == 10000000000
177
+ balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 0
178
+ balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 5000000000
179
+ balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 10000000000
180
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_3A.dat"
181
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_4A.dat"
182
+ @store.get_head.hash.should =~ /000000002f264d65040/
183
+ @store.import "./spec/bitcoin/fixtures/reorg/blk_5A.dat"
184
+ @store.get_depth.should == 5
185
+ @store.get_head.hash.should =~ /00000000195f85184e7/
186
+ balance("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").should == 15000000000
187
+ balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 1000000000
188
+ balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 0
189
+ balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 14000000000
190
+ class Bitcoin::Validation::Block
191
+ def difficulty
192
+ return true if Bitcoin.network_name == :testnet3
193
+ block.bits == next_bits_required || [block.bits, next_bits_required]
194
+ end
195
+ end
196
+ end
197
+
198
+ end
199
+ end