tapyrus 0.1.0
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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/tapyrusrb-cli +5 -0
- data/exe/tapyrusrbd +41 -0
- data/lib/openassets/marker_output.rb +20 -0
- data/lib/openassets/payload.rb +54 -0
- data/lib/openassets/util.rb +28 -0
- data/lib/openassets.rb +9 -0
- data/lib/tapyrus/base58.rb +38 -0
- data/lib/tapyrus/block.rb +77 -0
- data/lib/tapyrus/block_header.rb +88 -0
- data/lib/tapyrus/bloom_filter.rb +78 -0
- data/lib/tapyrus/chain_params.rb +90 -0
- data/lib/tapyrus/chainparams/mainnet.yml +41 -0
- data/lib/tapyrus/chainparams/regtest.yml +38 -0
- data/lib/tapyrus/chainparams/testnet.yml +41 -0
- data/lib/tapyrus/constants.rb +195 -0
- data/lib/tapyrus/descriptor.rb +147 -0
- data/lib/tapyrus/ext_key.rb +337 -0
- data/lib/tapyrus/key.rb +296 -0
- data/lib/tapyrus/key_path.rb +26 -0
- data/lib/tapyrus/logger.rb +42 -0
- data/lib/tapyrus/merkle_tree.rb +149 -0
- data/lib/tapyrus/message/addr.rb +35 -0
- data/lib/tapyrus/message/base.rb +28 -0
- data/lib/tapyrus/message/block.rb +46 -0
- data/lib/tapyrus/message/block_transaction_request.rb +45 -0
- data/lib/tapyrus/message/block_transactions.rb +31 -0
- data/lib/tapyrus/message/block_txn.rb +27 -0
- data/lib/tapyrus/message/cmpct_block.rb +42 -0
- data/lib/tapyrus/message/error.rb +10 -0
- data/lib/tapyrus/message/fee_filter.rb +27 -0
- data/lib/tapyrus/message/filter_add.rb +28 -0
- data/lib/tapyrus/message/filter_clear.rb +17 -0
- data/lib/tapyrus/message/filter_load.rb +39 -0
- data/lib/tapyrus/message/get_addr.rb +17 -0
- data/lib/tapyrus/message/get_block_txn.rb +27 -0
- data/lib/tapyrus/message/get_blocks.rb +29 -0
- data/lib/tapyrus/message/get_data.rb +21 -0
- data/lib/tapyrus/message/get_headers.rb +28 -0
- data/lib/tapyrus/message/header_and_short_ids.rb +57 -0
- data/lib/tapyrus/message/headers.rb +35 -0
- data/lib/tapyrus/message/headers_parser.rb +24 -0
- data/lib/tapyrus/message/inv.rb +21 -0
- data/lib/tapyrus/message/inventories_parser.rb +23 -0
- data/lib/tapyrus/message/inventory.rb +51 -0
- data/lib/tapyrus/message/mem_pool.rb +17 -0
- data/lib/tapyrus/message/merkle_block.rb +42 -0
- data/lib/tapyrus/message/network_addr.rb +63 -0
- data/lib/tapyrus/message/not_found.rb +21 -0
- data/lib/tapyrus/message/ping.rb +30 -0
- data/lib/tapyrus/message/pong.rb +26 -0
- data/lib/tapyrus/message/prefilled_tx.rb +29 -0
- data/lib/tapyrus/message/reject.rb +46 -0
- data/lib/tapyrus/message/send_cmpct.rb +43 -0
- data/lib/tapyrus/message/send_headers.rb +16 -0
- data/lib/tapyrus/message/tx.rb +30 -0
- data/lib/tapyrus/message/ver_ack.rb +17 -0
- data/lib/tapyrus/message/version.rb +69 -0
- data/lib/tapyrus/message.rb +70 -0
- data/lib/tapyrus/mnemonic/wordlist/chinese_simplified.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/chinese_traditional.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/english.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/french.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/italian.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/japanese.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/spanish.txt +2048 -0
- data/lib/tapyrus/mnemonic.rb +77 -0
- data/lib/tapyrus/network/connection.rb +73 -0
- data/lib/tapyrus/network/message_handler.rb +241 -0
- data/lib/tapyrus/network/peer.rb +223 -0
- data/lib/tapyrus/network/peer_discovery.rb +42 -0
- data/lib/tapyrus/network/pool.rb +135 -0
- data/lib/tapyrus/network.rb +13 -0
- data/lib/tapyrus/node/cli.rb +112 -0
- data/lib/tapyrus/node/configuration.rb +38 -0
- data/lib/tapyrus/node/spv.rb +79 -0
- data/lib/tapyrus/node.rb +7 -0
- data/lib/tapyrus/opcodes.rb +178 -0
- data/lib/tapyrus/out_point.rb +44 -0
- data/lib/tapyrus/rpc/http_server.rb +65 -0
- data/lib/tapyrus/rpc/request_handler.rb +150 -0
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +72 -0
- data/lib/tapyrus/rpc.rb +7 -0
- data/lib/tapyrus/script/multisig.rb +92 -0
- data/lib/tapyrus/script/script.rb +551 -0
- data/lib/tapyrus/script/script_error.rb +111 -0
- data/lib/tapyrus/script/script_interpreter.rb +668 -0
- data/lib/tapyrus/script/tx_checker.rb +81 -0
- data/lib/tapyrus/script_witness.rb +38 -0
- data/lib/tapyrus/secp256k1/native.rb +174 -0
- data/lib/tapyrus/secp256k1/ruby.rb +123 -0
- data/lib/tapyrus/secp256k1.rb +12 -0
- data/lib/tapyrus/slip39/share.rb +122 -0
- data/lib/tapyrus/slip39/sss.rb +245 -0
- data/lib/tapyrus/slip39/wordlist/english.txt +1024 -0
- data/lib/tapyrus/slip39.rb +93 -0
- data/lib/tapyrus/store/chain_entry.rb +67 -0
- data/lib/tapyrus/store/db/level_db.rb +98 -0
- data/lib/tapyrus/store/db.rb +9 -0
- data/lib/tapyrus/store/spv_chain.rb +101 -0
- data/lib/tapyrus/store.rb +9 -0
- data/lib/tapyrus/tx.rb +347 -0
- data/lib/tapyrus/tx_in.rb +89 -0
- data/lib/tapyrus/tx_out.rb +74 -0
- data/lib/tapyrus/util.rb +133 -0
- data/lib/tapyrus/validation.rb +115 -0
- data/lib/tapyrus/version.rb +3 -0
- data/lib/tapyrus/wallet/account.rb +151 -0
- data/lib/tapyrus/wallet/base.rb +162 -0
- data/lib/tapyrus/wallet/db.rb +81 -0
- data/lib/tapyrus/wallet/master_key.rb +110 -0
- data/lib/tapyrus/wallet.rb +8 -0
- data/lib/tapyrus.rb +219 -0
- data/tapyrusrb.conf.sample +0 -0
- data/tapyrusrb.gemspec +47 -0
- metadata +451 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'siphash'
|
|
2
|
+
module Tapyrus
|
|
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 = Tapyrus.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 = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
|
|
25
|
+
nonce = buf.read(8).unpack('q*').first
|
|
26
|
+
short_ids_len = Tapyrus.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 = Tapyrus.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 << Tapyrus.pack_var_int(short_ids.size)
|
|
41
|
+
p << short_ids.map{|id|sprintf('%12x', id).htb.reverse}.join
|
|
42
|
+
p << Tapyrus.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,35 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# headers message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#headers
|
|
6
|
+
class Headers < Base
|
|
7
|
+
|
|
8
|
+
COMMAND = 'headers'
|
|
9
|
+
|
|
10
|
+
# Array[Tapyrus::BlockHeader]
|
|
11
|
+
attr_accessor :headers
|
|
12
|
+
|
|
13
|
+
def initialize(headers = [])
|
|
14
|
+
@headers = headers
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.parse_from_payload(payload)
|
|
18
|
+
buf = StringIO.new(payload)
|
|
19
|
+
header_count = Tapyrus.unpack_var_int_from_io(buf)
|
|
20
|
+
h = new
|
|
21
|
+
header_count.times do
|
|
22
|
+
h.headers << Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
|
|
23
|
+
buf.read(1) # read tx count 0x00 (headers message doesn't include any tx.)
|
|
24
|
+
end
|
|
25
|
+
h
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_payload
|
|
29
|
+
Tapyrus.pack_var_int(headers.size) << headers.map { |h| h.to_payload << 0x00 }.join
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# Common message parser which handle multiple block headers as a payload.
|
|
5
|
+
module HeadersParser
|
|
6
|
+
|
|
7
|
+
def parse_from_payload(payload)
|
|
8
|
+
ver, payload = payload.unpack('Va*')
|
|
9
|
+
size, payload = Tapyrus.unpack_var_int(payload)
|
|
10
|
+
hashes = []
|
|
11
|
+
buf = StringIO.new(payload)
|
|
12
|
+
size.times do
|
|
13
|
+
hashes << buf.read(32).bth
|
|
14
|
+
end
|
|
15
|
+
new(ver, hashes, buf.read(32).bth)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_payload
|
|
19
|
+
[version].pack('V') << Tapyrus.pack_var_int(hashes.length) << hashes.map{|h|h.htb}.join << stop_hash.htb
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# inv message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#inv
|
|
6
|
+
class Inv < Base
|
|
7
|
+
include InventoriesParser
|
|
8
|
+
extend InventoriesParser
|
|
9
|
+
|
|
10
|
+
COMMAND = 'inv'
|
|
11
|
+
|
|
12
|
+
attr_reader :inventories
|
|
13
|
+
|
|
14
|
+
def initialize(inventories = [])
|
|
15
|
+
@inventories = inventories
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# Common message parser which only handle multiple inventory as payload.
|
|
5
|
+
module InventoriesParser
|
|
6
|
+
|
|
7
|
+
def parse_from_payload(payload)
|
|
8
|
+
size, payload = Tapyrus.unpack_var_int(payload)
|
|
9
|
+
buf = StringIO.new(payload)
|
|
10
|
+
i = new
|
|
11
|
+
size.times do
|
|
12
|
+
i.inventories << Inventory.parse_from_payload(buf.read(36))
|
|
13
|
+
end
|
|
14
|
+
i
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_payload
|
|
18
|
+
Tapyrus.pack_var_int(inventories.length) << inventories.map(&:to_payload).join
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
# inventory class. inventory is a part of message.
|
|
4
|
+
# https://bitcoin.org/en/developer-reference#term-inventory
|
|
5
|
+
class Inventory
|
|
6
|
+
|
|
7
|
+
SEGWIT_FLAG = 1 << 30
|
|
8
|
+
|
|
9
|
+
MSG_TX = 1
|
|
10
|
+
MSG_BLOCK = 2
|
|
11
|
+
MSG_FILTERED_BLOCK = 3
|
|
12
|
+
MSG_CMPCT_BLOCK = 4
|
|
13
|
+
MSG_WITNESS_TX = SEGWIT_FLAG | MSG_TX
|
|
14
|
+
MSG_WITNESS_BLOCK = SEGWIT_FLAG | MSG_BLOCK
|
|
15
|
+
MSG_FILTERED_WITNESS_BLOCK = SEGWIT_FLAG | MSG_FILTERED_BLOCK
|
|
16
|
+
|
|
17
|
+
attr_accessor :identifier
|
|
18
|
+
attr_accessor :hash
|
|
19
|
+
|
|
20
|
+
def initialize(identifier, hash)
|
|
21
|
+
raise Error, "invalid type identifier specified. identifier = #{identifier}" unless valid_identifier?(identifier)
|
|
22
|
+
@identifier = identifier
|
|
23
|
+
@hash = hash
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# parse inventory payload
|
|
27
|
+
def self.parse_from_payload(payload)
|
|
28
|
+
raise Error, 'invalid inventory size.' if payload.bytesize != 36
|
|
29
|
+
identifier = payload[0..4].unpack('V').first
|
|
30
|
+
hash = payload[4..-1].bth # internal byte order
|
|
31
|
+
new(identifier, hash)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_payload
|
|
35
|
+
[identifier].pack('V') << hash.htb
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def block?
|
|
39
|
+
[MSG_BLOCK, MSG_WITNESS_BLOCK, MSG_FILTERED_WITNESS_BLOCK].include?(identifier)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def valid_identifier?(identifier)
|
|
45
|
+
[MSG_TX, MSG_BLOCK, MSG_FILTERED_BLOCK, MSG_CMPCT_BLOCK, MSG_WITNESS_TX,
|
|
46
|
+
MSG_WITNESS_BLOCK, MSG_FILTERED_WITNESS_BLOCK].include?(identifier)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# merckleblock message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#merkleblock
|
|
6
|
+
class MerkleBlock < Base
|
|
7
|
+
|
|
8
|
+
COMMAND = 'merkleblock'
|
|
9
|
+
|
|
10
|
+
attr_accessor :header
|
|
11
|
+
attr_accessor :tx_count
|
|
12
|
+
attr_accessor :hashes
|
|
13
|
+
attr_accessor :flags
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@hashes = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.parse_from_payload(payload)
|
|
20
|
+
m = new
|
|
21
|
+
buf = StringIO.new(payload)
|
|
22
|
+
m.header = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
|
|
23
|
+
m.tx_count = buf.read(4).unpack('V').first
|
|
24
|
+
hash_count = Tapyrus.unpack_var_int_from_io(buf)
|
|
25
|
+
hash_count.times do
|
|
26
|
+
m.hashes << buf.read(32).bth
|
|
27
|
+
end
|
|
28
|
+
flag_count = Tapyrus.unpack_var_int_from_io(buf)
|
|
29
|
+
# A sequence of bits packed eight in a byte with the least significant bit first.
|
|
30
|
+
m.flags = buf.read(flag_count).bth
|
|
31
|
+
m
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_payload
|
|
35
|
+
header.to_payload << [tx_count].pack('V') << Tapyrus.pack_var_int(hashes.size) <<
|
|
36
|
+
hashes.map(&:htb).join << Tapyrus.pack_var_int(flags.htb.bytesize) << flags.htb
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'ipaddr'
|
|
2
|
+
|
|
3
|
+
module Tapyrus
|
|
4
|
+
module Message
|
|
5
|
+
|
|
6
|
+
class NetworkAddr
|
|
7
|
+
|
|
8
|
+
# unix time.
|
|
9
|
+
# Nodes advertising their own IP address set this to the current time.
|
|
10
|
+
# Nodes advertising IP addresses they’ve connected to set this to the last time they connected to that node.
|
|
11
|
+
# Other nodes just relaying the IP address should not change the time. Nodes can use the time field to avoid relaying old addr messages.
|
|
12
|
+
attr_accessor :time
|
|
13
|
+
|
|
14
|
+
# The services the node advertised in its version message.
|
|
15
|
+
attr_accessor :services
|
|
16
|
+
|
|
17
|
+
attr_accessor :ip_addr # IPAddr
|
|
18
|
+
|
|
19
|
+
attr_accessor :port
|
|
20
|
+
|
|
21
|
+
attr_reader :skip_time
|
|
22
|
+
|
|
23
|
+
def initialize(ip: '127.0.0.1', port: Tapyrus.chain_params.default_port, services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i)
|
|
24
|
+
@time = time
|
|
25
|
+
@ip_addr = IPAddr.new(ip)
|
|
26
|
+
@port = port
|
|
27
|
+
@services = services
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.parse_from_payload(payload)
|
|
31
|
+
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
|
32
|
+
has_time = buf.size > 26
|
|
33
|
+
addr = new(time: nil)
|
|
34
|
+
addr.time = buf.read(4).unpack('V').first if has_time
|
|
35
|
+
addr.services = buf.read(8).unpack('Q').first
|
|
36
|
+
addr.ip_addr = IPAddr::new_ntoh(buf.read(16))
|
|
37
|
+
addr.port = buf.read(2).unpack('n').first
|
|
38
|
+
addr
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.local_addr
|
|
42
|
+
addr = new
|
|
43
|
+
addr.ip_addr = IPAddr.new('127.0.0.1')
|
|
44
|
+
addr.port = Tapyrus.chain_params.default_port
|
|
45
|
+
addr.services = DEFAULT_SERVICE_FLAGS
|
|
46
|
+
addr
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ip
|
|
50
|
+
ip_addr.ipv4_mapped? ? ip_addr.native : ip_addr.to_s
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_payload(skip_time = false)
|
|
54
|
+
p = ''
|
|
55
|
+
p << [time].pack('V') unless skip_time
|
|
56
|
+
addr = ip_addr.ipv4? ? ip_addr.ipv4_mapped : ip_addr
|
|
57
|
+
p << [services].pack('Q') << addr.hton << [port].pack('n')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# notfound message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#notfound
|
|
6
|
+
class NotFound < Base
|
|
7
|
+
include InventoriesParser
|
|
8
|
+
extend InventoriesParser
|
|
9
|
+
|
|
10
|
+
attr_reader :inventories
|
|
11
|
+
|
|
12
|
+
COMMAND = 'notfound'
|
|
13
|
+
|
|
14
|
+
def initialize(inventories = [])
|
|
15
|
+
@inventories = inventories
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# ping message class
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#ping
|
|
6
|
+
class Ping < Base
|
|
7
|
+
|
|
8
|
+
COMMAND = 'ping'
|
|
9
|
+
|
|
10
|
+
attr_accessor :nonce
|
|
11
|
+
|
|
12
|
+
def initialize(nonce = SecureRandom.random_number(0xffffffff))
|
|
13
|
+
@nonce = nonce
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.parse_from_payload(payload)
|
|
17
|
+
new(payload.unpack('Q').first)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_payload
|
|
21
|
+
[nonce].pack('Q')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_response
|
|
25
|
+
Pong.new(nonce)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# pong message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#pong
|
|
6
|
+
class Pong < Base
|
|
7
|
+
|
|
8
|
+
COMMAND = 'pong'
|
|
9
|
+
|
|
10
|
+
attr_reader :nonce
|
|
11
|
+
|
|
12
|
+
def initialize(nonce)
|
|
13
|
+
@nonce = nonce
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.parse_from_payload(payload)
|
|
17
|
+
new(payload.unpack('Q').first)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_payload
|
|
21
|
+
[nonce].pack('Q')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Tapyrus
|
|
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 = Tapyrus.unpack_var_int_from_io(io)
|
|
18
|
+
tx = Tapyrus::Tx.parse_from_payload(io)
|
|
19
|
+
self.new(index, tx)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_payload
|
|
23
|
+
Tapyrus.pack_var_int(index) << tx.to_payload
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# reject message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#reject
|
|
6
|
+
class Reject < Base
|
|
7
|
+
|
|
8
|
+
attr_accessor :message
|
|
9
|
+
attr_accessor :code
|
|
10
|
+
attr_accessor :reason
|
|
11
|
+
attr_accessor :extra
|
|
12
|
+
|
|
13
|
+
COMMAND = 'reject'
|
|
14
|
+
|
|
15
|
+
CODE_MALFORMED = 0x01
|
|
16
|
+
CODE_INVALID = 0x10
|
|
17
|
+
CODE_OBSOLETE = 0x11
|
|
18
|
+
CODE_DUPLICATE = 0x12
|
|
19
|
+
CODE_NONSTANDARD = 0x40
|
|
20
|
+
CODE_DUST = 0x41
|
|
21
|
+
CODE_INSUFFICIENT_FEE = 0x42
|
|
22
|
+
CODE_CHECKPOINT = 0x43
|
|
23
|
+
|
|
24
|
+
def initialize(message, code, reason = '', extra = '')
|
|
25
|
+
@message = message
|
|
26
|
+
@code = code
|
|
27
|
+
@reason = reason
|
|
28
|
+
@extra = extra
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.parse_from_payload(payload)
|
|
32
|
+
message, payload = Tapyrus.unpack_var_string(payload)
|
|
33
|
+
code, payload = payload.unpack('Ca*')
|
|
34
|
+
reason, payload = Tapyrus.unpack_var_string(payload)
|
|
35
|
+
extra = ['tx', 'block'].include?(message) ? payload.bth : payload
|
|
36
|
+
new(message, code, reason, extra)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_payload
|
|
40
|
+
e = ['tx', 'block'].include?(message) ? extra.htb : extra
|
|
41
|
+
Tapyrus.pack_var_string(message) << [code].pack('C') << Tapyrus.pack_var_string(reason) << e
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# sendcmpct message
|
|
5
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
|
|
6
|
+
class SendCmpct < Base
|
|
7
|
+
|
|
8
|
+
COMMAND = 'sendcmpct'
|
|
9
|
+
|
|
10
|
+
MODE_HIGH = 1
|
|
11
|
+
MODE_LOW = 0
|
|
12
|
+
|
|
13
|
+
attr_accessor :mode
|
|
14
|
+
attr_accessor :version
|
|
15
|
+
# TODO support version 2
|
|
16
|
+
|
|
17
|
+
def initialize(mode = MODE_HIGH, version = 1)
|
|
18
|
+
@mode = mode
|
|
19
|
+
@version = version
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.parse_from_payload(payload)
|
|
23
|
+
buf = StringIO.new(payload)
|
|
24
|
+
mode = buf.read(1).unpack('c').first
|
|
25
|
+
version = buf.read(8).unpack('Q').first
|
|
26
|
+
new(mode, version)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_payload
|
|
30
|
+
[mode, version].pack('cQ')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def high?
|
|
34
|
+
mode == 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def low?
|
|
38
|
+
mode.zero?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module Message
|
|
3
|
+
|
|
4
|
+
# tx message
|
|
5
|
+
# https://bitcoin.org/en/developer-reference#tx
|
|
6
|
+
class Tx < Base
|
|
7
|
+
|
|
8
|
+
COMMAND = 'tx'
|
|
9
|
+
|
|
10
|
+
attr_accessor :tx
|
|
11
|
+
attr_accessor :use_segwit
|
|
12
|
+
|
|
13
|
+
def initialize(tx, use_segwit = false)
|
|
14
|
+
@tx = tx
|
|
15
|
+
@use_segwit = use_segwit
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.parse_from_payload(payload)
|
|
19
|
+
tx = Tapyrus::Tx.parse_from_payload(payload)
|
|
20
|
+
new(tx, tx.witness?)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_payload
|
|
24
|
+
use_segwit ? tx.to_payload : tx.serialize_old_format
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
require 'ipaddr'
|
|
3
|
+
module Tapyrus
|
|
4
|
+
module Message
|
|
5
|
+
|
|
6
|
+
# https://bitcoin.org/en/developer-reference#version
|
|
7
|
+
class Version < Base
|
|
8
|
+
|
|
9
|
+
COMMAND = 'version'
|
|
10
|
+
|
|
11
|
+
attr_accessor :version
|
|
12
|
+
attr_accessor :services
|
|
13
|
+
attr_accessor :timestamp
|
|
14
|
+
attr_accessor :local_addr
|
|
15
|
+
attr_accessor :remote_addr
|
|
16
|
+
attr_accessor :nonce
|
|
17
|
+
attr_accessor :user_agent
|
|
18
|
+
attr_accessor :start_height
|
|
19
|
+
attr_accessor :relay
|
|
20
|
+
|
|
21
|
+
def initialize(opts = {})
|
|
22
|
+
@version = Tapyrus.chain_params.protocol_version
|
|
23
|
+
@services = DEFAULT_SERVICE_FLAGS
|
|
24
|
+
@timestamp = Time.now.to_i
|
|
25
|
+
@local_addr = NetworkAddr.local_addr
|
|
26
|
+
@remote_addr = NetworkAddr.local_addr
|
|
27
|
+
@nonce = SecureRandom.random_number(0xffffffffffffffff)
|
|
28
|
+
@user_agent = Tapyrus::Message::USER_AGENT
|
|
29
|
+
@start_height = 0
|
|
30
|
+
opts.each { |k, v| send "#{k}=", v }
|
|
31
|
+
@relay = opts[:relay] || false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.parse_from_payload(payload)
|
|
35
|
+
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack('VQQa26a26Qa*')
|
|
36
|
+
v = new
|
|
37
|
+
v.version = version
|
|
38
|
+
v.services = services
|
|
39
|
+
v.timestamp = timestamp
|
|
40
|
+
v.local_addr = NetworkAddr.parse_from_payload(local_addr)
|
|
41
|
+
v.remote_addr = NetworkAddr.parse_from_payload(remote_addr)
|
|
42
|
+
v.nonce = nonce
|
|
43
|
+
user_agent, rest = unpack_var_string(rest)
|
|
44
|
+
start_height, rest = rest.unpack('Va*')
|
|
45
|
+
v.user_agent = user_agent
|
|
46
|
+
v.start_height = start_height
|
|
47
|
+
v.relay = v.unpack_relay_field(rest).first
|
|
48
|
+
v
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_payload
|
|
52
|
+
[
|
|
53
|
+
[version, services, timestamp].pack('VQQ'),
|
|
54
|
+
local_addr.to_payload(true),
|
|
55
|
+
remote_addr.to_payload(true),
|
|
56
|
+
[nonce].pack('Q'),
|
|
57
|
+
pack_var_string(user_agent),
|
|
58
|
+
[start_height].pack('V'),
|
|
59
|
+
pack_boolean(relay)
|
|
60
|
+
].join
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def unpack_relay_field(payload)
|
|
64
|
+
( version >= 70001 && payload ) ? unpack_boolean(payload) : [ true, nil ]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|