bitcoinrb 0.1.5 → 0.1.6
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 +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
|