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,77 @@
|
|
1
|
+
require_relative '../spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Bitcoin::Protocol::Parser (version)' do
|
4
|
+
|
5
|
+
class Version_Handler < Bitcoin::Protocol::Handler
|
6
|
+
attr_reader :pkt
|
7
|
+
def on_version(pkt)
|
8
|
+
@pkt = pkt
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'parses version packets' do
|
13
|
+
pkt = Bitcoin::Protocol.pkt("version",
|
14
|
+
["60ea00000100000000000000b3c1424f00000000010000000000000000000000000000000000ffff7f000001e1ca010000000000000000000000000000000000ffff7f000001479d9525d0c7b30688ae122f626974636f696e2d71743a302e362e302f82b60000"].pack("H*"))
|
15
|
+
|
16
|
+
parser = Bitcoin::Protocol::Parser.new( handler = Version_Handler.new )
|
17
|
+
parser.parse(pkt + "AAAA").should == "AAAA"
|
18
|
+
|
19
|
+
pkt = handler.pkt
|
20
|
+
pkt.fields.should == {
|
21
|
+
:version => 60000,
|
22
|
+
:services => "\x01\x00\x00\x00\x00\x00\x00\x00",
|
23
|
+
:time => 1329775027,
|
24
|
+
:from => "127.0.0.1:18333",
|
25
|
+
:to => "127.0.0.1:57802",
|
26
|
+
:nonce => 12576309328653329813,
|
27
|
+
:user_agent => "/bitcoin-qt:0.6.0/",
|
28
|
+
:last_block => 46722
|
29
|
+
}
|
30
|
+
|
31
|
+
pkt = [
|
32
|
+
"f9 be b4 d9 76 65 72 73 69 6f 6e 00 00 00 00 00 55 00 00 00 a4 c2 08 cb 40 9c 00 00 01 00 00 00 00 00 00 00 10 42 c9 4e 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff 7f 00 00 01 04 d2 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff 7f 00 00 01 47 9d 4b b8 bb 21 ae d7 f0 71 00 fa 00 00 00"
|
33
|
+
.split(" ").join].pack("H*")
|
34
|
+
|
35
|
+
|
36
|
+
parser = Bitcoin::Protocol::Parser.new( handler = Version_Handler.new )
|
37
|
+
parser.parse(pkt + "AAAA").should == "AAAA"
|
38
|
+
|
39
|
+
pkt = handler.pkt
|
40
|
+
pkt.fields.should == {
|
41
|
+
:version => 40000,
|
42
|
+
:services => "\x01\x00\x00\x00\x00\x00\x00\x00",
|
43
|
+
:time => 1321812496,
|
44
|
+
:from => "127.0.0.1:18333",
|
45
|
+
:to => "127.0.0.1:1234",
|
46
|
+
:nonce => 8210299263586646091,
|
47
|
+
:user_agent => nil,
|
48
|
+
:last_block => 250
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'creates version packets' do
|
53
|
+
version = Bitcoin::Protocol::Version.new({
|
54
|
+
:time => 1337,
|
55
|
+
:from => "127.0.0.1:8333",
|
56
|
+
:to => "127.0.0.1:1234",
|
57
|
+
:nonce => 123,
|
58
|
+
:last_block => 188617,
|
59
|
+
})
|
60
|
+
|
61
|
+
parser = Bitcoin::Protocol::Parser.new( handler = Version_Handler.new )
|
62
|
+
parser.parse( version.to_pkt )
|
63
|
+
|
64
|
+
pkt = handler.pkt
|
65
|
+
pkt.fields.should == {
|
66
|
+
:version => Bitcoin::Protocol::VERSION,
|
67
|
+
:services => "\x01\x00\x00\x00\x00\x00\x00\x00",
|
68
|
+
:time => 1337,
|
69
|
+
:to => "127.0.0.1:8333",
|
70
|
+
:from => "127.0.0.1:1234",
|
71
|
+
:nonce => 123,
|
72
|
+
:user_agent => "/bitcoin-ruby:#{Bitcoin::VERSION}/",
|
73
|
+
:last_block => 188617
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include Bitcoin::Builder
|
4
|
+
|
5
|
+
describe "reorg" do
|
6
|
+
|
7
|
+
def create_block prev, store = true
|
8
|
+
block = blk do |b|
|
9
|
+
b.prev_block prev
|
10
|
+
b.tx do |t|
|
11
|
+
t.input {|i| i.coinbase }
|
12
|
+
t.output do |o|
|
13
|
+
o.value 5000000000
|
14
|
+
o.script {|s| s.type :address; s.recipient Bitcoin::Key.generate.addr }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@store.store_block(block) if store
|
19
|
+
block
|
20
|
+
end
|
21
|
+
|
22
|
+
def balance addr
|
23
|
+
@store.get_balance(Bitcoin.hash160_from_address(addr))
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
Bitcoin.network = :testnet
|
28
|
+
@store = Bitcoin::Storage.sequel(:db => "sqlite:/")
|
29
|
+
@store.log.level = :warn
|
30
|
+
@block0 = Bitcoin::Protocol::Block.new(fixtures_file('testnet/block_0.bin'))
|
31
|
+
@store.store_block(@block0)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should reorg a single side block" do
|
35
|
+
@store.get_head.should == @block0
|
36
|
+
|
37
|
+
block1 = create_block @block0.hash
|
38
|
+
@store.get_head.should == block1
|
39
|
+
|
40
|
+
block2_0 = create_block block1.hash
|
41
|
+
@store.get_head.should == block2_0
|
42
|
+
|
43
|
+
block2_1 = create_block block1.hash
|
44
|
+
@store.get_head.should == block2_0
|
45
|
+
|
46
|
+
block3 = create_block block2_1.hash
|
47
|
+
@store.get_head.should == block3
|
48
|
+
@store.get_block_by_depth(2).hash.should == block2_1.hash
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should reorg two side blocks" do
|
52
|
+
block1 = create_block @block0.hash
|
53
|
+
@store.get_head.should == block1
|
54
|
+
|
55
|
+
block2_0 = create_block block1.hash
|
56
|
+
@store.get_head.should == block2_0
|
57
|
+
|
58
|
+
block2_1 = create_block block1.hash
|
59
|
+
@store.get_head.should == block2_0
|
60
|
+
|
61
|
+
block3_1 = create_block block2_1.hash
|
62
|
+
@store.get_head.should == block3_1
|
63
|
+
|
64
|
+
block3_0 = create_block block2_0.hash
|
65
|
+
@store.get_head.should == block3_1
|
66
|
+
|
67
|
+
block4 = create_block block3_0.hash
|
68
|
+
@store.get_head.should == block4
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should reconnect orphans" do
|
72
|
+
blocks = [@block0]
|
73
|
+
3.times { blocks << create_block(blocks.last.hash, false) }
|
74
|
+
|
75
|
+
{
|
76
|
+
[0, 1, 2, 3] => [0, 1, 2, 3],
|
77
|
+
[0, 1, 3, 2] => [0, 1, 1, 3],
|
78
|
+
[0, 3, 2, 1] => [0, 0, 0, 3],
|
79
|
+
[0, 3, 1, 2] => [0, 0, 1, 3],
|
80
|
+
[0, 2, 3, 1] => [0, 0, 0, 3],
|
81
|
+
}.each do |order, result|
|
82
|
+
@store.reset
|
83
|
+
order.each_with_index do |n, i|
|
84
|
+
@store.store_block(blocks[n])
|
85
|
+
@store.get_head.should == blocks[result[i]]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
i = 3; (0..i).to_a.permutation.each do |order|
|
90
|
+
@store.reset
|
91
|
+
order.each {|n| @store.store_block(blocks[n]) }
|
92
|
+
@store.get_head.should == blocks[i]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should handle existing blocks" do
|
97
|
+
Bitcoin.network = :testnet
|
98
|
+
blocks = [@block0]
|
99
|
+
3.times { blocks << create_block(blocks.last.hash, false) }
|
100
|
+
3.times do
|
101
|
+
@store.store_block(blocks[1]).should == [1, 0]
|
102
|
+
@store.store_block(blocks[2]).should == [2, 0]
|
103
|
+
@store.get_head.should == blocks[2]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# see https://bitcointalk.org/index.php?topic=46370.0
|
108
|
+
it "should pass reorg unit tests" do
|
109
|
+
Bitcoin.network = :bitcoin
|
110
|
+
@store.import "./spec/bitcoin/fixtures/reorg/blk_0_to_4.dat"
|
111
|
+
@store.get_depth.should == 4
|
112
|
+
@store.get_head.hash.should =~ /000000002f264d65040/
|
113
|
+
balance("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").should == 10000000000
|
114
|
+
balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 0
|
115
|
+
balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 5000000000
|
116
|
+
balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 10000000000
|
117
|
+
@store.import "./spec/bitcoin/fixtures/reorg/blk_3A.dat"
|
118
|
+
@store.import "./spec/bitcoin/fixtures/reorg/blk_4A.dat"
|
119
|
+
@store.get_head.hash.should =~ /000000002f264d65040/
|
120
|
+
@store.import "./spec/bitcoin/fixtures/reorg/blk_5A.dat"
|
121
|
+
@store.get_depth.should == 5
|
122
|
+
@store.get_head.hash.should =~ /00000000195f85184e7/
|
123
|
+
balance("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").should == 15000000000
|
124
|
+
balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 1000000000
|
125
|
+
balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 0
|
126
|
+
balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 14000000000
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,417 @@
|
|
1
|
+
require_relative '../spec_helper.rb'
|
2
|
+
require 'bitcoin/script'
|
3
|
+
|
4
|
+
describe "Bitcoin::Script OPCODES" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@script = Bitcoin::Script.new("")
|
8
|
+
@script.class.instance_eval { attr_accessor :stack, :stack_alt }
|
9
|
+
@script.stack << "foobar"
|
10
|
+
end
|
11
|
+
|
12
|
+
def op(op, stack)
|
13
|
+
@script.stack = stack
|
14
|
+
@script.send("op_#{op}")
|
15
|
+
@script.stack
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should do OP_NOP" do
|
19
|
+
@script.op_nop
|
20
|
+
@script.stack.should == ["foobar"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should do OP_DUP" do
|
24
|
+
@script.op_dup
|
25
|
+
@script.stack.should == ["foobar", "foobar"]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should do OP_SHA256" do
|
29
|
+
@script.op_sha256
|
30
|
+
@script.stack.should ==
|
31
|
+
[["c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"].pack("H*")]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should do OP_SHA1" do
|
35
|
+
@script.op_sha1
|
36
|
+
@script.stack.should ==
|
37
|
+
[["8843d7f92416211de9ebb963ff4ce28125932878"].pack("H*")]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should do OP_HASH160" do
|
41
|
+
@script.op_hash160
|
42
|
+
@script.stack.should ==
|
43
|
+
[["f6c97547d73156abb300ae059905c4acaadd09dd"].pack("H*")]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should do OP_RIPEMD160" do
|
47
|
+
@script.op_ripemd160
|
48
|
+
@script.stack.should ==
|
49
|
+
[["a06e327ea7388c18e4740e350ed4e60f2e04fc41"].pack("H*")]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should do OP_HASH256" do
|
53
|
+
@script.op_hash256
|
54
|
+
@script.stack.should ==
|
55
|
+
[["3f2c7ccae98af81e44c0ec419659f50d8b7d48c681e5d57fc747d0461e42dda1"].pack("H*")]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should do OP_TOALTSTACK" do
|
59
|
+
@script.op_toaltstack
|
60
|
+
@script.stack.should == []
|
61
|
+
@script.stack_alt.should == ["foobar"]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should do OP_FROMALTSTACK" do
|
65
|
+
@script.instance_eval { @stack_alt << "barfoo" }
|
66
|
+
@script.op_fromaltstack
|
67
|
+
@script.stack.should == ["foobar", "barfoo"]
|
68
|
+
@script.stack_alt.should == []
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should do OP_TUCK" do
|
72
|
+
@script.instance_eval { @stack += ["foo", "bar"] }
|
73
|
+
@script.op_tuck
|
74
|
+
@script.stack.should == ["foobar", "bar", "foo", "bar"]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should do OP_SWAP" do
|
78
|
+
@script.instance_eval { @stack << "barfoo" }
|
79
|
+
@script.op_swap
|
80
|
+
@script.stack.should == ["barfoo", "foobar"]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should do OP_BOOLAND" do
|
84
|
+
op(:booland, [0, 0]).should == [0]
|
85
|
+
op(:booland, [0, 1]).should == [0]
|
86
|
+
op(:booland, [1, 0]).should == [0]
|
87
|
+
op(:booland, [1, 1]).should == [1]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should do OP_ADD" do
|
91
|
+
op(:add, [0, 1]).should == [1]
|
92
|
+
op(:add, [3, 4]).should == [7]
|
93
|
+
op(:add, [5,-4]).should == [1]
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should do OP_SUB" do
|
97
|
+
op(:sub, [2, 3]).should == [1]
|
98
|
+
op(:sub, [1, 9]).should == [8]
|
99
|
+
op(:sub, [3, 1]).should == [-2]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should do OP_GREATERTHANOREQUAL" do
|
103
|
+
op(:greaterthanorequal, [1, 2]).should == [1]
|
104
|
+
op(:greaterthanorequal, [2, 2]).should == [1]
|
105
|
+
op(:greaterthanorequal, [2, 1]).should == [0]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should do OP_DROP" do
|
109
|
+
@script.op_drop
|
110
|
+
@script.stack.should == []
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should do OP_EQUAL" do
|
114
|
+
op(:equal, [1,2]).should == [0]
|
115
|
+
op(:equal, [1,1]).should == [1]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should do OP_VERIFY" do
|
119
|
+
op(:verify, [1]).should == []
|
120
|
+
op(:verify, [0]).should == [0]
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should do OP_EQUALVERIFY" do
|
124
|
+
op(:equalverify, [1,2]).should == [0]
|
125
|
+
@script.invalid?.should == true
|
126
|
+
op(:equalverify, [1,1]).should == []
|
127
|
+
@script.invalid?.should == false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should do OP_0" do
|
131
|
+
@script.op_0
|
132
|
+
@script.stack.should == ["foobar", ""]
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should do OP_1" do
|
136
|
+
@script.op_1
|
137
|
+
@script.stack.should == ["foobar", 1]
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should do OP_MIN" do
|
141
|
+
[
|
142
|
+
[4, 5], [5, 4], [4, 4]
|
143
|
+
].each{|s|
|
144
|
+
@script.instance_eval { @stack = s }
|
145
|
+
@script.op_min
|
146
|
+
@script.stack.should == [4]
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should do OP_MAX" do
|
151
|
+
[
|
152
|
+
[4, 5], [5, 4], [5, 5]
|
153
|
+
].each{|s|
|
154
|
+
@script.instance_eval { @stack = s }
|
155
|
+
@script.op_max
|
156
|
+
@script.stack.should == [5]
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should do op_2over" do
|
161
|
+
@script.instance_eval { @stack = [1,2,3,4] }
|
162
|
+
@script.op_2over
|
163
|
+
@script.stack.should == [1,2,3,4,1,2]
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should do op_2swap" do
|
167
|
+
@script.instance_eval { @stack = [1,2,3,4] }
|
168
|
+
@script.op_2swap
|
169
|
+
@script.stack.should == [3,4,1,2]
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should do op_ifdup" do
|
173
|
+
@script.instance_eval { @stack = [1] }
|
174
|
+
@script.op_ifdup
|
175
|
+
@script.stack.should == [1,1]
|
176
|
+
|
177
|
+
@script.instance_eval { @stack = ['a'] }
|
178
|
+
@script.op_ifdup
|
179
|
+
@script.stack.should == ['a','a']
|
180
|
+
|
181
|
+
@script.instance_eval { @stack = [0] }
|
182
|
+
@script.op_ifdup
|
183
|
+
@script.stack.should == [0]
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should do op_1negate" do
|
187
|
+
@script.instance_eval { @stack = [] }
|
188
|
+
@script.op_1negate
|
189
|
+
@script.stack.should == [ -1 ]
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should do op_depth" do
|
193
|
+
@script.instance_eval { @stack = [] }
|
194
|
+
@script.op_depth
|
195
|
+
@script.stack.should == [0]
|
196
|
+
|
197
|
+
@script.instance_eval { @stack = [1,2,3] }
|
198
|
+
@script.op_depth
|
199
|
+
@script.stack.should == [1,2,3,3]
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should do OP_CHECKSIG" do
|
203
|
+
@script.stack = ["bar", "foo"]
|
204
|
+
verify_callback = proc{|pubkey,signature,type|
|
205
|
+
pubkey .should == "foo"
|
206
|
+
signature.should == "ba"
|
207
|
+
type .should == "r".ord
|
208
|
+
true
|
209
|
+
}
|
210
|
+
@script.op_checksig(verify_callback).should == [1]
|
211
|
+
|
212
|
+
@script.stack = ["bar", "foo"]
|
213
|
+
verify_callback = proc{ true }
|
214
|
+
@script.op_checksig(verify_callback).should == [1]
|
215
|
+
|
216
|
+
@script.stack = ["bar", "foo"]
|
217
|
+
verify_callback = proc{ false }
|
218
|
+
@script.op_checksig(verify_callback).should == [0]
|
219
|
+
|
220
|
+
@script.stack = ["foo"]
|
221
|
+
verify_callback = proc{ false }
|
222
|
+
@script.op_checksig(verify_callback).should == nil
|
223
|
+
|
224
|
+
|
225
|
+
pubkey = ["04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3"].pack("H*")
|
226
|
+
signature = ["304402202c2fb840b527326f9bbc7ce68c6c196a368a38864b5a47681352c4b2f416f7ed02205c4801cfa8aed205f26c7122ab5a5934fcf7a2f038fd130cdd8bcc56bdde0a00"].pack("H*")
|
227
|
+
hash_type = [1].pack("C")
|
228
|
+
signature_data = ["20245059adb84acaf1aa942b5d8a586da7ba76f17ecb5de4e7543e1ce1b94bc3"].pack("H*")
|
229
|
+
|
230
|
+
@script.stack = [signature + hash_type, pubkey]
|
231
|
+
verify_callback = proc{|pub,sig,hash_type|
|
232
|
+
pub .should == pubkey
|
233
|
+
sig .should == signature
|
234
|
+
hash_type.should == 1
|
235
|
+
|
236
|
+
hash = signature_data
|
237
|
+
Bitcoin.verify_signature( hash, sig, pub.unpack("H*")[0] )
|
238
|
+
}
|
239
|
+
@script.op_checksig(verify_callback).should == [1]
|
240
|
+
|
241
|
+
|
242
|
+
@script.stack = [signature + hash_type, pubkey]
|
243
|
+
verify_callback = proc{|pub,sig,hash_type|
|
244
|
+
hash = "foo" + signature_data
|
245
|
+
Bitcoin.verify_signature( hash, sig, pub.unpack("H*")[0] )
|
246
|
+
}
|
247
|
+
@script.op_checksig(verify_callback).should == [0]
|
248
|
+
|
249
|
+
@script.stack = [signature + hash_type, pubkey]
|
250
|
+
verify_callback = proc{|pub,sig,hash_type|
|
251
|
+
hash = signature_data
|
252
|
+
Bitcoin.verify_signature( hash, "foo", pub.unpack("H*")[0] )
|
253
|
+
}
|
254
|
+
@script.op_checksig(verify_callback).should == [0]
|
255
|
+
|
256
|
+
@script.stack = [signature + hash_type, pubkey]
|
257
|
+
verify_callback = proc{|pub,sig,hash_type|
|
258
|
+
hash = signature_data
|
259
|
+
Bitcoin.verify_signature( hash, sig, "foo" )
|
260
|
+
}
|
261
|
+
@script.op_checksig(verify_callback).should == [0]
|
262
|
+
|
263
|
+
# Bitcoin::Key API
|
264
|
+
key = Bitcoin::Key.new; key.generate
|
265
|
+
sig = (key.sign("foobar") + "\x01").unpack("H*")[0]
|
266
|
+
script = Bitcoin::Script.from_string("#{sig} #{key.pub} OP_CHECKSIG")
|
267
|
+
script.run{|pk, sig, hash_type|
|
268
|
+
k = Bitcoin::Key.new nil, pk.unpack("H*")[0]
|
269
|
+
k.verify("foobar", sig)
|
270
|
+
}.should == true
|
271
|
+
script.stack.should == []
|
272
|
+
end
|
273
|
+
|
274
|
+
def run_script(string, hash)
|
275
|
+
script = Bitcoin::Script.from_string(string)
|
276
|
+
script.run do |pk, sig, hash_type|
|
277
|
+
k = Bitcoin::Key.new nil, pk.unpack("H*")[0]
|
278
|
+
k.verify(hash, sig) rescue false
|
279
|
+
end == true
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should do OP_CHECKMULTISIG" do
|
283
|
+
k1 = Bitcoin::Key.new; k1.generate
|
284
|
+
k2 = Bitcoin::Key.new; k2.generate
|
285
|
+
k3 = Bitcoin::Key.new; k3.generate
|
286
|
+
sig1 = (k1.sign("foobar") + "\x01").unpack("H*")[0]
|
287
|
+
sig2 = (k2.sign("foobar") + "\x01").unpack("H*")[0]
|
288
|
+
sig3 = (k3.sign("foobar") + "\x01").unpack("H*")[0]
|
289
|
+
|
290
|
+
script = "0 #{sig1} #{sig2} 2 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
291
|
+
run_script(script, "foobar").should == true
|
292
|
+
|
293
|
+
script = "0 #{sig1} #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
294
|
+
run_script(script, "foobar").should == true
|
295
|
+
|
296
|
+
script = "0 #{sig2} #{sig3} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
297
|
+
run_script(script, "foobar").should == true
|
298
|
+
|
299
|
+
script = "0 #{sig1} #{sig2} #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
300
|
+
run_script(script, "foobar").should == true
|
301
|
+
|
302
|
+
script = "#{sig1} #{sig2} #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG" # without OP_NOP
|
303
|
+
run_script(script, "foobar").should == true
|
304
|
+
|
305
|
+
script = "0 #{sig2} 1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
306
|
+
run_script(script, "foobar").should == true
|
307
|
+
|
308
|
+
script = "0 #{sig2} OP_TRUE #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
309
|
+
run_script(script, "foobar").should == true
|
310
|
+
|
311
|
+
script = "0 #{sig1} #{sig2} #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
|
312
|
+
run_script(script, "foobar").should == false
|
313
|
+
|
314
|
+
script = "0 #{sig1} #{sig2} #{sig3} 3 #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
|
315
|
+
run_script(script, "foobar").should == false
|
316
|
+
|
317
|
+
script = "0 #{sig1} #{sig2} #{sig3} 3 2 #{k3.pub} 2 OP_CHECKMULTISIG"
|
318
|
+
run_script(script, "foobar").should == false
|
319
|
+
|
320
|
+
script = "0 #{sig1} #{sig2} #{sig3} 3 0 #{k3.pub} 2 OP_CHECKMULTISIG"
|
321
|
+
run_script(script, "foobar").should == false
|
322
|
+
|
323
|
+
script = "0 #{sig1} #{sig2} 2 3 #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
|
324
|
+
run_script(script, "foobar").should == false
|
325
|
+
|
326
|
+
script = "0 #{sig1} #{sig2} 0 3 #{k2.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
|
327
|
+
run_script(script, "foobar").should == false
|
328
|
+
|
329
|
+
script = "0 #{sig2} f0f0f0f0 2 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
330
|
+
run_script(script, "foobar").should == false
|
331
|
+
|
332
|
+
script = "0 afafafaf #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
333
|
+
run_script(script, "foobar").should == false
|
334
|
+
|
335
|
+
script = "0 #{sig1} f0f0f0f0 #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
336
|
+
run_script(script, "foobar").should == false
|
337
|
+
|
338
|
+
# # TODO: check signature order; these assertions should fail:
|
339
|
+
# script = "0 #{sig2} #{sig1} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
340
|
+
# run_script(script, "foobar").should == false
|
341
|
+
# script = "0 #{sig3} #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
342
|
+
# run_script(script, "foobar").should == false
|
343
|
+
# script = "0 #{sig1} #{sig3} #{sig2} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
344
|
+
# run_script(script, "foobar").should == false
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
it "should do OP_CHECKHASHVERIFY" do # https://en.bitcoin.it/wiki/BIP_0017
|
349
|
+
k1 = Bitcoin::Key.new; k1.generate
|
350
|
+
k2 = Bitcoin::Key.new; k2.generate
|
351
|
+
k3 = Bitcoin::Key.new; k2.generate
|
352
|
+
sig1 = (k1.sign("foobar") + "\x01").unpack("H*")[0]
|
353
|
+
sig2 = (k2.sign("foobar") + "\x01").unpack("H*")[0]
|
354
|
+
sig3 = (k2.sign("foobar") + "\x01").unpack("H*")[0]
|
355
|
+
|
356
|
+
|
357
|
+
# scriptSig: [signatures...] OP_CODESEPARATOR 1 [pubkey1] [pubkey2] 2 OP_CHECKMULTISIG
|
358
|
+
# scriptPubKey: [20-byte-hash of {1 [pubkey1] [pubkey2] 2 OP_CHECKMULTISIG} ] OP_CHECKHASHVERIFY OP_DROP
|
359
|
+
script = "1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
360
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
361
|
+
script = "0 #{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
|
362
|
+
run_script(script, "foobar").should == true
|
363
|
+
|
364
|
+
script = "1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
365
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
366
|
+
script = "0 #{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_NOP2 OP_DROP" # tests OP_NOP2 as OP_CHECKHASHVERIFY
|
367
|
+
run_script(script, "foobar").should == true
|
368
|
+
|
369
|
+
# invalid checkhashverify
|
370
|
+
script = "1 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
371
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
372
|
+
script = "1 #{k1.pub} #{k3.pub} 2 OP_CHECKMULTISIG"
|
373
|
+
script = "0 #{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_NOP2 OP_DROP" # tests OP_NOP2 as OP_CHECKHASHVERIFY
|
374
|
+
run_script(script, "foobar").should == false
|
375
|
+
|
376
|
+
|
377
|
+
# scriptSig: [signature] OP_CODESEPARATOR [pubkey] OP_CHECKSIG
|
378
|
+
# scriptPubKey: [20-byte-hash of {[pubkey] OP_CHECKSIG} ] OP_CHECKHASHVERIFY OP_DROP
|
379
|
+
script = "#{k1.pub} OP_CHECKSIG"
|
380
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
381
|
+
script = "#{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
|
382
|
+
run_script(script, "foobar").should == true
|
383
|
+
|
384
|
+
# invalid checkhashverify
|
385
|
+
script = "#{k2.pub} OP_CHECKSIG"
|
386
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
387
|
+
script = "#{k1.pub} OP_CHECKSIG"
|
388
|
+
script = "#{sig1} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
|
389
|
+
run_script(script, "foobar").should == false
|
390
|
+
|
391
|
+
# invalid signature in checksig
|
392
|
+
script = "#{k1.pub} OP_CHECKSIG"
|
393
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
394
|
+
script = "#{sig2} OP_CODESEPARATOR #{script} #{checkhash} OP_CHECKHASHVERIFY OP_DROP"
|
395
|
+
run_script(script, "foobar").should == false
|
396
|
+
end
|
397
|
+
|
398
|
+
it "should do P2SH" do
|
399
|
+
k1 = Bitcoin::Key.new; k1.generate
|
400
|
+
sig = (k1.sign("foobar") + "\x01").unpack("H*")[0]
|
401
|
+
inner_script = Bitcoin::Script.from_string("#{k1.pub} OP_CHECKSIG").raw.unpack("H*")[0]
|
402
|
+
script_hash = Bitcoin.hash160(inner_script)
|
403
|
+
script = Bitcoin::Script.from_string("#{sig} #{inner_script} OP_HASH160 #{script_hash} OP_EQUAL")
|
404
|
+
script.is_p2sh?.should == true
|
405
|
+
run_script(script.to_string, "foobar").should == true
|
406
|
+
run_script(script.to_string, "barbaz").should == false
|
407
|
+
|
408
|
+
script = Bitcoin::Script.from_string("OP_HASH160 #{script_hash} OP_EQUAL")
|
409
|
+
script.is_p2sh?.should == true
|
410
|
+
run_script(script.to_string, "foobar").should == false
|
411
|
+
|
412
|
+
address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
|
413
|
+
script = Bitcoin::Script.new(Bitcoin::Script.to_address_script(address))
|
414
|
+
script.type.should == :p2sh
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|