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