bitcoinrb 0.1.1 → 0.1.2
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 +4 -4
- data/.gitignore +1 -0
- data/bitcoinrb.gemspec +7 -3
- data/exe/bitcoinrb-cli +1 -1
- data/exe/bitcoinrbd +9 -23
- data/lib/bitcoin/block.rb +66 -0
- data/lib/bitcoin/block_header.rb +33 -1
- data/lib/bitcoin/chain_params.rb +11 -4
- data/lib/bitcoin/chainparams/mainnet.yml +9 -2
- data/lib/bitcoin/chainparams/regtest.yml +9 -1
- data/lib/bitcoin/chainparams/testnet.yml +9 -1
- data/lib/bitcoin/constants.rb +13 -0
- data/lib/bitcoin/ext_key.rb +2 -1
- data/lib/bitcoin/merkle_tree.rb +50 -51
- data/lib/bitcoin/message/block.rb +5 -0
- data/lib/bitcoin/message.rb +5 -5
- data/lib/bitcoin/network/connection.rb +58 -0
- data/lib/bitcoin/network/message_handler.rb +192 -0
- data/lib/bitcoin/network/peer.rb +55 -0
- data/lib/bitcoin/network/peer_discovery.rb +34 -0
- data/lib/bitcoin/network/pool.rb +74 -0
- data/lib/bitcoin/network.rb +14 -0
- data/lib/bitcoin/node/spv.rb +39 -0
- data/lib/bitcoin/node/spv_block_chain.rb +15 -0
- data/lib/bitcoin/node.rb +6 -0
- data/lib/bitcoin/script/script.rb +13 -0
- data/lib/bitcoin/store/spv_block_store.rb +11 -0
- data/lib/bitcoin/store.rb +7 -0
- data/lib/bitcoin/tx.rb +11 -0
- data/lib/bitcoin/tx_out.rb +2 -0
- data/lib/bitcoin/validation.rb +33 -11
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin.rb +5 -2
- data/lib/openassets/marker_output.rb +14 -0
- data/lib/openassets/payload.rb +45 -0
- data/lib/openassets.rb +8 -0
- metadata +59 -8
- data/lib/bitcoin/connection.rb +0 -68
- data/lib/bitcoin/message/handler.rb +0 -183
- data/lib/bitcoin/nodes/spv/cli.rb +0 -12
- data/lib/bitcoin/nodes/spv/daemon.rb +0 -21
- data/lib/bitcoin/nodes/spv.rb +0 -13
- data/lib/bitcoin/nodes.rb +0 -5
@@ -0,0 +1,192 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Network
|
3
|
+
|
4
|
+
# P2P message handler used by peer connection class.
|
5
|
+
module MessageHandler
|
6
|
+
|
7
|
+
# handle p2p message.
|
8
|
+
def handle(message)
|
9
|
+
logger.info "handle message #{message.bth}"
|
10
|
+
begin
|
11
|
+
parse(message)
|
12
|
+
rescue Bitcoin::Message::Error => e
|
13
|
+
logger.error("invalid header magic. #{e.message}")
|
14
|
+
close
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(message)
|
19
|
+
@message += message
|
20
|
+
command, payload, rest = parse_header
|
21
|
+
return unless command
|
22
|
+
|
23
|
+
handle_command(command, payload)
|
24
|
+
@message = ""
|
25
|
+
parse(rest) if rest && rest.bytesize > 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_header
|
29
|
+
head_magic = Bitcoin.chain_params.magic_head
|
30
|
+
return if @message.nil? || @message.size < MESSAGE_HEADER_SIZE
|
31
|
+
|
32
|
+
magic, command, length, checksum = @message.unpack('a4A12Va4')
|
33
|
+
raise Bitcoin::Message::Error, "invalid header magic. #{magic.bth}" unless magic.bth == head_magic
|
34
|
+
|
35
|
+
payload = @message[MESSAGE_HEADER_SIZE...(MESSAGE_HEADER_SIZE + length)]
|
36
|
+
return if payload.size < length
|
37
|
+
raise Bitcoin::Message::Error, "header checksum mismatch. #{checksum.bth}" unless Bitcoin.double_sha256(payload)[0...4] == checksum
|
38
|
+
|
39
|
+
rest = @message[(MESSAGE_HEADER_SIZE + length)..-1]
|
40
|
+
[command, payload, rest]
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_command(command, payload)
|
44
|
+
logger.info("[#{addr}] process command #{command}. payload = #{payload.bth}")
|
45
|
+
case command
|
46
|
+
when Bitcoin::Message::Version::COMMAND
|
47
|
+
on_version(Bitcoin::Message::Version.parse_from_payload(payload))
|
48
|
+
when Bitcoin::Message::VerAck::COMMAND
|
49
|
+
on_ver_ack
|
50
|
+
when Bitcoin::Message::GetAddr::COMMAND
|
51
|
+
on_get_addr
|
52
|
+
when Bitcoin::Message::Addr::COMMAND
|
53
|
+
on_addr(Bitcoin::Message::Addr.parse_from_payload(payload))
|
54
|
+
when Bitcoin::Message::SendHeaders::COMMAND
|
55
|
+
on_send_headers
|
56
|
+
when Bitcoin::Message::FeeFilter::COMMAND
|
57
|
+
on_fee_filter(Bitcoin::Message::FeeFilter.parse_from_payload(payload))
|
58
|
+
when Bitcoin::Message::Ping::COMMAND
|
59
|
+
on_ping(Bitcoin::Message::Ping.parse_from_payload(payload))
|
60
|
+
when Bitcoin::Message::Pong::COMMAND
|
61
|
+
on_pong(Bitcoin::Message::Pong.parse_from_payload(payload))
|
62
|
+
when Bitcoin::Message::GetHeaders::COMMAND
|
63
|
+
on_get_headers(Bitcoin::Message::GetHeaders.parse_from_payload(payload))
|
64
|
+
when Bitcoin::Message::Headers::COMMAND
|
65
|
+
on_headers(Bitcoin::Message::Headers.parse_from_payload(payload))
|
66
|
+
when Bitcoin::Message::Block::COMMAND
|
67
|
+
on_block(Bitcoin::Message::Block.parse_from_payload(payload))
|
68
|
+
when Bitcoin::Message::Tx::COMMAND
|
69
|
+
on_tx(Bitcoin::Message::Tx.parse_from_payload(payload))
|
70
|
+
when Bitcoin::Message::NotFound::COMMAND
|
71
|
+
on_not_found(Bitcoin::Message::NotFound.parse_from_payload(payload))
|
72
|
+
when Bitcoin::Message::MemPool::COMMAND
|
73
|
+
on_mem_pool
|
74
|
+
when Bitcoin::Message::Reject::COMMAND
|
75
|
+
on_reject(Bitcoin::Message::Reject.parse_from_payload(payload))
|
76
|
+
when Bitcoin::Message::SendCmpct::COMMAND
|
77
|
+
on_send_cmpct(Bitcoin::Message::SendCmpct.parse_from_payload(payload))
|
78
|
+
when Bitcoin::Message::Inv::COMMAND
|
79
|
+
on_inv(Bitcoin::Message::Inv.parse_from_payload(payload))
|
80
|
+
else
|
81
|
+
logger.warn("unsupported command received. #{command}")
|
82
|
+
close("with command #{command}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def send_message(msg)
|
87
|
+
logger.info "send message #{msg.class::COMMAND}"
|
88
|
+
send_data(msg.to_pkt)
|
89
|
+
end
|
90
|
+
|
91
|
+
# start handshake
|
92
|
+
def begin_handshake
|
93
|
+
logger.info "begin handshake with #{addr}"
|
94
|
+
ver = Bitcoin::Message::Version.new(remote_addr: addr, start_height: 0) # TODO use start_height in wallet
|
95
|
+
send_message(ver)
|
96
|
+
end
|
97
|
+
|
98
|
+
def handshake_done
|
99
|
+
logger.info 'handshake finished.'
|
100
|
+
@connected = true
|
101
|
+
post_handshake
|
102
|
+
end
|
103
|
+
|
104
|
+
def on_version(version)
|
105
|
+
logger.info("receive version message. #{version.build_json}")
|
106
|
+
@version = version
|
107
|
+
send_message(Bitcoin::Message::VerAck.new)
|
108
|
+
end
|
109
|
+
|
110
|
+
def on_ver_ack
|
111
|
+
logger.info('receive verack message.')
|
112
|
+
handshake_done
|
113
|
+
end
|
114
|
+
|
115
|
+
def on_get_addr
|
116
|
+
logger.info('receive getaddr message.')
|
117
|
+
end
|
118
|
+
|
119
|
+
def on_addr(addr)
|
120
|
+
logger.info("receive addr message. #{addr.build_json}")
|
121
|
+
# TODO
|
122
|
+
end
|
123
|
+
|
124
|
+
def on_send_headers
|
125
|
+
logger.info('receive sendheaders message.')
|
126
|
+
@sendheaders = true
|
127
|
+
end
|
128
|
+
|
129
|
+
def on_fee_filter(fee_filter)
|
130
|
+
logger.info("receive feefilter message. #{fee_filter.build_json}")
|
131
|
+
@fee_rate = fee_filter.fee_rate
|
132
|
+
end
|
133
|
+
|
134
|
+
def on_ping(ping)
|
135
|
+
logger.info("receive ping message. #{ping.build_json}")
|
136
|
+
send_message(ping.to_response)
|
137
|
+
end
|
138
|
+
|
139
|
+
def on_pong(pong)
|
140
|
+
logger.info("receive pong message. #{pong.build_json}")
|
141
|
+
# TODO calculate response
|
142
|
+
end
|
143
|
+
|
144
|
+
def on_get_headers(headers)
|
145
|
+
logger.info("receive getheaders message. #{headers.build_json}")
|
146
|
+
# TODO
|
147
|
+
end
|
148
|
+
|
149
|
+
def on_headers(headers)
|
150
|
+
logger.info("receive headers message. #{headers.build_json}")
|
151
|
+
# TODO
|
152
|
+
end
|
153
|
+
|
154
|
+
def on_block(block)
|
155
|
+
logger.info("receive block message. #{block.build_json}")
|
156
|
+
# TODO
|
157
|
+
end
|
158
|
+
|
159
|
+
def on_tx(tx)
|
160
|
+
logger.info("receive tx message. #{tx.build_json}")
|
161
|
+
# TODO
|
162
|
+
end
|
163
|
+
|
164
|
+
def on_not_found(not_found)
|
165
|
+
logger.info("receive notfound message. #{not_found.build_json}")
|
166
|
+
# TODO
|
167
|
+
end
|
168
|
+
|
169
|
+
def on_mem_pool
|
170
|
+
logger.info('receive mempool message.')
|
171
|
+
# TODO return mempool tx
|
172
|
+
end
|
173
|
+
|
174
|
+
def on_reject(reject)
|
175
|
+
logger.warn("receive reject message. #{reject.build_json}")
|
176
|
+
# TODO
|
177
|
+
end
|
178
|
+
|
179
|
+
def on_send_cmpct(cmpct)
|
180
|
+
logger.info("receive sendcmpct message. #{cmpct.build_json}")
|
181
|
+
# TODO if mode is high and version is 1, relay block with cmpctblock message
|
182
|
+
end
|
183
|
+
|
184
|
+
def on_inv(inv)
|
185
|
+
logger.info("receive inv message. #{inv.build_json}")
|
186
|
+
# TODO
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Network
|
3
|
+
|
4
|
+
# remote peer class.
|
5
|
+
class Peer
|
6
|
+
|
7
|
+
# remote peer info
|
8
|
+
attr_reader :host
|
9
|
+
attr_reader :port
|
10
|
+
# remote peer connection
|
11
|
+
attr_accessor :conn
|
12
|
+
attr_accessor :connected
|
13
|
+
# parent pool
|
14
|
+
attr_reader :pool
|
15
|
+
attr_accessor :fee_rate
|
16
|
+
|
17
|
+
def initialize(host, port, pool)
|
18
|
+
@host = host
|
19
|
+
@port = port
|
20
|
+
@pool = pool
|
21
|
+
@connected = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect
|
25
|
+
self.conn ||= EM.connect(host, port, Bitcoin::Network::Connection, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def connected?
|
29
|
+
@connected
|
30
|
+
end
|
31
|
+
|
32
|
+
def addr
|
33
|
+
"#{host}:#{port}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def post_handshake
|
37
|
+
@connected = true
|
38
|
+
pool.handle_new_peer(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
# broadcast tx.
|
42
|
+
def broadcast_tx(tx)
|
43
|
+
send_message(Bitcoin::Message::Tx.new(tx, ))
|
44
|
+
end
|
45
|
+
|
46
|
+
# check the remote peer support segwit.
|
47
|
+
def support_segwit?
|
48
|
+
return false unless conn.version
|
49
|
+
conn.version.services & Bitcoin::Message::SERVICE_FLAGS[:witness] > 0
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Network
|
3
|
+
|
4
|
+
class PeerDiscovery
|
5
|
+
|
6
|
+
attr_reader :logger
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@logger = Bitcoin::Logger.create(:debug)
|
10
|
+
end
|
11
|
+
|
12
|
+
# get peer addresses, from DNS seeds.
|
13
|
+
def peers
|
14
|
+
# TODO add find from previous connected peer at first.
|
15
|
+
find_from_dns_seeds
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def find_from_dns_seeds
|
21
|
+
logger.debug 'discover peer address from DNS seeds.'
|
22
|
+
Bitcoin.chain_params.dns_seeds.map {|seed|
|
23
|
+
begin
|
24
|
+
Socket.getaddrinfo(seed, Bitcoin.chain_params.default_port).map{|a|a[2]}.uniq
|
25
|
+
rescue SocketError => e
|
26
|
+
logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
|
27
|
+
end
|
28
|
+
}.flatten
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
module Network
|
4
|
+
|
5
|
+
# Time between pings automatically sent out for latency probing and keepalive (in seconds).
|
6
|
+
PING_INTERVAL = 2 * 60
|
7
|
+
# Time after which to disconnect, after waiting for a ping response (or inactivity).
|
8
|
+
TIMEOUT_INTERVAL = 20 * 60
|
9
|
+
# Maximum number of automatic outgoing nodes
|
10
|
+
MAX_OUTBOUND_CONNECTIONS = 4
|
11
|
+
|
12
|
+
# peer pool class.
|
13
|
+
class Pool
|
14
|
+
|
15
|
+
attr_reader :peers # active peers
|
16
|
+
attr_reader :pending_peers # currently connecting peer
|
17
|
+
attr_reader :chain
|
18
|
+
attr_reader :max_outbound
|
19
|
+
attr_reader :logger
|
20
|
+
attr_reader :peer_discovery
|
21
|
+
attr_accessor :started
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@peers = []
|
25
|
+
@pending_peers = []
|
26
|
+
@max_outbound = MAX_OUTBOUND_CONNECTIONS
|
27
|
+
@chain = Bitcoin::Node::SPVBlockChain.new
|
28
|
+
@logger = Bitcoin::Logger.create(:debug)
|
29
|
+
@peer_discovery = PeerDiscovery.new
|
30
|
+
@started = false
|
31
|
+
end
|
32
|
+
|
33
|
+
# connecting other peers and begin network activity.
|
34
|
+
def start
|
35
|
+
raise 'Cannot start a peer pool twice.' if started
|
36
|
+
logger.debug 'Start connecting other pears.'
|
37
|
+
addr_list = peer_discovery.peers
|
38
|
+
port = Bitcoin.chain_params.default_port
|
39
|
+
Parallel.map(addr_list, in_processes: 3) do |ip|
|
40
|
+
if peers.size < MAX_OUTBOUND_CONNECTIONS
|
41
|
+
EM.run do
|
42
|
+
peer = Peer.new(ip, port, self)
|
43
|
+
pending_peers << peer
|
44
|
+
peer.connect
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@started = true
|
49
|
+
end
|
50
|
+
|
51
|
+
# detect new peer connection.
|
52
|
+
def handle_new_peer(peer)
|
53
|
+
logger.debug "connected new peer #{peer.addr}"
|
54
|
+
peers << peer
|
55
|
+
pending_peers.delete(peer)
|
56
|
+
end
|
57
|
+
|
58
|
+
# terminate peers.
|
59
|
+
def terminate
|
60
|
+
peers.each {|peer| peer.close('terminate')}
|
61
|
+
@peers = []
|
62
|
+
@started = false
|
63
|
+
end
|
64
|
+
|
65
|
+
def broadcast(tx)
|
66
|
+
peers.each {|peer| peer.broadcast(tx) }
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'parallel'
|
2
|
+
require 'eventmachine'
|
3
|
+
|
4
|
+
module Bitcoin
|
5
|
+
module Network
|
6
|
+
|
7
|
+
autoload :MessageHandler, 'bitcoin/network/message_handler'
|
8
|
+
autoload :Connection, 'bitcoin/network/connection'
|
9
|
+
autoload :Pool, 'bitcoin/network/pool'
|
10
|
+
autoload :Peer, 'bitcoin/network/peer'
|
11
|
+
autoload :PeerDiscovery, 'bitcoin/network/peer_discovery'
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Node
|
3
|
+
|
4
|
+
# SPV class
|
5
|
+
class SPV
|
6
|
+
|
7
|
+
attr_reader :pool
|
8
|
+
attr_reader :logger
|
9
|
+
attr_accessor :running
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@pool = Bitcoin::Network::Pool.new
|
13
|
+
@logger = Bitcoin::Logger.create(:debug)
|
14
|
+
@running = false
|
15
|
+
end
|
16
|
+
|
17
|
+
# open the node.
|
18
|
+
def run
|
19
|
+
return if running
|
20
|
+
logger.debug 'SPV node start running.'
|
21
|
+
pool.start
|
22
|
+
end
|
23
|
+
|
24
|
+
# close the node.
|
25
|
+
def shutdown
|
26
|
+
pool.terminate
|
27
|
+
logger.debug 'SPV node shutdown.'
|
28
|
+
end
|
29
|
+
|
30
|
+
# broadcast a transaction
|
31
|
+
def broadcast(tx)
|
32
|
+
pool.broadcast(tx)
|
33
|
+
logger.debug "broadcast tx: #{tx.to_payload.bth}"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/bitcoin/node.rb
ADDED
@@ -159,6 +159,11 @@ module Bitcoin
|
|
159
159
|
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
160
160
|
end
|
161
161
|
|
162
|
+
def op_return_data
|
163
|
+
return nil unless op_return?
|
164
|
+
chunks[1].pushed_data
|
165
|
+
end
|
166
|
+
|
162
167
|
# whether data push only script which dose not include other opcode
|
163
168
|
def push_only?
|
164
169
|
chunks.each do |c|
|
@@ -184,6 +189,14 @@ module Bitcoin
|
|
184
189
|
false
|
185
190
|
end
|
186
191
|
|
192
|
+
# get witness commitment
|
193
|
+
def witness_commitment
|
194
|
+
return nil if !op_return? || op_return_data.bytesize < 36
|
195
|
+
buf = StringIO.new(op_return_data)
|
196
|
+
return nil unless buf.read(4).bth == WITNESS_COMMITMENT_HEADER
|
197
|
+
buf.read(32).bth
|
198
|
+
end
|
199
|
+
|
187
200
|
# If this script is witness program, return its script code,
|
188
201
|
# otherwise returns the self payload. ScriptInterpreter does not use this.
|
189
202
|
def to_script_code(skip_separator_index = 0)
|
data/lib/bitcoin/tx.rb
CHANGED
@@ -71,6 +71,17 @@ module Bitcoin
|
|
71
71
|
Bitcoin.double_sha256(to_payload).reverse.bth
|
72
72
|
end
|
73
73
|
|
74
|
+
# get the witness commitment of coinbase tx.
|
75
|
+
# if this tx does not coinbase or not have commitment, return nil.
|
76
|
+
def witness_commitment
|
77
|
+
return nil unless coinbase_tx?
|
78
|
+
outputs.each do |output|
|
79
|
+
commitment = output.script_pubkey.witness_commitment
|
80
|
+
return commitment if commitment
|
81
|
+
end
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
74
85
|
def to_payload
|
75
86
|
witness? ? serialize_witness_format : serialize_old_format
|
76
87
|
end
|
data/lib/bitcoin/tx_out.rb
CHANGED
data/lib/bitcoin/validation.rb
CHANGED
@@ -6,47 +6,69 @@ module Bitcoin
|
|
6
6
|
def check_tx(tx, state)
|
7
7
|
# Basic checks that don't depend on any context
|
8
8
|
if tx.inputs.empty?
|
9
|
-
return state.DoS(10, reject_code: Message::Reject::CODE_INVALID,
|
9
|
+
return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vin-empty')
|
10
10
|
end
|
11
11
|
|
12
12
|
if tx.outputs.empty?
|
13
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
13
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-empty')
|
14
14
|
end
|
15
15
|
|
16
16
|
# Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
17
17
|
if tx.serialize_old_format.bytesize * Bitcoin::WITNESS_SCALE_FACTOR > Bitcoin::MAX_BLOCK_WEIGHT
|
18
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
18
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-oversize')
|
19
19
|
end
|
20
20
|
|
21
21
|
# Check for negative or overflow output values
|
22
22
|
amount = 0
|
23
23
|
tx.outputs.each do |o|
|
24
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
25
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
24
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-negative') if o.value < 0
|
25
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-toolarge') if MAX_MONEY < o.value
|
26
26
|
amount += o.value
|
27
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
27
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-toolarge') if MAX_MONEY < amount
|
28
28
|
end
|
29
29
|
|
30
30
|
# Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
|
31
31
|
out_points = tx.inputs.map{|i|i.out_point.to_payload}
|
32
32
|
unless out_points.size == out_points.uniq.size
|
33
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
33
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-inputs-duplicate')
|
34
34
|
end
|
35
35
|
|
36
36
|
if tx.coinbase_tx?
|
37
37
|
if tx.inputs[0].script_sig.size < 2 || tx.inputs[0].script_sig.size > 100
|
38
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID,
|
38
|
+
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-cb-length')
|
39
39
|
end
|
40
40
|
else
|
41
41
|
tx.inputs.each do |i|
|
42
42
|
if i.out_point.nil? || !i.out_point.valid?
|
43
|
-
return state.DoS(10, reject_code: Message::Reject::CODE_INVALID,
|
43
|
+
return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-prevout-null')
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
47
|
true
|
48
48
|
end
|
49
49
|
|
50
|
+
# check proof of work
|
51
|
+
def check_block_header(header, state)
|
52
|
+
header.hash
|
53
|
+
header.bits
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_block(block, state)
|
58
|
+
# check block header
|
59
|
+
return false unless check_block_header(block.header, state)
|
60
|
+
|
61
|
+
# check merkle root
|
62
|
+
|
63
|
+
# size limits
|
64
|
+
|
65
|
+
# first tx is coinbase?
|
66
|
+
|
67
|
+
# check tx count
|
68
|
+
|
69
|
+
# check sigop count
|
70
|
+
end
|
71
|
+
|
50
72
|
end
|
51
73
|
|
52
74
|
class ValidationState
|
@@ -67,9 +89,9 @@ module Bitcoin
|
|
67
89
|
@corruption_possible = false
|
68
90
|
end
|
69
91
|
|
70
|
-
def DoS(level, ret: false, reject_code: 0,
|
92
|
+
def DoS(level, ret: false, reject_code: 0, reject_reason: '', corruption_in: false, debug_message: '')
|
71
93
|
@reject_code = reject_code
|
72
|
-
@reject_reason =
|
94
|
+
@reject_reason = reject_reason
|
73
95
|
@corruption_possible = corruption_in
|
74
96
|
@debug_message = debug_message
|
75
97
|
return ret if mode == MODE[:error]
|
data/lib/bitcoin/version.rb
CHANGED
data/lib/bitcoin.rb
CHANGED
@@ -5,14 +5,15 @@ require 'securerandom'
|
|
5
5
|
require 'json'
|
6
6
|
require 'bech32'
|
7
7
|
require 'ffi'
|
8
|
+
require_relative 'openassets'
|
8
9
|
|
9
10
|
module Bitcoin
|
10
11
|
|
11
12
|
autoload :Util, 'bitcoin/util'
|
12
13
|
autoload :ChainParams, 'bitcoin/chain_params'
|
13
14
|
autoload :Message, 'bitcoin/message'
|
14
|
-
autoload :Connection, 'bitcoin/connection'
|
15
15
|
autoload :Logger, 'bitcoin/logger'
|
16
|
+
autoload :Block, 'bitcoin/block'
|
16
17
|
autoload :BlockHeader, 'bitcoin/block_header'
|
17
18
|
autoload :Tx, 'bitcoin/tx'
|
18
19
|
autoload :Script, 'bitcoin/script/script'
|
@@ -27,11 +28,13 @@ module Bitcoin
|
|
27
28
|
autoload :Key, 'bitcoin/key'
|
28
29
|
autoload :ExtKey, 'bitcoin/ext_key'
|
29
30
|
autoload :Opcodes, 'bitcoin/opcodes'
|
30
|
-
autoload :Node, 'bitcoin/
|
31
|
+
autoload :Node, 'bitcoin/node'
|
31
32
|
autoload :Base58, 'bitcoin/base58'
|
32
33
|
autoload :Secp256k1, 'bitcoin/secp256k1'
|
33
34
|
autoload :Mnemonic, 'bitcoin/mnemonic'
|
34
35
|
autoload :ValidationState, 'bitcoin/validation'
|
36
|
+
autoload :Network, 'bitcoin/network'
|
37
|
+
autoload :Store, 'bitcoin/store'
|
35
38
|
|
36
39
|
require_relative 'bitcoin/constants'
|
37
40
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module OpenAssets
|
2
|
+
|
3
|
+
module MarkerOutput
|
4
|
+
|
5
|
+
# whether this output is marker output for open assets.
|
6
|
+
def open_assets_marker?
|
7
|
+
return false unless script_pubkey.op_return?
|
8
|
+
payload = Payload.parse_from_payload(script_pubkey.op_return_data)
|
9
|
+
!payload.nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|