bitcoinrb 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/README.md +40 -1
- data/bitcoinrb.gemspec +1 -0
- data/lib/bitcoin/base58.rb +2 -4
- data/lib/bitcoin/bloom_filter.rb +71 -0
- data/lib/bitcoin/chain_params.rb +8 -0
- data/lib/bitcoin/constants.rb +8 -1
- data/lib/bitcoin/ext_key.rb +0 -4
- data/lib/bitcoin/key.rb +31 -7
- data/lib/bitcoin/message/block_transaction_request.rb +45 -0
- data/lib/bitcoin/message/block_transactions.rb +31 -0
- data/lib/bitcoin/message/block_txn.rb +27 -0
- data/lib/bitcoin/message/cmpct_block.rb +42 -0
- data/lib/bitcoin/message/get_block_txn.rb +27 -0
- data/lib/bitcoin/message/header_and_short_ids.rb +57 -0
- data/lib/bitcoin/message/prefilled_tx.rb +29 -0
- data/lib/bitcoin/message/send_cmpct.rb +1 -1
- data/lib/bitcoin/message/version.rb +1 -1
- data/lib/bitcoin/message.rb +7 -0
- data/lib/bitcoin/network/message_handler.rb +7 -1
- data/lib/bitcoin/network/peer.rb +26 -4
- data/lib/bitcoin/network/pool.rb +17 -1
- data/lib/bitcoin/node/cli.rb +5 -0
- data/lib/bitcoin/node/configuration.rb +1 -0
- data/lib/bitcoin/node/spv.rb +21 -1
- data/lib/bitcoin/rpc/request_handler.rb +6 -0
- data/lib/bitcoin/script/script.rb +4 -1
- data/lib/bitcoin/script/script_interpreter.rb +5 -18
- data/lib/bitcoin/secp256k1/native.rb +9 -3
- data/lib/bitcoin/secp256k1/ruby.rb +51 -23
- data/lib/bitcoin/secp256k1.rb +2 -0
- data/lib/bitcoin/store/chain_entry.rb +1 -2
- data/lib/bitcoin/store/db/level_db.rb +2 -3
- data/lib/bitcoin/store/spv_chain.rb +1 -1
- data/lib/bitcoin/tx.rb +8 -4
- data/lib/bitcoin/tx_in.rb +3 -0
- data/lib/bitcoin/tx_out.rb +3 -0
- data/lib/bitcoin/util.rb +3 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +3 -3
- data/lib/bitcoin/wallet/base.rb +15 -1
- data/lib/bitcoin/wallet/db.rb +9 -2
- data/lib/bitcoin.rb +23 -3
- metadata +25 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 99f5168564a4d436b4a3a43ce30fc4cf0657f4feb0f96cfa57362de3a322846b
|
4
|
+
data.tar.gz: 0bf7749a29bfcebb5006a5a01f47dabeb766d98269476dfa75f2d524af6d6da2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c23353ea258a891d79fff2f91a0e8661f389dc6045ca0199955dfe3fb91cdb01be5dc857553c7073633c7f34f0603991f6761403f787aea9bf306586124ccea4
|
7
|
+
data.tar.gz: '0597bdd738e1ffc9b8189ec1aaf05245b2cc988bd4ce3a92ad35bf8182cfb3864b59209c7a4894cf0c6d10d5fabaf3a9f2e0da10c7afa25f595969274ca3f5e1'
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.5.0
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -50,7 +50,46 @@ Or install it yourself as:
|
|
50
50
|
|
51
51
|
## Usage
|
52
52
|
|
53
|
-
|
53
|
+
### Chain selection
|
54
|
+
|
55
|
+
The parameters of the blockchain are managed by `Bitcoin::ChainParams`. Switch chain parameters as follows:
|
56
|
+
|
57
|
+
* mainnet
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
Bitcoin.chain_params = :mainnet
|
61
|
+
```
|
62
|
+
|
63
|
+
This parameter is described in https://github.com/haw-itn/bitcoinrb/blob/master/lib/bitcoin/chainparams/mainnet.yml.
|
64
|
+
|
65
|
+
* testnet
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
Bitcoin.chain_params = :testnet
|
69
|
+
```
|
70
|
+
|
71
|
+
This parameter is described in https://github.com/haw-itn/bitcoinrb/blob/master/lib/bitcoin/chainparams/testnet.yml.
|
72
|
+
|
73
|
+
* regtest
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
Bitcoin.chain_params = :regtest
|
77
|
+
```
|
78
|
+
|
79
|
+
This parameter is described in https://github.com/haw-itn/bitcoinrb/blob/master/lib/bitcoin/chainparams/regtest.yml.
|
80
|
+
|
81
|
+
#### Fork coin
|
82
|
+
|
83
|
+
When using with fork coin, please specify the fork_id of the coin as follows.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
Bitcoin.chain_params.fork_id = 0 # 0 is bch fork id
|
87
|
+
```
|
88
|
+
|
89
|
+
Currently bitcoinrb supports only support and verification of transaction replay protection using `SIGHASH_FORK_ID`.
|
90
|
+
For details of `SIGHASH_FORK_ID`, refer to the following.
|
91
|
+
|
92
|
+
https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md
|
54
93
|
|
55
94
|
## Contributing
|
56
95
|
|
data/bitcoinrb.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_runtime_dependency 'eventmachine_httpserver'
|
33
33
|
spec.add_runtime_dependency 'rest-client'
|
34
34
|
spec.add_runtime_dependency 'iniparse'
|
35
|
+
spec.add_runtime_dependency 'siphash'
|
35
36
|
|
36
37
|
spec.add_development_dependency 'bundler', '~> 1.11'
|
37
38
|
spec.add_development_dependency 'rake', '~> 10.0'
|
data/lib/bitcoin/base58.rb
CHANGED
@@ -3,7 +3,6 @@ module Bitcoin
|
|
3
3
|
# Base58Check encoding
|
4
4
|
# https://en.bitcoin.it/wiki/Base58Check_encoding
|
5
5
|
module Base58
|
6
|
-
|
7
6
|
module_function
|
8
7
|
|
9
8
|
ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
@@ -28,8 +27,7 @@ module Bitcoin
|
|
28
27
|
raise ArgumentError, 'Value passed not a valid Base58 String.' if (char_index = ALPHABET.index(char)).nil?
|
29
28
|
int_val += char_index * (SIZE**index)
|
30
29
|
end
|
31
|
-
s = int_val.
|
32
|
-
s = (s.bytesize.odd? ? '0' + s : s)
|
30
|
+
s = int_val.to_even_length_hex
|
33
31
|
s = '' if s == '00'
|
34
32
|
leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
|
35
33
|
s = ('00' * leading_zero_bytes) + s if leading_zero_bytes > 0
|
@@ -37,4 +35,4 @@ module Bitcoin
|
|
37
35
|
end
|
38
36
|
|
39
37
|
end
|
40
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "murmurhash3"
|
2
|
+
module Bitcoin
|
3
|
+
class BloomFilter
|
4
|
+
LN2_SQUARED = 0.4804530139182014246671025263266649717305529515945455 # log(2) ** 2
|
5
|
+
LN2 = 0.6931471805599453094172321214581765680755001343602552 # log(2)
|
6
|
+
|
7
|
+
MAX_BLOOM_FILTER_SIZE = 36_000 # bytes
|
8
|
+
MAX_HASH_FUNCS = 50
|
9
|
+
|
10
|
+
attr_reader :hash_funcs, :tweak
|
11
|
+
|
12
|
+
# @param [Integer] elements_length the number of elements
|
13
|
+
# @param [Float] fp_rate the false positive rate chosen by the client
|
14
|
+
# @param [Integer] tweak A random value to add to the seed value in the hash function used by the bloom filter
|
15
|
+
def initialize(elements_length, fp_rate, tweak=0)
|
16
|
+
# The size S of the filter in bytes is given by (-1 / pow(log(2), 2) * N * log(P)) / 8
|
17
|
+
len = [[(-elements_length * Math.log(fp_rate) / (LN2_SQUARED * 8)).to_i, MAX_BLOOM_FILTER_SIZE].min, 1].max
|
18
|
+
@filter = Array.new(len, 0)
|
19
|
+
# The number of hash functions required is given by S * 8 / N * log(2)
|
20
|
+
@hash_funcs = [[(@filter.size * 8 * LN2 / elements_length).to_i, MAX_HASH_FUNCS].min, 1].max
|
21
|
+
@tweak = tweak
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [String] data The data element to add to the current filter.
|
25
|
+
def add(data)
|
26
|
+
return if full?
|
27
|
+
@hash_funcs.times do |i|
|
28
|
+
hash = to_hash(data, i)
|
29
|
+
set_bit(hash)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns true if the given data matches the filter
|
34
|
+
# @param [String] data The data to check the current filter
|
35
|
+
# @return [Boolean] true if the given data matches the filter
|
36
|
+
def contains?(data)
|
37
|
+
return true if full?
|
38
|
+
@hash_funcs.times do |i|
|
39
|
+
hash = to_hash(data, i)
|
40
|
+
return false unless check_bit(hash)
|
41
|
+
end
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear
|
46
|
+
@filter.fill(0)
|
47
|
+
@full = false
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_a
|
51
|
+
@filter
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def to_hash(data, i)
|
56
|
+
MurmurHash3::V32.str_hash(data, (i * 0xfba4c795 + @tweak) & 0xffffffff) % (@filter.length * 8)
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_bit(data)
|
60
|
+
@filter[data >> 3] |= (1 << (7 & data))
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_bit(data)
|
64
|
+
@filter[data >> 3] & (1 << (7 & data)) != 0
|
65
|
+
end
|
66
|
+
|
67
|
+
def full?
|
68
|
+
@full |= @filter.all? {|byte| byte == 0xff}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/bitcoin/chain_params.rb
CHANGED
@@ -26,6 +26,9 @@ module Bitcoin
|
|
26
26
|
attr_reader :genesis
|
27
27
|
attr_reader :bip44_coin_type
|
28
28
|
|
29
|
+
# fork coin id.
|
30
|
+
attr_accessor :fork_id
|
31
|
+
|
29
32
|
# mainnet genesis
|
30
33
|
def self.mainnet
|
31
34
|
YAML.load(File.open("#{__dir__}/chainparams/mainnet.yml"))
|
@@ -60,6 +63,11 @@ module Bitcoin
|
|
60
63
|
Bitcoin::Block.new(header)
|
61
64
|
end
|
62
65
|
|
66
|
+
# whether fork coin.
|
67
|
+
def fork_chain?
|
68
|
+
!fork_id.nil?
|
69
|
+
end
|
70
|
+
|
63
71
|
end
|
64
72
|
|
65
73
|
end
|
data/lib/bitcoin/constants.rb
CHANGED
@@ -56,7 +56,7 @@ module Bitcoin
|
|
56
56
|
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
|
57
57
|
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
|
58
58
|
SCRIPT_VERIFY_LOW_S,
|
59
|
-
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM]
|
59
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM].inject(SCRIPT_VERIFY_NONE){|flags, f| flags |= f}
|
60
60
|
|
61
61
|
# for script
|
62
62
|
|
@@ -84,6 +84,13 @@ module Bitcoin
|
|
84
84
|
# Signature hash types/flags
|
85
85
|
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
86
86
|
|
87
|
+
# SIGHASH_FORK_ID for replay protection of the fork coin
|
88
|
+
SIGHASH_FORK_ID = 0x40
|
89
|
+
|
90
|
+
# fork coin id.
|
91
|
+
FORK_ID_CASH = 0
|
92
|
+
FORK_ID_GOLD = 79
|
93
|
+
|
87
94
|
# Maximum number length in bytes
|
88
95
|
DEFAULT_MAX_NUM_SIZE = 4
|
89
96
|
|
data/lib/bitcoin/ext_key.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
module Bitcoin
|
2
2
|
|
3
|
-
def self.hmac_sha512(key, data)
|
4
|
-
OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA512'), key, data)
|
5
|
-
end
|
6
|
-
|
7
3
|
# Integers modulo the order of the curve(secp256k1)
|
8
4
|
CURVE_ORDER = 115792089237316195423570985008687907852837564279074904382605163141518161494337
|
9
5
|
|
data/lib/bitcoin/key.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# Porting part of the code from bitcoin-ruby. see the license.
|
2
|
+
# https://github.com/lian/bitcoin-ruby/blob/master/COPYING
|
3
|
+
|
1
4
|
module Bitcoin
|
2
5
|
|
3
6
|
# bitcoin key class
|
@@ -64,13 +67,10 @@ module Bitcoin
|
|
64
67
|
end
|
65
68
|
|
66
69
|
# sign +data+ with private key
|
70
|
+
# @param [String] data a data to be signed with binary format
|
71
|
+
# @return [String] signature data with binary format
|
67
72
|
def sign(data)
|
68
|
-
|
69
|
-
until sig
|
70
|
-
signature = secp256k1_module.sign_data(data, priv_key)
|
71
|
-
sig = signature if Key.low_signature?(signature)
|
72
|
-
end
|
73
|
-
sig
|
73
|
+
secp256k1_module.sign_data(data, priv_key)
|
74
74
|
end
|
75
75
|
|
76
76
|
# verify signature using public key
|
@@ -78,7 +78,13 @@ module Bitcoin
|
|
78
78
|
# @param [String] origin original message
|
79
79
|
# @return [Boolean] verify result
|
80
80
|
def verify(sig, origin)
|
81
|
-
|
81
|
+
return false unless valid_pubkey?
|
82
|
+
begin
|
83
|
+
sig = ecdsa_signature_parse_der_lax(sig)
|
84
|
+
secp256k1_module.verify_sig(origin, sig, pubkey)
|
85
|
+
rescue Exception
|
86
|
+
false
|
87
|
+
end
|
82
88
|
end
|
83
89
|
|
84
90
|
# get pay to pubkey hash address
|
@@ -209,6 +215,24 @@ module Bitcoin
|
|
209
215
|
MIN_PRIV_KEy_MOD_ORDER <= value && value <= MAX_PRIV_KEY_MOD_ORDER
|
210
216
|
end
|
211
217
|
|
218
|
+
# Supported violations include negative integers, excessive padding, garbage
|
219
|
+
# at the end, and overly long length descriptors. This is safe to use in
|
220
|
+
# Bitcoin because since the activation of BIP66, signatures are verified to be
|
221
|
+
# strict DER before being passed to this module, and we know it supports all
|
222
|
+
# violations present in the blockchain before that point.
|
223
|
+
def ecdsa_signature_parse_der_lax(sig)
|
224
|
+
sig_array = sig.unpack('C*')
|
225
|
+
len_r = sig_array[3]
|
226
|
+
r = sig_array[4...(len_r+4)].pack('C*').bth
|
227
|
+
len_s = sig_array[len_r + 5]
|
228
|
+
s = sig_array[(len_r + 6)...(len_r + 6 + len_s)].pack('C*').bth
|
229
|
+
ECDSA::Format::SignatureDerString.encode(ECDSA::Signature.new(r.to_i(16), s.to_i(16)))
|
230
|
+
end
|
231
|
+
|
232
|
+
def valid_pubkey?
|
233
|
+
!pubkey.nil? && pubkey.size > 0
|
234
|
+
end
|
235
|
+
|
212
236
|
end
|
213
237
|
|
214
238
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# BIP-152 Compact Block's data format.
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#BlockTransactionsRequest
|
6
|
+
class BlockTransactionRequest
|
7
|
+
|
8
|
+
attr_accessor :block_hash # When matching with Bitcoin::BlockHeader#hash It is necessary to reverse the byte order.
|
9
|
+
attr_accessor :indexes
|
10
|
+
|
11
|
+
def initialize(block_hash, indexes)
|
12
|
+
@block_hash = block_hash
|
13
|
+
@indexes = indexes
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_from_payload(payload)
|
17
|
+
buf = StringIO.new(payload)
|
18
|
+
block_hash = buf.read(32).bth
|
19
|
+
index_len = Bitcoin.unpack_var_int_from_io(buf)
|
20
|
+
indexes = index_len.times.map{Bitcoin.unpack_var_int_from_io(buf)}
|
21
|
+
# index data differentially encoded
|
22
|
+
offset = 0
|
23
|
+
index_len.times do |i|
|
24
|
+
index = indexes[i]
|
25
|
+
index += offset
|
26
|
+
indexes[i] = index
|
27
|
+
offset = index + 1
|
28
|
+
end
|
29
|
+
self.new(block_hash, indexes)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_payload
|
33
|
+
p = block_hash.htb << Bitcoin.pack_var_int(indexes.size)
|
34
|
+
indexes.size.times do |i|
|
35
|
+
index = indexes[i]
|
36
|
+
index -= indexes[i-1] + 1 if i > 0
|
37
|
+
p << Bitcoin.pack_var_int(index)
|
38
|
+
end
|
39
|
+
p
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# BIP-152 Compact Block's data format.
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#BlockTransactions
|
6
|
+
class BlockTransactions
|
7
|
+
|
8
|
+
attr_accessor :block_hash
|
9
|
+
attr_accessor :transactions
|
10
|
+
|
11
|
+
def initialize(block_hash, transactions)
|
12
|
+
@block_hash = block_hash
|
13
|
+
@transactions = transactions
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_from_payload(payload)
|
17
|
+
buf = StringIO.new(payload)
|
18
|
+
block_hash = buf.read(32).bth
|
19
|
+
tx_count = Bitcoin.unpack_var_int_from_io(buf)
|
20
|
+
txn = tx_count.times.map{Bitcoin::Tx.parse_from_payload(buf)}
|
21
|
+
self.new(block_hash, txn)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_payload
|
25
|
+
block_hash.htb << Bitcoin.pack_var_int(transactions.size) << transactions.map(&:to_payload).join
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# blocktxn message.
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#blocktxn
|
6
|
+
class BlockTxn < Base
|
7
|
+
|
8
|
+
COMMAND = 'blocktxn'
|
9
|
+
|
10
|
+
attr_accessor :block_transactions
|
11
|
+
|
12
|
+
def initialize(block_transactions)
|
13
|
+
@block_transactions = block_transactions
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_from_payload(payload)
|
17
|
+
self.new(BlockTransactions.parse_from_payload(payload))
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_payload
|
21
|
+
block_transactions.to_payload
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# cmpctblock message
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
|
6
|
+
class CmpctBlock < Base
|
7
|
+
|
8
|
+
COMMAND = 'cmpctblock'
|
9
|
+
|
10
|
+
attr_accessor :header_and_short_ids
|
11
|
+
|
12
|
+
def initialize(header_and_short_ids)
|
13
|
+
@header_and_short_ids = header_and_short_ids
|
14
|
+
end
|
15
|
+
|
16
|
+
# generate CmpctBlock from Block data.
|
17
|
+
# @param [Bitcoin::Block] block the block to generate CmpctBlock.
|
18
|
+
# @param [Integer] version Compact Block version specified by sendcmpct message.
|
19
|
+
# @param [Integer] nonce
|
20
|
+
# @return [Bitcoin::Message::CmpctBlock]
|
21
|
+
def self.from_block(block, version, nonce = SecureRandom.hex(8).to_i(16))
|
22
|
+
raise 'Unsupported version.' unless [1, 2].include?(version)
|
23
|
+
h = HeaderAndShortIDs.new(block.header, nonce)
|
24
|
+
block.transactions[1..-1].each do |tx|
|
25
|
+
h.short_ids << h.short_id(version == 1 ? tx.txid : tx.wtxid)
|
26
|
+
end
|
27
|
+
h.prefilled_txn << PrefilledTx.new(0, block.transactions.first)
|
28
|
+
self.new(h)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse_from_payload(payload)
|
32
|
+
self.new(HeaderAndShortIDs.parse_from_payload(payload))
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_payload
|
36
|
+
header_and_short_ids.to_payload
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# getblocktxn message.
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
|
6
|
+
class GetBlockTxn < Base
|
7
|
+
|
8
|
+
COMMAND = 'getblocktxn'
|
9
|
+
|
10
|
+
attr_accessor :request
|
11
|
+
|
12
|
+
def initialize(request)
|
13
|
+
@request = request
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_from_payload(payload)
|
17
|
+
self.new(BlockTransactionRequest.parse_from_payload(payload))
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_payload
|
21
|
+
request.to_payload
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'siphash'
|
2
|
+
module Bitcoin
|
3
|
+
module Message
|
4
|
+
|
5
|
+
# BIP-152 Compact Block's data format.
|
6
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#HeaderAndShortIDs
|
7
|
+
class HeaderAndShortIDs
|
8
|
+
attr_accessor :header
|
9
|
+
attr_accessor :nonce
|
10
|
+
attr_accessor :short_ids
|
11
|
+
attr_accessor :prefilled_txn
|
12
|
+
attr_accessor :siphash_key
|
13
|
+
|
14
|
+
def initialize(header, nonce, short_ids = [], prefilled_txn = [])
|
15
|
+
@header = header
|
16
|
+
@nonce = nonce
|
17
|
+
@short_ids = short_ids
|
18
|
+
@prefilled_txn = prefilled_txn
|
19
|
+
@siphash_key = Bitcoin.sha256(header.to_payload << [nonce].pack('q*'))[0...16]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse_from_payload(payload)
|
23
|
+
buf = StringIO.new(payload)
|
24
|
+
header = Bitcoin::BlockHeader.parse_from_payload(buf.read(80))
|
25
|
+
nonce = buf.read(8).unpack('q*').first
|
26
|
+
short_ids_len = Bitcoin.unpack_var_int_from_io(buf)
|
27
|
+
short_ids = short_ids_len.times.map do
|
28
|
+
buf.read(6).reverse.bth.to_i(16)
|
29
|
+
end
|
30
|
+
prefilled_txn_len = Bitcoin.unpack_var_int_from_io(buf)
|
31
|
+
prefilled_txn = prefilled_txn_len.times.map do
|
32
|
+
PrefilledTx.parse_from_io(buf)
|
33
|
+
end
|
34
|
+
self.new(header, nonce, short_ids, prefilled_txn)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_payload
|
38
|
+
p = header.to_payload
|
39
|
+
p << [nonce].pack('q*')
|
40
|
+
p << Bitcoin.pack_var_int(short_ids.size)
|
41
|
+
p << short_ids.map{|id|sprintf('%12x', id).htb.reverse}.join
|
42
|
+
p << Bitcoin.pack_var_int(prefilled_txn.size)
|
43
|
+
p << prefilled_txn.map(&:to_payload).join
|
44
|
+
p
|
45
|
+
end
|
46
|
+
|
47
|
+
# calculate short transaction id which specified by BIP-152.
|
48
|
+
# @param [String] txid a transaction id
|
49
|
+
# @return [Integer] 6 bytes short transaction id.
|
50
|
+
def short_id(txid)
|
51
|
+
hash = SipHash.digest(siphash_key, txid.htb.reverse).to_even_length_hex
|
52
|
+
[hash].pack('H*')[2...8].bth.to_i(16)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# A PrefilledTransaction structure is used in HeaderAndShortIDs to provide a list of a few transactions explicitly.
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
|
6
|
+
class PrefilledTx
|
7
|
+
|
8
|
+
attr_accessor :index
|
9
|
+
attr_accessor :tx
|
10
|
+
|
11
|
+
def initialize(index, tx)
|
12
|
+
@index = index
|
13
|
+
@tx = tx
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_from_io(io)
|
17
|
+
index = Bitcoin.unpack_var_int_from_io(io)
|
18
|
+
tx = Bitcoin::Tx.parse_from_payload(io)
|
19
|
+
self.new(index, tx)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_payload
|
23
|
+
Bitcoin.pack_var_int(index) << tx.to_payload
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -27,8 +27,8 @@ module Bitcoin
|
|
27
27
|
@nonce = SecureRandom.random_number(0xffffffffffffffff)
|
28
28
|
@user_agent = Bitcoin::Message::USER_AGENT
|
29
29
|
@start_height = 0
|
30
|
-
@relay = true
|
31
30
|
opts.each { |k, v| send "#{k}=", v }
|
31
|
+
@relay = opts[:relay] || false
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.parse_from_payload(payload)
|
data/lib/bitcoin/message.rb
CHANGED
@@ -30,6 +30,13 @@ module Bitcoin
|
|
30
30
|
autoload :Error, 'bitcoin/message/error'
|
31
31
|
autoload :Reject, 'bitcoin/message/reject'
|
32
32
|
autoload :SendCmpct, 'bitcoin/message/send_cmpct'
|
33
|
+
autoload :CmpctBlock, 'bitcoin/message/cmpct_block'
|
34
|
+
autoload :HeaderAndShortIDs, 'bitcoin/message/header_and_short_ids'
|
35
|
+
autoload :PrefilledTx, 'bitcoin/message/prefilled_tx'
|
36
|
+
autoload :GetBlockTxn, 'bitcoin/message/get_block_txn'
|
37
|
+
autoload :BlockTransactionRequest, 'bitcoin/message/block_transaction_request'
|
38
|
+
autoload :BlockTxn, 'bitcoin/message/block_txn'
|
39
|
+
autoload :BlockTransactions, 'bitcoin/message/block_transactions'
|
33
40
|
|
34
41
|
USER_AGENT = "/bitcoinrb:#{Bitcoin::VERSION}/"
|
35
42
|
|
@@ -92,8 +92,10 @@ module Bitcoin
|
|
92
92
|
on_inv(Bitcoin::Message::Inv.parse_from_payload(payload))
|
93
93
|
when Bitcoin::Message::MerkleBlock::COMMAND
|
94
94
|
on_merkle_block(Bitcoin::Message::MerkleBlock.parse_from_payload(payload))
|
95
|
+
when Bitcoin::Message::CmpctBlock::COMMAND
|
96
|
+
on_cmpct_block(Bitcoin::Message::CmpctBlock.parse_from_payload(payload))
|
95
97
|
else
|
96
|
-
logger.warn("unsupported command received. #{command}")
|
98
|
+
logger.warn("unsupported command received. command: #{command}, payload: #{payload.bth}")
|
97
99
|
close("with command #{command}")
|
98
100
|
end
|
99
101
|
end
|
@@ -224,6 +226,10 @@ module Bitcoin
|
|
224
226
|
logger.info("receive merkle block message. #{merkle_block.build_json}")
|
225
227
|
end
|
226
228
|
|
229
|
+
def on_cmpct_block(cmpct_block)
|
230
|
+
logger.info("receive cmpct_block message. #{cmpct_block.build_json}")
|
231
|
+
end
|
232
|
+
|
227
233
|
end
|
228
234
|
end
|
229
235
|
end
|
data/lib/bitcoin/network/peer.rb
CHANGED
@@ -5,7 +5,7 @@ module Bitcoin
|
|
5
5
|
class Peer
|
6
6
|
|
7
7
|
# Interval for pinging peers.
|
8
|
-
PING_INTERVAL =
|
8
|
+
PING_INTERVAL = 2 * 60
|
9
9
|
|
10
10
|
attr_reader :logger
|
11
11
|
attr_accessor :id
|
@@ -34,7 +34,7 @@ module Bitcoin
|
|
34
34
|
attr_reader :chain
|
35
35
|
attr_accessor :fee_rate
|
36
36
|
|
37
|
-
def initialize(host, port, pool)
|
37
|
+
def initialize(host, port, pool, configuration)
|
38
38
|
@host = host
|
39
39
|
@port = port
|
40
40
|
@pool = pool
|
@@ -48,8 +48,9 @@ module Bitcoin
|
|
48
48
|
@min_ping = -1
|
49
49
|
@bytes_sent = 0
|
50
50
|
@bytes_recv = 0
|
51
|
+
@relay = configuration.conf[:relay]
|
51
52
|
current_height = @chain.latest_block.height
|
52
|
-
@local_version = Bitcoin::Message::Version.new(remote_addr: addr, start_height: current_height)
|
53
|
+
@local_version = Bitcoin::Message::Version.new(remote_addr: addr, start_height: current_height, relay: @relay)
|
53
54
|
end
|
54
55
|
|
55
56
|
def connect
|
@@ -186,7 +187,28 @@ module Bitcoin
|
|
186
187
|
conn.send_message(ping)
|
187
188
|
end
|
188
189
|
|
189
|
-
|
190
|
+
# send filterload message.
|
191
|
+
def send_filter_load(bloom)
|
192
|
+
filter_load = Bitcoin::Message::FilterLoad.new(
|
193
|
+
bloom.to_a,
|
194
|
+
bloom.hash_funcs,
|
195
|
+
bloom.tweak,
|
196
|
+
Bitcoin::Message::FilterLoad::BLOOM_UPDATE_ALL
|
197
|
+
)
|
198
|
+
conn.send_message(filter_load)
|
199
|
+
end
|
200
|
+
|
201
|
+
# send filteradd message.
|
202
|
+
def send_filter_add(element)
|
203
|
+
filter_add = Bitcoin::Message::FilterAdd.new(element)
|
204
|
+
conn.send_message(filter_add)
|
205
|
+
end
|
190
206
|
|
207
|
+
# send filterclear message.
|
208
|
+
def send_filter_clear
|
209
|
+
filter_clear = Bitcoin::Message::FilterClear.new
|
210
|
+
conn.send_message(filter_clear)
|
211
|
+
end
|
212
|
+
end
|
191
213
|
end
|
192
214
|
end
|