bitcoinrb 0.0.1
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 +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bitcoinrb.gemspec +32 -0
- data/exe/bitcoinrb-cli +5 -0
- data/exe/bitcoinrbd +49 -0
- data/lib/bitcoin.rb +121 -0
- data/lib/bitcoin/base58.rb +40 -0
- data/lib/bitcoin/block_header.rb +41 -0
- data/lib/bitcoin/chain_params.rb +57 -0
- data/lib/bitcoin/chainparams/mainnet.yml +25 -0
- data/lib/bitcoin/chainparams/regtest.yml +20 -0
- data/lib/bitcoin/chainparams/testnet.yml +24 -0
- data/lib/bitcoin/connection.rb +66 -0
- data/lib/bitcoin/ext_key.rb +205 -0
- data/lib/bitcoin/key.rb +131 -0
- data/lib/bitcoin/logger.rb +18 -0
- data/lib/bitcoin/merkle_tree.rb +120 -0
- data/lib/bitcoin/message.rb +42 -0
- data/lib/bitcoin/message/addr.rb +74 -0
- data/lib/bitcoin/message/base.rb +40 -0
- data/lib/bitcoin/message/block.rb +41 -0
- data/lib/bitcoin/message/error.rb +10 -0
- data/lib/bitcoin/message/fee_filter.rb +27 -0
- data/lib/bitcoin/message/filter_add.rb +28 -0
- data/lib/bitcoin/message/filter_clear.rb +17 -0
- data/lib/bitcoin/message/filter_load.rb +43 -0
- data/lib/bitcoin/message/get_addr.rb +17 -0
- data/lib/bitcoin/message/get_blocks.rb +29 -0
- data/lib/bitcoin/message/get_data.rb +21 -0
- data/lib/bitcoin/message/get_headers.rb +28 -0
- data/lib/bitcoin/message/handler.rb +170 -0
- data/lib/bitcoin/message/headers.rb +34 -0
- data/lib/bitcoin/message/headers_parser.rb +24 -0
- data/lib/bitcoin/message/inv.rb +21 -0
- data/lib/bitcoin/message/inventories_parser.rb +23 -0
- data/lib/bitcoin/message/inventory.rb +47 -0
- data/lib/bitcoin/message/mem_pool.rb +17 -0
- data/lib/bitcoin/message/merkle_block.rb +42 -0
- data/lib/bitcoin/message/not_found.rb +29 -0
- data/lib/bitcoin/message/ping.rb +30 -0
- data/lib/bitcoin/message/pong.rb +26 -0
- data/lib/bitcoin/message/reject.rb +46 -0
- data/lib/bitcoin/message/send_cmpct.rb +43 -0
- data/lib/bitcoin/message/send_headers.rb +16 -0
- data/lib/bitcoin/message/tx.rb +30 -0
- data/lib/bitcoin/message/ver_ack.rb +17 -0
- data/lib/bitcoin/message/version.rb +79 -0
- data/lib/bitcoin/mnemonic.rb +76 -0
- data/lib/bitcoin/mnemonic/wordlist/chinese_simplified.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/chinese_traditional.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/english.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/french.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/italian.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/japanese.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/spanish.txt +2048 -0
- data/lib/bitcoin/nodes.rb +5 -0
- data/lib/bitcoin/nodes/spv.rb +13 -0
- data/lib/bitcoin/nodes/spv/cli.rb +12 -0
- data/lib/bitcoin/nodes/spv/daemon.rb +21 -0
- data/lib/bitcoin/opcodes.rb +172 -0
- data/lib/bitcoin/out_point.rb +31 -0
- data/lib/bitcoin/script/script.rb +347 -0
- data/lib/bitcoin/script/script_error.rb +168 -0
- data/lib/bitcoin/script/script_interpreter.rb +694 -0
- data/lib/bitcoin/script/tx_checker.rb +44 -0
- data/lib/bitcoin/script_witness.rb +29 -0
- data/lib/bitcoin/secp256k1.rb +10 -0
- data/lib/bitcoin/secp256k1/native.rb +22 -0
- data/lib/bitcoin/secp256k1/ruby.rb +96 -0
- data/lib/bitcoin/tx.rb +191 -0
- data/lib/bitcoin/tx_in.rb +45 -0
- data/lib/bitcoin/tx_out.rb +32 -0
- data/lib/bitcoin/util.rb +105 -0
- data/lib/bitcoin/version.rb +3 -0
- metadata +256 -0
data/lib/bitcoin/key.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# bitcoin key class
|
4
|
+
class Key
|
5
|
+
|
6
|
+
attr_accessor :priv_key
|
7
|
+
attr_accessor :pubkey
|
8
|
+
attr_accessor :compressed
|
9
|
+
|
10
|
+
def initialize(priv_key: nil, pubkey: nil, compressed: true)
|
11
|
+
extend Bitcoin.secp_impl
|
12
|
+
@priv_key = priv_key
|
13
|
+
if pubkey
|
14
|
+
@pubkey = pubkey
|
15
|
+
else
|
16
|
+
@pubkey = generate_pubkey(priv_key, compressed: compressed) if priv_key
|
17
|
+
end
|
18
|
+
@compressed = compressed
|
19
|
+
end
|
20
|
+
|
21
|
+
# import private key from wif format
|
22
|
+
# https://en.bitcoin.it/wiki/Wallet_import_format
|
23
|
+
def self.from_wif(wif)
|
24
|
+
compressed = wif.size == 52
|
25
|
+
hex = Base58.decode(wif)
|
26
|
+
version, key, flag, checksum = hex.unpack("a2a64a#{compressed ? 2 : 0}a8")
|
27
|
+
raise ArgumentError, 'invalid version' unless version == Bitcoin.chain_params.privkey_version
|
28
|
+
raise ArgumentError, 'invalid checksum' unless Bitcoin.calc_checksum(version + key + flag) == checksum
|
29
|
+
new(priv_key: key, compressed: compressed)
|
30
|
+
end
|
31
|
+
|
32
|
+
# export private key with wif format
|
33
|
+
def to_wif
|
34
|
+
version = Bitcoin.chain_params.privkey_version
|
35
|
+
hex = version + priv_key
|
36
|
+
hex += '01' if compressed?
|
37
|
+
hex += Bitcoin.calc_checksum(hex)
|
38
|
+
Base58.encode(hex)
|
39
|
+
end
|
40
|
+
|
41
|
+
# sign +data+ with private key
|
42
|
+
def sign(data)
|
43
|
+
sign_data(data, priv_key)
|
44
|
+
end
|
45
|
+
|
46
|
+
# verify signature using public key
|
47
|
+
# @param [String] sig signature data with binary format
|
48
|
+
# @param [String] origin original message
|
49
|
+
# @return [Boolean] verify result
|
50
|
+
def verify(sig, origin)
|
51
|
+
verify_sig(origin, sig, pubkey)
|
52
|
+
end
|
53
|
+
|
54
|
+
# get pay to pubkey hash address
|
55
|
+
def to_p2pkh
|
56
|
+
Bitcoin::Script.to_p2pkh(Bitcoin.hash160(pubkey)).to_addr
|
57
|
+
end
|
58
|
+
|
59
|
+
# get pay to witness pubkey hash address
|
60
|
+
def to_p2wpkh
|
61
|
+
Bitcoin::Script.to_p2wpkh(Bitcoin.hash160(pubkey)).to_addr
|
62
|
+
end
|
63
|
+
|
64
|
+
def compressed?
|
65
|
+
@compressed
|
66
|
+
end
|
67
|
+
|
68
|
+
# generate pubkey ec point
|
69
|
+
# @return [ECDSA::Point]
|
70
|
+
def to_point
|
71
|
+
p = pubkey
|
72
|
+
p ||= generate_pubkey(priv_key, compressed: compressed)
|
73
|
+
ECDSA::Format::PointOctetString.decode(p.htb, Bitcoin::Secp256k1::GROUP)
|
74
|
+
end
|
75
|
+
|
76
|
+
# check +pubkey+ (hex) is compress or uncompress pubkey.
|
77
|
+
def self.compress_or_uncompress_pubkey?(pubkey)
|
78
|
+
p = pubkey.htb
|
79
|
+
return false if p.bytesize < 33
|
80
|
+
case p[0]
|
81
|
+
when "\x04"
|
82
|
+
return false unless p.bytesize == 65
|
83
|
+
when "\x02", "\x03"
|
84
|
+
return false unless p.bytesize == 33
|
85
|
+
else
|
86
|
+
return false
|
87
|
+
end
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
# check +pubkey+ (hex) is compress pubkey.
|
92
|
+
def self.compress_pubkey?(pubkey)
|
93
|
+
p = pubkey.htb
|
94
|
+
p.bytesize == 33 && ["\x02", "\x03"].include?(p[0])
|
95
|
+
end
|
96
|
+
|
97
|
+
# check +sig+ is low.
|
98
|
+
def self.low_signature?(sig)
|
99
|
+
s = sig.unpack('C*')
|
100
|
+
len_r = s[3]
|
101
|
+
len_s = s[5 + len_r]
|
102
|
+
val_s = s.slice(6 + len_r, len_s)
|
103
|
+
max_mod_half_order = [
|
104
|
+
0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
105
|
+
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
106
|
+
0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,
|
107
|
+
0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0]
|
108
|
+
compare_big_endian(val_s, [0]) > 0 &&
|
109
|
+
compare_big_endian(val_s, max_mod_half_order) <= 0
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def self.compare_big_endian(c1, c2)
|
115
|
+
c1, c2 = c1.dup, c2.dup # Clone the arrays
|
116
|
+
|
117
|
+
while c1.size > c2.size
|
118
|
+
return 1 if c1.shift > 0
|
119
|
+
end
|
120
|
+
|
121
|
+
while c2.size > c1.size
|
122
|
+
return -1 if c2.shift > 0
|
123
|
+
end
|
124
|
+
|
125
|
+
c1.size.times{|idx| return c1[idx] - c2[idx] if c1[idx] != c2[idx] }
|
126
|
+
0
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
|
5
|
+
# Simple Logger module
|
6
|
+
module Logger
|
7
|
+
|
8
|
+
# Create a logger with given +name+.log in $HOME/.bitcoinrb/log.
|
9
|
+
def self.create(name, level = ::Logger::INFO)
|
10
|
+
dir = "#{Bitcoin.base_dir}/log"
|
11
|
+
FileUtils.mkdir_p(dir)
|
12
|
+
logger = ::Logger.new(dir + "/#{name}.log", 10)
|
13
|
+
logger.level = level
|
14
|
+
logger
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# merkle tree
|
4
|
+
class MerkleTree
|
5
|
+
|
6
|
+
attr_accessor :root
|
7
|
+
|
8
|
+
def initialize(root = nil)
|
9
|
+
@root = root
|
10
|
+
end
|
11
|
+
|
12
|
+
def merkle_root
|
13
|
+
root.hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.build_from_leaf(txids)
|
17
|
+
nodes = txids.each_slice(2).map{ |m|
|
18
|
+
left = Node.new(m[0])
|
19
|
+
right = Node.new(m[1] ? m[1] : m[0])
|
20
|
+
[left, right]
|
21
|
+
}.flatten
|
22
|
+
new(build_initial_tree(nodes))
|
23
|
+
end
|
24
|
+
|
25
|
+
# https://bitcoin.org/en/developer-reference#creating-a-merkleblock-message
|
26
|
+
def self.build_partial(tx_count, hashes, flags)
|
27
|
+
flags = flags.each_char.map(&:to_i)
|
28
|
+
root = build_initial_tree( Array.new(tx_count) { Node.new })
|
29
|
+
current_node = root
|
30
|
+
hash_index = 0
|
31
|
+
flags.each do |f|
|
32
|
+
current_node.flag = f
|
33
|
+
if f.zero? || current_node.leaf?
|
34
|
+
current_node.hash = hashes[hash_index]
|
35
|
+
hash_index += 1
|
36
|
+
end
|
37
|
+
current_node = current_node.next_partial
|
38
|
+
break if hash_index == hashes.size
|
39
|
+
end
|
40
|
+
new(root)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.build_initial_tree(nodes)
|
44
|
+
while nodes.size != 1
|
45
|
+
nodes = nodes.each_slice(2).map { |m|
|
46
|
+
parent = Node.new
|
47
|
+
parent.left = m[0]
|
48
|
+
parent.right = m[1] ? m[1] : m[0].dup
|
49
|
+
parent
|
50
|
+
}
|
51
|
+
end
|
52
|
+
nodes.first
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# node of merkle tree
|
58
|
+
class Node
|
59
|
+
|
60
|
+
attr_accessor :flag
|
61
|
+
attr_accessor :hash
|
62
|
+
attr_accessor :parent
|
63
|
+
attr_accessor :left
|
64
|
+
attr_accessor :right
|
65
|
+
|
66
|
+
def initialize(hash = nil)
|
67
|
+
@hash = hash
|
68
|
+
end
|
69
|
+
|
70
|
+
def left=(node)
|
71
|
+
node.parent = self
|
72
|
+
@left = node
|
73
|
+
end
|
74
|
+
|
75
|
+
def right=(node)
|
76
|
+
node.parent = self
|
77
|
+
@right = node
|
78
|
+
end
|
79
|
+
|
80
|
+
def hash
|
81
|
+
return @hash if @hash
|
82
|
+
self.right = left.dup unless right
|
83
|
+
Digest::SHA256.digest(Digest::SHA256.digest(
|
84
|
+
[right.hash + left.hash].pack('H*').reverse )).reverse.bth
|
85
|
+
end
|
86
|
+
|
87
|
+
def root?
|
88
|
+
parent.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
def leaf?
|
92
|
+
right.nil? && left.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
def partial?
|
96
|
+
!flag.nil?
|
97
|
+
end
|
98
|
+
|
99
|
+
def next_partial
|
100
|
+
return nil if root? && (flag.zero? || (left.partial? && right.partial?))
|
101
|
+
return parent.next_partial if flag.zero? || leaf?
|
102
|
+
return left unless left.partial?
|
103
|
+
self.right = left.dup unless right
|
104
|
+
right.partial? ? parent.next_partial : right
|
105
|
+
end
|
106
|
+
|
107
|
+
# calculate the depth of this node in the tree.
|
108
|
+
def depth
|
109
|
+
d = 0
|
110
|
+
current_node = self
|
111
|
+
until current_node.root? do
|
112
|
+
current_node = current_node.parent
|
113
|
+
d += 1
|
114
|
+
end
|
115
|
+
d
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
autoload :Handler, 'bitcoin/message/handler'
|
5
|
+
autoload :Base, 'bitcoin/message/base'
|
6
|
+
autoload :Inventory, 'bitcoin/message/inventory'
|
7
|
+
autoload :InventoriesParser, 'bitcoin/message/inventories_parser'
|
8
|
+
autoload :HeadersParser, 'bitcoin/message/headers_parser'
|
9
|
+
autoload :Version, 'bitcoin/message/version'
|
10
|
+
autoload :VerAck, 'bitcoin/message/ver_ack'
|
11
|
+
autoload :Addr, 'bitcoin/message/addr'
|
12
|
+
autoload :Block, 'bitcoin/message/block'
|
13
|
+
autoload :FilterLoad, 'bitcoin/message/filter_load'
|
14
|
+
autoload :FilterAdd, 'bitcoin/message/filter_add'
|
15
|
+
autoload :FilterClear, 'bitcoin/message/filter_clear'
|
16
|
+
autoload :MerkleBlock, 'bitcoin/message/merkle_block'
|
17
|
+
autoload :Tx, 'bitcoin/message/tx'
|
18
|
+
autoload :Ping, 'bitcoin/message/ping'
|
19
|
+
autoload :Pong, 'bitcoin/message/pong'
|
20
|
+
autoload :Inv, 'bitcoin/message/inv'
|
21
|
+
autoload :GetBlocks, 'bitcoin/message/get_blocks'
|
22
|
+
autoload :GetHeaders, 'bitcoin/message/get_headers'
|
23
|
+
autoload :Headers, 'bitcoin/message/headers'
|
24
|
+
autoload :GetAddr, 'bitcoin/message/get_addr'
|
25
|
+
autoload :GetData, 'bitcoin/message/get_data'
|
26
|
+
autoload :SendHeaders, 'bitcoin/message/send_headers'
|
27
|
+
autoload :FeeFilter, 'bitcoin/message/fee_filter'
|
28
|
+
autoload :MemPool, 'bitcoin/message/mem_pool'
|
29
|
+
autoload :NotFound, 'bitcoin/message/not_found'
|
30
|
+
autoload :Error, 'bitcoin/message/error'
|
31
|
+
autoload :Reject, 'bitcoin/message/reject'
|
32
|
+
autoload :SendCmpct, 'bitcoin/message/send_cmpct'
|
33
|
+
|
34
|
+
HEADER_SIZE = 24
|
35
|
+
USER_AGENT = "/bitcoinrb:#{Bitcoin::VERSION}/"
|
36
|
+
|
37
|
+
SERVICE_FLAGS = {none: 0, network: 1 << 0, getutxo: 1 << 1, bloom: 1 << 2, witness: 1 << 3, xthin: 1 << 4}
|
38
|
+
|
39
|
+
DEFAULT_STOP_HASH = "00"*32
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
module Message
|
5
|
+
|
6
|
+
# addr message
|
7
|
+
# https://bitcoin.org/en/developer-reference#addr
|
8
|
+
class Addr < Base
|
9
|
+
|
10
|
+
COMMAND = 'addr'
|
11
|
+
|
12
|
+
attr_reader :addrs
|
13
|
+
|
14
|
+
def initialize(addrs = [])
|
15
|
+
@addrs = addrs
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.parse_from_payload(payload)
|
19
|
+
buf = StringIO.new(payload)
|
20
|
+
addr_count = Bitcoin.unpack_var_int_from_io(buf)
|
21
|
+
addr = new
|
22
|
+
addr_count.times do
|
23
|
+
addr.addrs << NetworkAddr.parse_from_payload(buf)
|
24
|
+
end
|
25
|
+
addr
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_payload
|
29
|
+
Bitcoin.pack_var_int(addrs.length) << addrs.map(&:to_payload).join
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class NetworkAddr
|
35
|
+
|
36
|
+
# unix time.
|
37
|
+
# Nodes advertising their own IP address set this to the current time.
|
38
|
+
# Nodes advertising IP addresses they’ve connected to set this to the last time they connected to that node.
|
39
|
+
# Other nodes just relaying the IP address should not change the time. Nodes can use the time field to avoid relaying old addr messages.
|
40
|
+
attr_accessor :time
|
41
|
+
|
42
|
+
# The services the node advertised in its version message.
|
43
|
+
attr_accessor :services
|
44
|
+
|
45
|
+
attr_accessor :ip
|
46
|
+
|
47
|
+
attr_accessor :port
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@time = Time.now.to_i
|
51
|
+
@services = Bitcoin::Message::SERVICE_FLAGS[:network]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.parse_from_payload(payload)
|
55
|
+
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
56
|
+
addr = new
|
57
|
+
addr.time = buf.read(4).unpack('V').first
|
58
|
+
addr.services = buf.read(8).unpack('Q').first
|
59
|
+
ip = IPAddr::new_ntoh(buf.read(16))
|
60
|
+
addr.ip = ip.ipv4_mapped? ? ip.native : ip.to_s
|
61
|
+
addr.port = buf.read(2).unpack('n').first
|
62
|
+
addr
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_payload
|
66
|
+
ip_addr = IPAddr.new (ip)
|
67
|
+
ip_addr = ip_addr.ipv4_mapped if ip_addr.ipv4?
|
68
|
+
[time, services].pack('VQ') << ip_addr.hton << [port].pack('n')
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# Base message class
|
5
|
+
class Base
|
6
|
+
include Bitcoin::Util
|
7
|
+
extend Bitcoin::Util
|
8
|
+
|
9
|
+
# generate message header (binary format)
|
10
|
+
# https://bitcoin.org/en/developer-reference#message-headers
|
11
|
+
def to_pkt
|
12
|
+
payload = to_payload
|
13
|
+
magic = Bitcoin.chain_params.magic_head.htb
|
14
|
+
command_name = self.class.const_get(:COMMAND, false).ljust(12, "\x00")
|
15
|
+
payload_size = [payload.bytesize].pack('V')
|
16
|
+
checksum = Bitcoin.double_sha256(payload)[0...4]
|
17
|
+
magic << command_name << payload_size << checksum << payload
|
18
|
+
end
|
19
|
+
|
20
|
+
# abstract method
|
21
|
+
def to_payload
|
22
|
+
raise 'to_payload must be implemented in a child class.'
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json
|
26
|
+
to_h.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h
|
30
|
+
instance_variables.inject({}) do |result, var|
|
31
|
+
key = var.to_s
|
32
|
+
key.slice!(0) if key.start_with?('@')
|
33
|
+
result.update(key => instance_variable_get(var))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Message
|
3
|
+
|
4
|
+
# block message
|
5
|
+
# https://bitcoin.org/en/developer-reference#block
|
6
|
+
class Block < Base
|
7
|
+
|
8
|
+
attr_accessor :header
|
9
|
+
attr_accessor :transactions
|
10
|
+
attr_accessor :use_segwit
|
11
|
+
|
12
|
+
COMMAND = 'block'
|
13
|
+
|
14
|
+
def initialize(header, transactions = [], use_segwit = false)
|
15
|
+
@header = header
|
16
|
+
@transactions = transactions
|
17
|
+
@use_segwit = use_segwit
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_from_payload(payload)
|
21
|
+
buf = StringIO.new(payload)
|
22
|
+
header = Bitcoin::BlockHeader.parse_from_payload(buf.read(80))
|
23
|
+
transactions = []
|
24
|
+
unless buf.eof?
|
25
|
+
tx_count = Bitcoin.unpack_var_int_from_io(buf)
|
26
|
+
tx_count.times do
|
27
|
+
transactions << Bitcoin::Tx.parse_from_payload(buf)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
new(header, transactions)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_payload
|
34
|
+
header.to_payload << Bitcoin.pack_var_int(transactions.size) <<
|
35
|
+
transactions.map{|t|use_segwit ? t.to_payload : t.serialize_old_format}.join
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|