bitcoin-ruby 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -3
- data/COPYING +1 -1
- data/Gemfile.lock +1 -1
- data/README.rdoc +11 -29
- data/lib/bitcoin.rb +83 -11
- data/lib/bitcoin/bech32.rb +172 -0
- data/lib/bitcoin/builder.rb +25 -7
- data/lib/bitcoin/ffi/secp256k1.rb +12 -12
- data/lib/bitcoin/protocol/txout.rb +7 -1
- data/lib/bitcoin/script.rb +25 -8
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bech32_spec.rb +160 -0
- data/spec/bitcoin/bitcoin_spec.rb +60 -0
- data/spec/bitcoin/builder_spec.rb +33 -0
- data/spec/bitcoin/fixtures/base58_keys_invalid.json +182 -0
- data/spec/bitcoin/fixtures/base58_keys_valid.json +488 -0
- data/spec/bitcoin/protocol/txout_spec.rb +6 -0
- data/spec/bitcoin/script/script_spec.rb +4 -0
- data/spec/bitcoin/secp256k1_spec.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 392936a305872b186484815f8a1c6c91725029d1
|
4
|
+
data.tar.gz: f44d55252bf1c614e5679b80113cfd59ca94815e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dab50ef851a4ba5a202099052ce566f86416cfdad002289668d76d109cdc32298fb0f3f99713522dee1d1dfa4bbac0335db3a9f4dd15b53bd7b9daaed9fc633
|
7
|
+
data.tar.gz: 06b920e486a56d4c22ad20ac2da62f2d3c89da1a60f561c01e82bf7cf953bc8db9e70bed8de2e01b1517843c72132fe9cc68403624b897bbcbc2cf3845baee86
|
data/.travis.yml
CHANGED
data/COPYING
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2018 Julian Langschaedel <meta.rb@gmail.com>
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to
|
data/Gemfile.lock
CHANGED
data/README.rdoc
CHANGED
@@ -4,22 +4,13 @@ This is a ruby library for interacting with the bitcoin protocol/network.
|
|
4
4
|
|
5
5
|
Some of the main features are:
|
6
6
|
|
7
|
+
* Bitcoin::Key provides a high-level API for creating and handling keys/addresses
|
7
8
|
* Bitcoin::Util provides the basic bitcoin utility functions for base58, ECC, etc.
|
8
9
|
* Bitcoin::Protocol can parse/create all protocol messages
|
9
10
|
* Bitcoin::Script implementation, create/run scripts and verify signatures
|
10
|
-
* Bitcoin::Key provides a high-level API for creating and handling keys/addresses
|
11
11
|
* Bitcoin::Builder provides a high-level API for creating transactions (and blocks)
|
12
12
|
* Bitcoin::Litecoin implements all the litecoin-specific differences
|
13
13
|
|
14
|
-
== Related Projects
|
15
|
-
|
16
|
-
* toshi[https://github.com/coinbase/toshi]
|
17
|
-
* bitcoin-ruby-blockchain[http://github.com/mhanne/bitcoin-ruby-blockchain]
|
18
|
-
* bitcoin-ruby-node[http://github.com/mhanne/bitcoin-ruby-node]
|
19
|
-
* bitcoin-ruby-wallet[http://github.com/mhanne/bitcoin-ruby-wallet]
|
20
|
-
* bitcoin-ruby-gui[http://github.com/mhanne/bitcoin-ruby-gui]
|
21
|
-
* namecoin-ruby[http://github.com/mhanne/namecoin-ruby]
|
22
|
-
|
23
14
|
== Compatible with...
|
24
15
|
|
25
16
|
* ruby 1.9.3
|
@@ -30,13 +21,9 @@ Some of the main features are:
|
|
30
21
|
|
31
22
|
== Installation
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
git clone https://github.com/lian/bitcoin-ruby.git; cd bitcoin-ruby
|
36
|
-
|
37
|
-
if you want to have it available system-wide, just build the gem and install it:
|
38
|
-
|
39
|
-
gem build bitcoin-ruby.gemspec && gem install bitcoin-ruby-0.0.11.gem
|
24
|
+
gem install bitcoin-ruby
|
25
|
+
# OR
|
26
|
+
git clone https://github.com/lian/bitcoin-ruby.git; cd bitcoin-ruby; bundle install
|
40
27
|
|
41
28
|
Note that some aspects of the library (such as networking, storage, etc.) need
|
42
29
|
additional dependencies which are not specified in the gemspec. The core requirements are
|
@@ -58,18 +45,13 @@ see EXAMPLES.
|
|
58
45
|
|
59
46
|
Generate a Bitcoin::Key
|
60
47
|
|
61
|
-
key = Bitcoin::
|
62
|
-
key
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
Check if an address is valid
|
70
|
-
|
71
|
-
Bitcoin::valid_address?(address) #=> true
|
72
|
-
|
48
|
+
key = Bitcoin::Key.generate
|
49
|
+
key.priv
|
50
|
+
key.pub
|
51
|
+
key.addr
|
52
|
+
sig = key.sign("data")
|
53
|
+
key.verify("data", sig)
|
54
|
+
recovered_key = Bitcoin::Key.from_base58(key.to_base58)
|
73
55
|
|
74
56
|
=== Blocks / Transactions parsing
|
75
57
|
|
data/lib/bitcoin.rb
CHANGED
@@ -8,6 +8,7 @@ require 'securerandom'
|
|
8
8
|
|
9
9
|
module Bitcoin
|
10
10
|
|
11
|
+
autoload :Bech32, 'bitcoin/bech32'
|
11
12
|
autoload :Connection, 'bitcoin/connection'
|
12
13
|
autoload :Protocol, 'bitcoin/protocol'
|
13
14
|
autoload :P, 'bitcoin/protocol'
|
@@ -58,10 +59,7 @@ module Bitcoin
|
|
58
59
|
# check if given +address+ is valid.
|
59
60
|
# this means having a correct version byte, length and checksum.
|
60
61
|
def valid_address?(address)
|
61
|
-
|
62
|
-
return false unless hex && hex.bytesize == 50
|
63
|
-
return false unless [address_version, p2sh_version].include?(hex[0...2])
|
64
|
-
address_checksum?(address)
|
62
|
+
address_type(address) != nil
|
65
63
|
end
|
66
64
|
|
67
65
|
# check if given +pubkey+ is valid.
|
@@ -74,17 +72,42 @@ module Bitcoin
|
|
74
72
|
|
75
73
|
# get hash160 for given +address+. returns nil if address is invalid.
|
76
74
|
def hash160_from_address(address)
|
77
|
-
|
78
|
-
|
75
|
+
case address_type(address)
|
76
|
+
when :witness_v0_keyhash
|
77
|
+
_, witness_program_hex = decode_segwit_address(address)
|
78
|
+
witness_program_hex
|
79
|
+
when :hash160, :p2sh
|
80
|
+
decode_base58(address)[2...42]
|
81
|
+
end
|
79
82
|
end
|
80
83
|
|
81
84
|
# get type of given +address+.
|
82
85
|
def address_type(address)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
86
|
+
segwit_decoded = decode_segwit_address(address)
|
87
|
+
if segwit_decoded
|
88
|
+
witness_version, witness_program_hex = segwit_decoded
|
89
|
+
witness_program = [witness_program_hex].pack("H*")
|
90
|
+
|
91
|
+
if witness_version == 0 && witness_program.bytesize == 20
|
92
|
+
return :witness_v0_keyhash
|
93
|
+
end
|
94
|
+
|
95
|
+
if witness_version == 0 && witness_program.bytesize == 32
|
96
|
+
return :witness_v0_scripthash
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
hex = decode_base58(address) rescue nil
|
101
|
+
if hex && hex.bytesize == 50 && address_checksum?(address)
|
102
|
+
case hex[0...2]
|
103
|
+
when address_version
|
104
|
+
return :hash160
|
105
|
+
when p2sh_version
|
106
|
+
return :p2sh
|
107
|
+
end
|
87
108
|
end
|
109
|
+
|
110
|
+
nil
|
88
111
|
end
|
89
112
|
|
90
113
|
def sha256(hex)
|
@@ -113,6 +136,49 @@ module Bitcoin
|
|
113
136
|
return Bitcoin.hash160_to_p2sh_address(Bitcoin.hash160(redeem_script.hth)), redeem_script
|
114
137
|
end
|
115
138
|
|
139
|
+
|
140
|
+
def encode_segwit_address(version, program_hex)
|
141
|
+
hrp = Bitcoin.network[:bech32_hrp]
|
142
|
+
raise "Invalid network" if hrp.nil?
|
143
|
+
|
144
|
+
program = [program_hex].pack("H*")
|
145
|
+
|
146
|
+
return nil if version > 16
|
147
|
+
length = program.size
|
148
|
+
return nil if version == 0 && length != 20 && length != 32
|
149
|
+
return nil if length < 2 || length > 40
|
150
|
+
|
151
|
+
data = [ version ] + Bitcoin::Bech32.convert_bits(program.unpack("C*"), from_bits: 8, to_bits: 5, pad: true)
|
152
|
+
|
153
|
+
address = Bitcoin::Bech32.encode(hrp, data)
|
154
|
+
|
155
|
+
return address.nil? ? nil : address
|
156
|
+
end
|
157
|
+
|
158
|
+
def decode_segwit_address(address)
|
159
|
+
hrp = Bitcoin.network[:bech32_hrp]
|
160
|
+
return nil if hrp.nil?
|
161
|
+
|
162
|
+
actual_hrp, data = Bitcoin::Bech32.decode(address)
|
163
|
+
|
164
|
+
return nil if actual_hrp.nil?
|
165
|
+
length = data.size
|
166
|
+
return nil if length == 0 || length > 65
|
167
|
+
return nil if hrp != actual_hrp
|
168
|
+
return nil if data[0] > 16
|
169
|
+
|
170
|
+
|
171
|
+
program = Bitcoin::Bech32.convert_bits(data[1..-1], from_bits: 5, to_bits: 8, pad: false)
|
172
|
+
return nil if program.nil?
|
173
|
+
|
174
|
+
length = program.size
|
175
|
+
return nil if length < 2 || length > 40
|
176
|
+
return nil if data[0] == 0 && length != 20 && length != 32
|
177
|
+
|
178
|
+
program_hex = program.pack("C*").unpack("H*").first
|
179
|
+
return [data[0], program_hex]
|
180
|
+
end
|
181
|
+
|
116
182
|
def int_to_base58(int_val, leading_zero_bytes=0)
|
117
183
|
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
118
184
|
base58_val, base = '', alpha.size
|
@@ -439,7 +505,6 @@ module Bitcoin
|
|
439
505
|
|
440
506
|
extend Util
|
441
507
|
|
442
|
-
|
443
508
|
module BinaryExtensions
|
444
509
|
# bin-to-hex
|
445
510
|
def bth; unpack("H*")[0]; end
|
@@ -557,6 +622,7 @@ module Bitcoin
|
|
557
622
|
privkey_version: "80",
|
558
623
|
extended_privkey_version: "0488ade4",
|
559
624
|
extended_pubkey_version: "0488b21e",
|
625
|
+
bech32_hrp: "bc",
|
560
626
|
default_port: 8333,
|
561
627
|
protocol_version: 70001,
|
562
628
|
coinbase_maturity: 100,
|
@@ -614,6 +680,7 @@ module Bitcoin
|
|
614
680
|
privkey_version: "ef",
|
615
681
|
extended_privkey_version: "04358394",
|
616
682
|
extended_pubkey_version: "043587cf",
|
683
|
+
bech32_hrp: "tb",
|
617
684
|
default_port: 18333,
|
618
685
|
bip34_height: 21111,
|
619
686
|
dns_seeds: [ ],
|
@@ -625,6 +692,7 @@ module Bitcoin
|
|
625
692
|
})
|
626
693
|
|
627
694
|
NETWORKS[:regtest] = NETWORKS[:testnet].merge({
|
695
|
+
bech32_hrp: "bcrt",
|
628
696
|
default_port: 18444,
|
629
697
|
genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
|
630
698
|
proof_of_work_limit: 0x207fffff,
|
@@ -660,6 +728,7 @@ module Bitcoin
|
|
660
728
|
address_version: "30",
|
661
729
|
p2sh_version: "32",
|
662
730
|
privkey_version: "b0",
|
731
|
+
bech32_hrp: "ltc",
|
663
732
|
extended_privkey_version: "019d9cfe",
|
664
733
|
extended_pubkey_version: "019da462",
|
665
734
|
default_port: 9333,
|
@@ -710,6 +779,7 @@ module Bitcoin
|
|
710
779
|
address_version: "6f",
|
711
780
|
p2sh_version: "3a",
|
712
781
|
privkey_version: "ef",
|
782
|
+
bech32_hrp: "tltc",
|
713
783
|
extended_privkey_version: "0436ef7d",
|
714
784
|
extended_pubkey_version: "0436f6e1",
|
715
785
|
default_port: 19335,
|
@@ -733,6 +803,7 @@ module Bitcoin
|
|
733
803
|
address_version: "1e",
|
734
804
|
p2sh_version: "16",
|
735
805
|
privkey_version: "9e",
|
806
|
+
bech32_hrp: nil,
|
736
807
|
extended_privkey_version: "02fac398",
|
737
808
|
extended_pubkey_version: "02facafd",
|
738
809
|
default_port: 22556,
|
@@ -835,6 +906,7 @@ module Bitcoin
|
|
835
906
|
project: :namecoin,
|
836
907
|
magic_head: "\xF9\xBE\xB4\xFE",
|
837
908
|
address_version: "34",
|
909
|
+
bech32_hrp: nil,
|
838
910
|
default_port: 8334,
|
839
911
|
protocol_version: 35000,
|
840
912
|
min_tx_fee: 50_000,
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# Ruby reference implementation: https://github.com/sipa/bech32/tree/master/ref/c
|
3
|
+
module Bitcoin; end
|
4
|
+
module Bitcoin::Bech32
|
5
|
+
|
6
|
+
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l".unpack("C*")
|
7
|
+
CHARSET_REV = [
|
8
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
9
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
10
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
11
|
+
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
12
|
+
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
13
|
+
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
14
|
+
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
15
|
+
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
16
|
+
]
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
def polymod_step(pre)
|
21
|
+
b = pre >> 25
|
22
|
+
return ((pre & 0x1FFFFFF) << 5) ^ \
|
23
|
+
(-((b >> 0) & 1) & 0x3b6a57b2) ^ \
|
24
|
+
(-((b >> 1) & 1) & 0x26508e6d) ^ \
|
25
|
+
(-((b >> 2) & 1) & 0x1ea119fa) ^ \
|
26
|
+
(-((b >> 3) & 1) & 0x3d4233dd) ^ \
|
27
|
+
(-((b >> 4) & 1) & 0x2a1462b3)
|
28
|
+
end
|
29
|
+
|
30
|
+
def encode(hrp, data)
|
31
|
+
buf = []
|
32
|
+
chk = 1
|
33
|
+
|
34
|
+
hrp.unpack("C*").each do |ch|
|
35
|
+
return nil if ch < 33 || ch > 126
|
36
|
+
return nil if ch >= 'A'.ord && ch <= 'Z'.ord
|
37
|
+
chk = polymod_step(chk) ^ (ch >> 5)
|
38
|
+
end
|
39
|
+
|
40
|
+
return nil if (hrp.bytesize + 7 + data.size) > 90
|
41
|
+
|
42
|
+
chk = polymod_step(chk)
|
43
|
+
hrp.unpack("C*").each do |ch|
|
44
|
+
chk = polymod_step(chk) ^ (ch & 0x1f)
|
45
|
+
buf << ch
|
46
|
+
end
|
47
|
+
|
48
|
+
buf << '1'.ord
|
49
|
+
|
50
|
+
data.each do |i|
|
51
|
+
return nil if (i >> 5) != 0
|
52
|
+
chk = polymod_step(chk) ^ i
|
53
|
+
buf << CHARSET[i]
|
54
|
+
end
|
55
|
+
|
56
|
+
6.times do |n|
|
57
|
+
chk = polymod_step(chk)
|
58
|
+
end
|
59
|
+
|
60
|
+
chk ^= 1
|
61
|
+
|
62
|
+
6.times do |i|
|
63
|
+
buf << CHARSET[(chk >> ((5 - i) * 5)) & 0x1f]
|
64
|
+
end
|
65
|
+
|
66
|
+
return buf.pack("C*")
|
67
|
+
end
|
68
|
+
|
69
|
+
def decode(input)
|
70
|
+
chk = 1
|
71
|
+
input_len = input.bytesize
|
72
|
+
have_lower, have_upper = false, false
|
73
|
+
|
74
|
+
return nil if input_len < 8 || input_len > 90
|
75
|
+
|
76
|
+
data_len = 0
|
77
|
+
while data_len < input_len && input[(input_len - 1) - data_len] != '1' do
|
78
|
+
data_len += 1
|
79
|
+
end
|
80
|
+
|
81
|
+
hrp_len = input_len - (1 + data_len)
|
82
|
+
return nil if hrp_len < 1 || data_len < 6
|
83
|
+
|
84
|
+
data_len -= 6
|
85
|
+
|
86
|
+
hrp = []
|
87
|
+
hrp_len.times do |i|
|
88
|
+
ch = input[i].ord
|
89
|
+
return nil if ch < 33 || ch > 126
|
90
|
+
|
91
|
+
if ch >= 'a'.ord && ch <= 'z'.ord
|
92
|
+
have_lower = true
|
93
|
+
elsif ch >= 'A'.ord && ch <= 'Z'.ord
|
94
|
+
have_upper = true
|
95
|
+
ch = (ch - 'A'.ord) + 'a'.ord
|
96
|
+
end
|
97
|
+
|
98
|
+
hrp << ch
|
99
|
+
chk = polymod_step(chk) ^ (ch >> 5)
|
100
|
+
end
|
101
|
+
|
102
|
+
chk = polymod_step(chk)
|
103
|
+
|
104
|
+
hrp_len.times do |i|
|
105
|
+
chk = polymod_step(chk) ^ (input[i].ord & 0x1f)
|
106
|
+
end
|
107
|
+
|
108
|
+
data = []
|
109
|
+
i = hrp_len + 1
|
110
|
+
while i < input_len do
|
111
|
+
ch = input[i].ord
|
112
|
+
v = ((ch & 0x80) != 0) ? -1 : CHARSET_REV[ch]
|
113
|
+
|
114
|
+
have_lower = true if ch >= 'a'.ord && ch <= 'z'.ord
|
115
|
+
have_upper = true if ch >= 'A'.ord && ch <= 'Z'.ord
|
116
|
+
return nil if v == -1
|
117
|
+
|
118
|
+
chk = polymod_step(chk) ^ v
|
119
|
+
if (i + 6) < input_len
|
120
|
+
data << v
|
121
|
+
end
|
122
|
+
i += 1
|
123
|
+
end
|
124
|
+
|
125
|
+
return nil if have_lower && have_upper
|
126
|
+
return nil if chk != 1
|
127
|
+
|
128
|
+
return [hrp.pack("C*"), data]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Utility for converting bytes of data between bases. These is used for
|
132
|
+
# BIP 173 address encoding/decoding to convert between sequences of bytes
|
133
|
+
# representing 8-bit values and groups of 5 bits. Conversions may be padded
|
134
|
+
# with trailing 0 bits to the nearest byte boundary. Returns nil if
|
135
|
+
# conversion requires padding and pad is false.
|
136
|
+
#
|
137
|
+
# For example:
|
138
|
+
#
|
139
|
+
# convert_bits("\xFF\xFF", from_bits: 8, to_bits: 5, pad: true)
|
140
|
+
# => "\x1F\x1F\x1F\10"
|
141
|
+
#
|
142
|
+
# See https://github.com/bitcoin/bitcoin/blob/595a7bab23bc21049526229054ea1fff1a29c0bf/src/utilstrencodings.h#L154
|
143
|
+
def convert_bits(chunks, from_bits:, to_bits:, pad:)
|
144
|
+
output_mask = (1 << to_bits) - 1
|
145
|
+
buffer_mask = (1 << (from_bits + to_bits - 1)) - 1
|
146
|
+
|
147
|
+
buffer = 0
|
148
|
+
bits = 0
|
149
|
+
|
150
|
+
output = []
|
151
|
+
chunks.each do |chunk|
|
152
|
+
buffer = ((buffer << from_bits) | chunk) & buffer_mask
|
153
|
+
bits += from_bits
|
154
|
+
while bits >= to_bits
|
155
|
+
bits -= to_bits
|
156
|
+
output << ((buffer >> bits) & output_mask)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
if pad && bits > 0
|
161
|
+
output << ((buffer << (to_bits - bits)) & output_mask)
|
162
|
+
end
|
163
|
+
|
164
|
+
if !pad && (bits >= from_bits || ((buffer << (to_bits - bits)) & output_mask) != 0)
|
165
|
+
return nil
|
166
|
+
end
|
167
|
+
|
168
|
+
output
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|