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.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- 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 == "
|
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 == "
|
49
|
-
Script.from_string("OP_NOP1").to_string.should == "
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
data/spec/bitcoin/spec_helper.rb
CHANGED
@@ -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
|