tapyrus 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|