bitcoin-ruby 0.0.14 → 0.0.15
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.
- 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
|