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