bitcoin-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/COPYING +18 -0
- data/Gemfile +4 -0
- data/README.rdoc +189 -0
- data/Rakefile +104 -0
- data/bin/bitcoin_dns_seed +130 -0
- data/bin/bitcoin_gui +80 -0
- data/bin/bitcoin_node +174 -0
- data/bin/bitcoin_shell +12 -0
- data/bin/bitcoin_wallet +323 -0
- data/bitcoin-ruby.gemspec +27 -0
- data/concept-examples/blockchain-pow.rb +151 -0
- data/doc/CONFIG.rdoc +66 -0
- data/doc/EXAMPLES.rdoc +9 -0
- data/doc/NODE.rdoc +35 -0
- data/doc/STORAGE.rdoc +21 -0
- data/doc/WALLET.rdoc +102 -0
- data/examples/balance.rb +60 -0
- data/examples/bbe_verify_tx.rb +55 -0
- data/examples/connect.rb +36 -0
- data/examples/relay_tx.rb +22 -0
- data/examples/verify_tx.rb +57 -0
- data/lib/bitcoin.rb +370 -0
- data/lib/bitcoin/builder.rb +266 -0
- data/lib/bitcoin/config.rb +56 -0
- data/lib/bitcoin/connection.rb +126 -0
- data/lib/bitcoin/ffi/openssl.rb +121 -0
- data/lib/bitcoin/gui/addr_view.rb +42 -0
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
- data/lib/bitcoin/gui/conn_view.rb +36 -0
- data/lib/bitcoin/gui/connection.rb +68 -0
- data/lib/bitcoin/gui/em_gtk.rb +28 -0
- data/lib/bitcoin/gui/gui.builder +1643 -0
- data/lib/bitcoin/gui/gui.rb +290 -0
- data/lib/bitcoin/gui/helpers.rb +113 -0
- data/lib/bitcoin/gui/tree_view.rb +82 -0
- data/lib/bitcoin/gui/tx_view.rb +67 -0
- data/lib/bitcoin/key.rb +125 -0
- data/lib/bitcoin/logger.rb +65 -0
- data/lib/bitcoin/network/command_client.rb +93 -0
- data/lib/bitcoin/network/command_handler.rb +179 -0
- data/lib/bitcoin/network/connection_handler.rb +274 -0
- data/lib/bitcoin/network/node.rb +399 -0
- data/lib/bitcoin/protocol.rb +140 -0
- data/lib/bitcoin/protocol/address.rb +48 -0
- data/lib/bitcoin/protocol/alert.rb +47 -0
- data/lib/bitcoin/protocol/block.rb +154 -0
- data/lib/bitcoin/protocol/handler.rb +38 -0
- data/lib/bitcoin/protocol/parser.rb +148 -0
- data/lib/bitcoin/protocol/tx.rb +205 -0
- data/lib/bitcoin/protocol/txin.rb +97 -0
- data/lib/bitcoin/protocol/txout.rb +73 -0
- data/lib/bitcoin/protocol/version.rb +70 -0
- data/lib/bitcoin/script.rb +634 -0
- data/lib/bitcoin/storage/dummy.rb +164 -0
- data/lib/bitcoin/storage/models.rb +133 -0
- data/lib/bitcoin/storage/sequel.rb +335 -0
- data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
- data/lib/bitcoin/storage/storage.rb +243 -0
- data/lib/bitcoin/version.rb +3 -0
- data/lib/bitcoin/wallet/coinselector.rb +30 -0
- data/lib/bitcoin/wallet/keygenerator.rb +75 -0
- data/lib/bitcoin/wallet/keystore.rb +203 -0
- data/lib/bitcoin/wallet/txdp.rb +116 -0
- data/lib/bitcoin/wallet/wallet.rb +243 -0
- data/spec/bitcoin/bitcoin_spec.rb +472 -0
- data/spec/bitcoin/builder_spec.rb +90 -0
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
- data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
- data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
- data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
- data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
- data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
- data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
- data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
- data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
- data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
- data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
- data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
- data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
- data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
- data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
- data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
- data/spec/bitcoin/key_spec.rb +123 -0
- data/spec/bitcoin/network_spec.rb +48 -0
- data/spec/bitcoin/protocol/addr_spec.rb +68 -0
- data/spec/bitcoin/protocol/alert_spec.rb +20 -0
- data/spec/bitcoin/protocol/block_spec.rb +101 -0
- data/spec/bitcoin/protocol/inv_spec.rb +124 -0
- data/spec/bitcoin/protocol/ping_spec.rb +49 -0
- data/spec/bitcoin/protocol/tx_spec.rb +226 -0
- data/spec/bitcoin/protocol/version_spec.rb +77 -0
- data/spec/bitcoin/reorg_spec.rb +129 -0
- data/spec/bitcoin/script/opcodes_spec.rb +417 -0
- data/spec/bitcoin/script/script_spec.rb +246 -0
- data/spec/bitcoin/spec_helper.rb +36 -0
- data/spec/bitcoin/storage_spec.rb +229 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
- data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
- metadata +295 -0
@@ -0,0 +1,246 @@
|
|
1
|
+
require_relative '../spec_helper.rb'
|
2
|
+
require 'bitcoin/script'
|
3
|
+
|
4
|
+
include Bitcoin
|
5
|
+
|
6
|
+
describe 'Bitcoin::Script' do
|
7
|
+
SCRIPT = [
|
8
|
+
"410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac",
|
9
|
+
"47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901",
|
10
|
+
"76a91417977bca1b6287a5e6559c57ef4b6525e9d7ded688ac",
|
11
|
+
"524104573b6e9f3a714440048a7b87d606bcbf9e45b8586e70a67a3665ea720c095658471a523e5d923f3f3e015626e7c900bd08560ddffeb17d33c5b52c96edb875954104039c2f4e413a26901e67ad4adbb6a4759af87bc16c7120459ecc9482fed3dd4a4502947f7b4c7782dcadc2bed513ed14d5e770452b97ae246ac2030f13b80a5141048b0f9d04e495c3c754f8c3c109196d713d0778882ef098f785570ee6043f8c192d8f84df43ebafbcc168f5d95a074dc4010b62c003e560abc163c312966b74b653ae", # multisig 2 of 3
|
12
|
+
"5141040ee607b584b36e995f2e96dec35457dbb40845d0ce0782c84002134e816a6b8cbc65e9eed047ae05e10760e4113f690fd49ad73b86b04a1d7813d843f8690ace4104220a78f5f6741bb0739675c2cc200643516b02cfdfda5cba21edeaa62c0f954936b30dfd956e3e99af0a8e7665cff6ac5b429c54c418184c81fbcd4bde4088f552ae", # multisig 1 of 2
|
13
|
+
].map{|s|[s].pack("H*")}
|
14
|
+
PUBKEYS = [
|
15
|
+
"04fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2",
|
16
|
+
"0423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a",
|
17
|
+
"04f43e76afac66bf3927638b6c4f7e324513ce56d2d658ac9d24c420d09993a4464eea6141a68a4748c092ad0e8f4ac29c4a2f661ef4d22b21f20110f42fcd6f6d",
|
18
|
+
]
|
19
|
+
describe "serialization" do
|
20
|
+
it '#to_string' do
|
21
|
+
Script.new(SCRIPT[0]).to_string.should ==
|
22
|
+
"0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 OP_CHECKSIG"
|
23
|
+
|
24
|
+
Script.new(SCRIPT[1]).to_string.should ==
|
25
|
+
"304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"
|
26
|
+
|
27
|
+
Script.new([123].pack("C")).to_string.should == "(opcode 123)"
|
28
|
+
Script.new([176].pack("C")).to_string.should == "OP_EVAL"
|
29
|
+
|
30
|
+
Script.from_string("1 OP_DROP 2").to_string.should == "1 OP_DROP 2"
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Script#binary_from_string' do
|
34
|
+
str = Script.new(SCRIPT[0]).to_string
|
35
|
+
Script.binary_from_string(str).unpack("H*")[0].should == SCRIPT[0].unpack("H*")[0]
|
36
|
+
Script.new(Script.binary_from_string(str)).to_string.should == str
|
37
|
+
|
38
|
+
str = Script.new(SCRIPT[1]).to_string
|
39
|
+
Script.binary_from_string(str).unpack("H*")[0].should == SCRIPT[1].unpack("H*")[0]
|
40
|
+
Script.new(Script.binary_from_string(str)).to_string.should == str
|
41
|
+
# TODO make tests for OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4 cases
|
42
|
+
|
43
|
+
string = "2 OP_TOALTSTACK 0 OP_TOALTSTACK OP_TUCK OP_CHECKSIG OP_SWAP OP_HASH160 3cd1def404e12a85ead2b4d3f5f9f817fb0d46ef OP_EQUAL OP_BOOLAND OP_FROMALTSTACK OP_ADD"
|
44
|
+
Script.from_string(string).to_string.should == string
|
45
|
+
|
46
|
+
Script.from_string("0 OP_DROP 2 3 4").to_string.should == "0 OP_DROP 2 3 4"
|
47
|
+
|
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
|
50
|
+
Script.from_string("OP_NOP").to_string.should == "OP_NOP"
|
51
|
+
Script.from_string("1").to_string.should == "1"
|
52
|
+
|
53
|
+
Script.from_string("0 ffff OP_CODESEPARATOR 1 ffff 1 OP_CHECKMULTISIG").to_string.should == "0 ffff OP_CODESEPARATOR 1 ffff 1 OP_CHECKMULTISIG"
|
54
|
+
|
55
|
+
[1,2,4].all?{|n| script = "OP_PUSHDATA#{n} 01 ff"
|
56
|
+
Bitcoin::Script.binary_from_string(script) == Bitcoin::Script.binary_from_string( Bitcoin::Script.from_string(script).to_string )
|
57
|
+
}.should == true
|
58
|
+
|
59
|
+
proc{ Script.from_string("OP_NOP OP_UNKOWN") }.should.raise(Script::ScriptOpcodeError).message.should == "OP_UNKOWN not defined!"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "get keys/addresses" do
|
64
|
+
it '#get_pubkey' do
|
65
|
+
Script.new(SCRIPT[0]).get_pubkey.should ==
|
66
|
+
"0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"
|
67
|
+
end
|
68
|
+
|
69
|
+
it '#get_pubkey_address' do
|
70
|
+
Script.new(SCRIPT[0]).get_pubkey_address.should ==
|
71
|
+
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "#get_hash160" do
|
75
|
+
Script.new(SCRIPT[2]).get_hash160.should ==
|
76
|
+
"17977bca1b6287a5e6559c57ef4b6525e9d7ded6"
|
77
|
+
Script.from_string("OP_DUP OP_HASH160 0 OP_EQUALVERIFY OP_CHECKSIG")
|
78
|
+
.get_hash160.should == nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it "#get_hash160_address" do
|
82
|
+
Script.new(SCRIPT[2]).get_hash160_address.should ==
|
83
|
+
"139k1g5rtTsL4aGZbcASH3Fv3fUh9yBEdW"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "#get_multisig_pubkeys" do
|
87
|
+
Script.new(SCRIPT[3]).get_multisig_pubkeys.should == [
|
88
|
+
"04573b6e9f3a714440048a7b87d606bcbf9e45b8586e70a67a3665ea720c095658471a523e5d923f3f3e015626e7c900bd08560ddffeb17d33c5b52c96edb87595",
|
89
|
+
"04039c2f4e413a26901e67ad4adbb6a4759af87bc16c7120459ecc9482fed3dd4a4502947f7b4c7782dcadc2bed513ed14d5e770452b97ae246ac2030f13b80a51",
|
90
|
+
"048b0f9d04e495c3c754f8c3c109196d713d0778882ef098f785570ee6043f8c192d8f84df43ebafbcc168f5d95a074dc4010b62c003e560abc163c312966b74b6"].map{|pk| [pk].pack("H*")}
|
91
|
+
Script.from_string("3 #{PUBKEYS[0..2].join(' ')} 3 OP_CHECKMULTISIG")
|
92
|
+
.get_multisig_pubkeys.should == [
|
93
|
+
"04fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2",
|
94
|
+
"0423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a",
|
95
|
+
"04f43e76afac66bf3927638b6c4f7e324513ce56d2d658ac9d24c420d09993a4464eea6141a68a4748c092ad0e8f4ac29c4a2f661ef4d22b21f20110f42fcd6f6d"].map{|k|[k].pack("H*")}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "#get_multisig_addresses" do
|
99
|
+
Script.new(SCRIPT[3]).get_multisig_addresses.should == [
|
100
|
+
"1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj", "19Fm2gY7qDTXriNTEhFY2wjxbHna3Gvenk",
|
101
|
+
"1B6k6g1d2L975i7beAbiBRxfBWhxomPxvy"]
|
102
|
+
Script.new(SCRIPT[4]).get_multisig_addresses.should == [
|
103
|
+
"1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5", "1EE7JGimkV7QqyHwXDJvk3b1yEN4ZUWeqx"]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "#get_address" do
|
107
|
+
Script.new(SCRIPT[0]).get_address.should ==
|
108
|
+
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S"
|
109
|
+
Script.new(SCRIPT[1]).get_address.should == nil
|
110
|
+
Script.new(SCRIPT[2]).get_address.should ==
|
111
|
+
"139k1g5rtTsL4aGZbcASH3Fv3fUh9yBEdW"
|
112
|
+
Script.new(SCRIPT[3]).get_address.should ==
|
113
|
+
"1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj"
|
114
|
+
Script.new(SCRIPT[4]).get_address.should ==
|
115
|
+
"1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "#get_addresses" do
|
119
|
+
Script.new(SCRIPT[0]).get_addresses.
|
120
|
+
should == ["12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S"]
|
121
|
+
Script.new(SCRIPT[3]).get_addresses
|
122
|
+
.should == ["1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj",
|
123
|
+
"19Fm2gY7qDTXriNTEhFY2wjxbHna3Gvenk", "1B6k6g1d2L975i7beAbiBRxfBWhxomPxvy"]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "determine type" do
|
128
|
+
|
129
|
+
it '#is_standard?' do
|
130
|
+
Script.new(SCRIPT[0]).is_standard?.should == true
|
131
|
+
Script.new(SCRIPT[1]).is_standard?.should == false
|
132
|
+
Script.new(SCRIPT[2]).is_standard?.should == true
|
133
|
+
Script.new(SCRIPT[3]).is_standard?.should == true
|
134
|
+
Script.new(SCRIPT[4]).is_standard?.should == true
|
135
|
+
end
|
136
|
+
|
137
|
+
it '#is_pubkey?' do
|
138
|
+
Script.new(SCRIPT[0]).is_pubkey?.should == true
|
139
|
+
Script.new(SCRIPT[1]).is_pubkey?.should == false
|
140
|
+
Script.new(SCRIPT[2]).is_pubkey?.should == false
|
141
|
+
Script.new(SCRIPT[3]).is_pubkey?.should == false
|
142
|
+
Script.new(SCRIPT[4]).is_send_to_ip?.should == false
|
143
|
+
end
|
144
|
+
|
145
|
+
it "#is_hash160?" do
|
146
|
+
Script.new(SCRIPT[0]).is_hash160?.should == false
|
147
|
+
Script.new(SCRIPT[1]).is_pubkey?.should == false
|
148
|
+
Script.new(SCRIPT[2]).is_hash160?.should == true
|
149
|
+
Script.from_string("OP_DUP OP_HASH160 0 OP_EQUALVERIFY OP_CHECKSIG")
|
150
|
+
.is_hash160?.should == false
|
151
|
+
end
|
152
|
+
|
153
|
+
it "#is_multisig?" do
|
154
|
+
Script.new(SCRIPT[3]).is_multisig?.should == true
|
155
|
+
Script.new(SCRIPT[4]).is_multisig?.should == true
|
156
|
+
Script.new(SCRIPT[0]).is_multisig?.should == false
|
157
|
+
Script.new("OP_DUP OP_DROP 2 #{PUBKEYS[0..2].join(' ')} 3 OP_CHECKMULTISIG")
|
158
|
+
.is_multisig?.should == false
|
159
|
+
Script.new("OP_DROP OP_CHECKMULTISIG").is_multisig?.should == false
|
160
|
+
end
|
161
|
+
|
162
|
+
it "#type" do
|
163
|
+
Script.new(SCRIPT[0]).type.should == :pubkey
|
164
|
+
Script.new(SCRIPT[1]).type.should == :unknown
|
165
|
+
Script.new(SCRIPT[2]).type.should == :hash160
|
166
|
+
Script.new(SCRIPT[3]).type.should == :multisig
|
167
|
+
Script.new(SCRIPT[4]).type.should == :multisig
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "generate scripts" do
|
173
|
+
|
174
|
+
it "should generate pubkey script" do
|
175
|
+
Script.to_pubkey_script(PUBKEYS[0]).should ==
|
176
|
+
Script.from_string("#{PUBKEYS[0]} OP_CHECKSIG").raw
|
177
|
+
Script.to_pubkey_script(PUBKEYS[1]).should ==
|
178
|
+
Script.from_string("#{PUBKEYS[1]} OP_CHECKSIG").raw
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should generate hash160 script" do
|
182
|
+
Script.to_address_script('16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9')
|
183
|
+
.should == ["76a9143be0c2daaabbf3d53e47352c19d1e8f047e2f94188ac"].pack("H*")
|
184
|
+
hash160 = Bitcoin.hash160_from_address('16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9')
|
185
|
+
Script.to_hash160_script(hash160)
|
186
|
+
.should == Script.from_string("OP_DUP OP_HASH160 #{hash160} OP_EQUALVERIFY OP_CHECKSIG").raw
|
187
|
+
Script.to_address_script('mr1jU3Adw2pkvxTLvQA4MKpXB9Dynj9cXF')
|
188
|
+
.should == nil
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should generate multisig script" do
|
192
|
+
Script.to_multisig_script(2, *PUBKEYS[0..2]).should ==
|
193
|
+
Script.from_string("2 #{PUBKEYS[0..2].join(' ')} 3 OP_CHECKMULTISIG").raw
|
194
|
+
Script.to_multisig_script(1, *PUBKEYS[0..1]).should ==
|
195
|
+
Script.from_string("1 #{PUBKEYS[0..1].join(' ')} 2 OP_CHECKMULTISIG").raw
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should generate p2sh script" do
|
199
|
+
address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
|
200
|
+
hash160 = Bitcoin.hash160_from_address address
|
201
|
+
Script.to_p2sh_script(hash160).should ==
|
202
|
+
Script.from_string("OP_HASH160 #{hash160} OP_EQUAL").raw
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should determine type for address script" do
|
206
|
+
address = '16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9'
|
207
|
+
hash160 = Bitcoin.hash160_from_address address
|
208
|
+
Script.to_address_script(address).should ==
|
209
|
+
Script.from_string("OP_DUP OP_HASH160 #{hash160} OP_EQUALVERIFY OP_CHECKSIG").raw
|
210
|
+
|
211
|
+
address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
|
212
|
+
hash160 = Bitcoin.hash160_from_address address
|
213
|
+
Script.to_p2sh_script(hash160).should ==
|
214
|
+
Script.from_string("OP_HASH160 #{hash160} OP_EQUAL").raw
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "generate script sigs" do
|
220
|
+
|
221
|
+
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*")
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
it '#run' do
|
231
|
+
script = SCRIPT[1] + SCRIPT[0]
|
232
|
+
Script.new(script).run.should == true
|
233
|
+
|
234
|
+
Script.from_string("1 OP_DUP OP_DROP 1 OP_EQUAL")
|
235
|
+
.run.should == true
|
236
|
+
Script.from_string("1 OP_DUP OP_DROP 1 OP_EQUAL")
|
237
|
+
.run.should == true
|
238
|
+
Script.from_string("foo OP_DUP OP_DROP foo OP_EQUAL")
|
239
|
+
.run.should == true
|
240
|
+
Script.from_string("bar foo OP_DUP OP_DROP bar OP_EQUAL")
|
241
|
+
.run.should == false
|
242
|
+
|
243
|
+
Script.from_string("1 OP_DROP 2").run.should == true
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '/../../lib'))
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start do
|
6
|
+
add_group("Bitcoin") do |file|
|
7
|
+
["bitcoin.rb", "opcodes.rb", "script.rb", "key.rb"].include?(file.filename.split("/").last)
|
8
|
+
end
|
9
|
+
add_group "Protocol", "lib/bitcoin/protocol"
|
10
|
+
add_group "Storage", "lib/bitcoin/storage"
|
11
|
+
add_group "Wallet", "lib/bitcoin/wallet"
|
12
|
+
add_group("Utilities") do |file|
|
13
|
+
["logger.rb", "openssl.rb"].include?(file.filename.split("/").last)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'bitcoin'
|
20
|
+
|
21
|
+
def fixtures_file(relative_path)
|
22
|
+
basedir = File.join(File.dirname(__FILE__), 'fixtures')
|
23
|
+
Bitcoin::Protocol.read_binary_file( File.join(basedir, relative_path) )
|
24
|
+
end
|
25
|
+
|
26
|
+
Bitcoin::network = :bitcoin
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'bacon'
|
30
|
+
rescue LoadError
|
31
|
+
puts "Cannot load 'bacon' - install with `gem install bacon`"
|
32
|
+
puts "Note: to run all the tests, you will also need: ffi, sequel, sqlite3"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
Bacon.summary_on_exit
|
36
|
+
require 'minitest/mock'
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
[
|
4
|
+
# { :name => :dummy },
|
5
|
+
{ :name => :sequel, :db => 'sqlite:/' }, # in memory
|
6
|
+
# { :name => :sequel, :db => 'sqlite:///tmp/bitcoin_test.db' },
|
7
|
+
# { :name => :sequel, :db => 'postgres://localhost/bitcoin_test' },
|
8
|
+
].each do |configuration|
|
9
|
+
describe "Bitcoin::Storage::Backends::#{configuration[:name].capitalize}Store" do
|
10
|
+
|
11
|
+
before do
|
12
|
+
Bitcoin::network = :testnet
|
13
|
+
Bitcoin::Storage.log.level = 3
|
14
|
+
@store = Bitcoin::Storage.send(configuration[:name], configuration)
|
15
|
+
@store.reset
|
16
|
+
|
17
|
+
@store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin')))
|
18
|
+
@store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_1.bin')))
|
19
|
+
@store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_2.bin')))
|
20
|
+
@store.store_block(Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_3.bin')))
|
21
|
+
|
22
|
+
@store.store_tx(Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-01.bin')))
|
23
|
+
@store.store_tx(Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-02.bin')))
|
24
|
+
|
25
|
+
|
26
|
+
@blk = Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_4.bin'))
|
27
|
+
@tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-03.bin'))
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should get depth" do
|
31
|
+
@store.get_depth.should == 3
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should report depth as -1 if store is empty" do
|
35
|
+
@store.reset
|
36
|
+
@store.get_depth.should == -1
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should get head" do
|
40
|
+
@store.get_head
|
41
|
+
.should == @store.get_block("0000000098932356a236718829dd9e3eb0f9143317ab921333b1a203de336de4")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should get locator" do
|
45
|
+
@store.get_locator.should == [
|
46
|
+
"0000000098932356a236718829dd9e3eb0f9143317ab921333b1a203de336de4",
|
47
|
+
"000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f",
|
48
|
+
"000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604",
|
49
|
+
"00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not store if there is no prev block" do
|
53
|
+
@store.reset
|
54
|
+
@store.store_block(@blk).should == [0, 2]
|
55
|
+
@store.get_depth.should == -1
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should check whether block is already stored" do
|
59
|
+
@store.has_block(@blk.hash).should == false
|
60
|
+
@store.store_block(@blk)
|
61
|
+
@store.has_block(@blk.hash).should == true
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should get block by depth" do
|
65
|
+
@store.get_block_by_depth(0).to_hash.should ==
|
66
|
+
Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin')).to_hash
|
67
|
+
@store.get_block_by_depth(1).to_hash.should ==
|
68
|
+
Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_1.bin')).to_hash
|
69
|
+
@store.get_block_by_depth(2).to_hash.should ==
|
70
|
+
Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_2.bin')).to_hash
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should get block by hash" do
|
74
|
+
@store.get_block(
|
75
|
+
"00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008").to_hash
|
76
|
+
.should == Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin')).to_hash
|
77
|
+
@store.get_block(
|
78
|
+
"000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604").to_hash
|
79
|
+
.should == Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_1.bin')).to_hash
|
80
|
+
@store.get_block(
|
81
|
+
"000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f").to_hash
|
82
|
+
.should == Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_2.bin')).to_hash
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not get block" do
|
86
|
+
@store.get_block("nonexistant").should == nil
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should get block depth" do
|
90
|
+
@store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
|
91
|
+
.depth.should == 0
|
92
|
+
@store.get_block("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")
|
93
|
+
.depth.should == 1
|
94
|
+
@store.get_block("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f")
|
95
|
+
.depth.should == 2
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should get prev block" do
|
99
|
+
@store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
|
100
|
+
.get_prev_block.should == nil
|
101
|
+
@store.get_block("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")
|
102
|
+
.get_prev_block.should ==
|
103
|
+
@store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should get next block" do
|
107
|
+
@store.get_block("0000000098932356a236718829dd9e3eb0f9143317ab921333b1a203de336de4")
|
108
|
+
.get_next_block.should == nil
|
109
|
+
@store.get_block("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")
|
110
|
+
.get_next_block.should ==
|
111
|
+
@store.get_block("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should get block for tx" do
|
115
|
+
@store.store_block(@blk)
|
116
|
+
@store.get_block_by_tx(@blk.tx[0].hash).should == @blk
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should store tx" do
|
120
|
+
@store.store_tx(@tx).should != false
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should not store tx if already stored and return existing id" do
|
124
|
+
id = @store.store_tx(@tx)
|
125
|
+
@store.store_tx(@tx).should == id
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should check if tx is already stored" do
|
129
|
+
@store.has_tx(@tx.hash).should == false
|
130
|
+
@store.store_tx(@tx)
|
131
|
+
@store.has_tx(@tx.hash).should == true
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should store hash160 for txout" do
|
135
|
+
@store.store_tx(@tx)
|
136
|
+
@store.get_tx(@tx.hash).out[0].hash160
|
137
|
+
.should == "3129d7051d509424d23d533fa2d5258977e822e3"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should get tx" do
|
141
|
+
@store.store_tx(@tx)
|
142
|
+
@store.get_tx(@tx.hash).should == @tx
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not get tx" do
|
146
|
+
@store.get_tx("nonexistant").should == nil
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
it "should get txouts for pk script" do
|
151
|
+
@store.store_block(@blk)
|
152
|
+
script = @blk.tx[0].out[0].pk_script
|
153
|
+
@store.get_txouts_for_pk_script(script)
|
154
|
+
.should == [@blk.tx[0].out[0]]
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should get block for tx" do
|
158
|
+
@store.store_block(@blk)
|
159
|
+
tx = @blk.tx[0]
|
160
|
+
@store.get_tx(tx.hash).get_block.should == @blk
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should get tx for txin" do
|
164
|
+
@store.store_tx(@tx)
|
165
|
+
@store.get_tx(@tx.hash).in[0].get_tx.should == @tx
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should get prev out for txin" do
|
169
|
+
tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin'))
|
170
|
+
outpoint_tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin'))
|
171
|
+
@store.store_tx(outpoint_tx)
|
172
|
+
@store.store_tx(tx)
|
173
|
+
|
174
|
+
@store.get_tx(tx.hash).in[0].get_prev_out.should == outpoint_tx.out[0]
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should get tx for txout" do
|
178
|
+
@store.store_tx(@tx)
|
179
|
+
@store.get_tx(@tx.hash).out[0].get_tx.should == @tx
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should get next in for txin" do
|
183
|
+
tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin'))
|
184
|
+
outpoint_tx = Bitcoin::Protocol::Tx.new(fixtures_file('rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin'))
|
185
|
+
@store.store_tx(outpoint_tx)
|
186
|
+
@store.store_tx(tx)
|
187
|
+
|
188
|
+
@store.get_tx(outpoint_tx.hash).out[0].get_next_in.should == tx.in[0]
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should get txouts for hash160" do
|
192
|
+
@store.store_tx(@tx)
|
193
|
+
@store.get_txouts_for_hash160("3129d7051d509424d23d533fa2d5258977e822e3", true)
|
194
|
+
.should == [@tx.out[0]]
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should get txouts for address" do
|
198
|
+
@store.store_tx(@tx)
|
199
|
+
@store.get_txouts_for_address("mjzuXYR2fncbPzn9nR5Ee5gBgYk9UQx36x", true)
|
200
|
+
.should == [@tx.out[0]]
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should get balance for address" do
|
204
|
+
@store.store_tx(@tx)
|
205
|
+
@store.get_balance("62e907b15cbf27d5425399ebf6f0fb50ebb88f18").should == 5000000000
|
206
|
+
@store.get_balance("4580f1b3632948202655fd555fdaaf9b9ef5ac0d").should == 0
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should store multisig tx and index hash160's" do
|
210
|
+
(true.should==true) && next if @store.class == Bitcoin::Storage::Backends::DummyStore
|
211
|
+
*keys = Bitcoin::Key.generate, Bitcoin::Key.generate
|
212
|
+
pk_script = Bitcoin::Script.to_multisig_script(1, keys[0].pub, keys[1].pub)
|
213
|
+
txout = Bitcoin::Protocol::TxOut.new(1000, pk_script)
|
214
|
+
@store.store_txout(0, txout, 0)
|
215
|
+
keys.each do |key|
|
216
|
+
hash160 = Bitcoin.hash160(key.pub)
|
217
|
+
txouts = @store.get_txouts_for_hash160(hash160, true)
|
218
|
+
txouts.size.should == 1
|
219
|
+
txouts[0].pk_script.should == txout.pk_script
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should index output script type" do
|
224
|
+
@store.store_tx(@tx)
|
225
|
+
@store.get_tx(@tx.hash).out.first.type.should == :hash160
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|