bitcoinrb 1.12.0 → 1.13.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 +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/bitcoinrb.gemspec +1 -6
- data/lib/bitcoin/bip321_uri.rb +3 -6
- data/lib/bitcoin/bip324/fs_chacha20.rb +4 -6
- data/lib/bitcoin/bip324/fs_chacha_poly1305.rb +20 -17
- data/lib/bitcoin/bip324.rb +3 -1
- data/lib/bitcoin/descriptor/checksum.rb +6 -2
- data/lib/bitcoin/descriptor/expression.rb +2 -2
- data/lib/bitcoin/descriptor/sp.rb +219 -0
- data/lib/bitcoin/descriptor.rb +20 -0
- data/lib/bitcoin/ext_key.rb +11 -7
- data/lib/bitcoin/key.rb +1 -1
- data/lib/bitcoin/message/cf_parser.rb +2 -2
- data/lib/bitcoin/message/fee_filter.rb +2 -2
- data/lib/bitcoin/message/header_and_short_ids.rb +4 -4
- data/lib/bitcoin/message/inventory.rb +1 -1
- data/lib/bitcoin/message/network_addr.rb +7 -3
- data/lib/bitcoin/message/ping.rb +5 -3
- data/lib/bitcoin/message/pong.rb +2 -2
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/message/version.rb +4 -4
- data/lib/bitcoin/message.rb +12 -2
- data/lib/bitcoin/message_sign.rb +98 -38
- data/lib/bitcoin/mnemonic.rb +9 -4
- data/lib/bitcoin/psbt/input.rb +51 -33
- data/lib/bitcoin/psbt/key_origin_info.rb +2 -2
- data/lib/bitcoin/psbt/output.rb +9 -5
- data/lib/bitcoin/psbt/tx.rb +16 -10
- data/lib/bitcoin/psbt.rb +1 -1
- data/lib/bitcoin/rpc.rb +0 -2
- data/lib/bitcoin/script/script.rb +14 -4
- data/lib/bitcoin/secp256k1/rfc6979.rb +6 -3
- data/lib/bitcoin/sighash_generator.rb +2 -2
- data/lib/bitcoin/silent_payment/output.rb +46 -0
- data/lib/bitcoin/silent_payment.rb +113 -12
- data/lib/bitcoin/taproot/control_block.rb +1 -0
- data/lib/bitcoin/tx.rb +8 -6
- data/lib/bitcoin/tx_out.rb +2 -2
- data/lib/bitcoin/util.rb +5 -5
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +2 -2
- data/lib/bitcoin/wallet/db.rb +3 -3
- data/lib/bitcoin/wallet/master_key.rb +2 -2
- data/lib/bitcoin/wallet/utxo.rb +2 -2
- data/lib/bitcoin.rb +1 -6
- metadata +18 -108
- data/exe/bitcoinrb-cli +0 -5
- data/exe/bitcoinrbd +0 -46
- data/lib/bitcoin/network/connection.rb +0 -73
- data/lib/bitcoin/network/message_handler.rb +0 -243
- data/lib/bitcoin/network/peer.rb +0 -228
- data/lib/bitcoin/network/peer_discovery.rb +0 -42
- data/lib/bitcoin/network/pool.rb +0 -135
- data/lib/bitcoin/network.rb +0 -13
- data/lib/bitcoin/node/cli.rb +0 -122
- data/lib/bitcoin/node/configuration.rb +0 -40
- data/lib/bitcoin/node/spv.rb +0 -87
- data/lib/bitcoin/node.rb +0 -7
- data/lib/bitcoin/rpc/http_server.rb +0 -84
- data/lib/bitcoin/rpc/request_handler.rb +0 -150
- data/lib/bitcoin/store/chain_entry.rb +0 -68
- data/lib/bitcoin/store/db/level_db.rb +0 -98
- data/lib/bitcoin/store/db.rb +0 -9
- data/lib/bitcoin/store/spv_chain.rb +0 -101
- data/lib/bitcoin/store/utxo_db.rb +0 -226
- data/lib/bitcoin/store.rb +0 -12
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
require 'evma_httpserver'
|
|
2
|
-
require 'json'
|
|
3
|
-
|
|
4
|
-
module Bitcoin
|
|
5
|
-
module RPC
|
|
6
|
-
|
|
7
|
-
# Bitcoinrb RPC server.
|
|
8
|
-
class HttpServer < EM::Connection
|
|
9
|
-
include EM::HttpServer
|
|
10
|
-
include RequestHandler
|
|
11
|
-
|
|
12
|
-
SUPPORTED_COMMANDS = %w[
|
|
13
|
-
getblockchaininfo
|
|
14
|
-
stop
|
|
15
|
-
getblockheader
|
|
16
|
-
getpeerinfo
|
|
17
|
-
sendrawtransaction
|
|
18
|
-
decoderawtransaction
|
|
19
|
-
decodescript
|
|
20
|
-
createwallet
|
|
21
|
-
listwallets
|
|
22
|
-
getwalletinfo
|
|
23
|
-
listaccounts
|
|
24
|
-
encryptwallet
|
|
25
|
-
getnewaddress
|
|
26
|
-
].freeze
|
|
27
|
-
|
|
28
|
-
attr_reader :node
|
|
29
|
-
attr_accessor :logger
|
|
30
|
-
|
|
31
|
-
def initialize(node)
|
|
32
|
-
@node = node
|
|
33
|
-
@logger = Bitcoin::Logger.create(:debug)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def post_init
|
|
37
|
-
super
|
|
38
|
-
logger.debug 'start http server.'
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def self.run(node, port = 8332)
|
|
42
|
-
EM.start_server('0.0.0.0', port, HttpServer, node)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# process http request.
|
|
46
|
-
def process_http_request
|
|
47
|
-
operation = proc {
|
|
48
|
-
command, args = parse_json_params
|
|
49
|
-
logger.debug("process http request. command = #{command}")
|
|
50
|
-
unless SUPPORTED_COMMANDS.include?(command)
|
|
51
|
-
raise ArgumentError, "Unsupported method: #{command}"
|
|
52
|
-
end
|
|
53
|
-
begin
|
|
54
|
-
send(command, *args).to_json
|
|
55
|
-
rescue Exception => e
|
|
56
|
-
e
|
|
57
|
-
end
|
|
58
|
-
}
|
|
59
|
-
callback = proc{ |result|
|
|
60
|
-
response = EM::DelegatedHttpResponse.new(self)
|
|
61
|
-
if result.is_a?(Exception)
|
|
62
|
-
response.status = 500
|
|
63
|
-
response.content = result.message
|
|
64
|
-
else
|
|
65
|
-
response.status = 200
|
|
66
|
-
response.content = result
|
|
67
|
-
end
|
|
68
|
-
response.content_type 'application/json'
|
|
69
|
-
response.send_response
|
|
70
|
-
}
|
|
71
|
-
EM.defer(operation, callback)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# parse request parameter.
|
|
75
|
-
# @return [Array] the array of command and args
|
|
76
|
-
def parse_json_params
|
|
77
|
-
params = JSON.parse(@http_post_content)
|
|
78
|
-
[params['method'], params['params']]
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
end
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
module Bitcoin
|
|
2
|
-
module RPC
|
|
3
|
-
|
|
4
|
-
# RPC server's request handler.
|
|
5
|
-
module RequestHandler
|
|
6
|
-
|
|
7
|
-
# Returns an object containing various state info regarding blockchain processing.
|
|
8
|
-
def getblockchaininfo
|
|
9
|
-
h = {}
|
|
10
|
-
h[:chain] = Bitcoin.chain_params.network
|
|
11
|
-
best_block = node.chain.latest_block
|
|
12
|
-
h[:headers] = best_block.height
|
|
13
|
-
h[:bestblockhash] = best_block.header.block_id
|
|
14
|
-
h[:chainwork] = best_block.header.work
|
|
15
|
-
h[:mediantime] = node.chain.mtp(best_block.block_hash)
|
|
16
|
-
h
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# shutdown node
|
|
20
|
-
def stop
|
|
21
|
-
node.shutdown
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# get block header information.
|
|
25
|
-
# @param [String] block_id block hash(big endian)
|
|
26
|
-
def getblockheader(block_id, verbose)
|
|
27
|
-
block_hash = block_id.rhex
|
|
28
|
-
entry = node.chain.find_entry_by_hash(block_hash)
|
|
29
|
-
raise ArgumentError.new('Block not found') unless entry
|
|
30
|
-
if verbose
|
|
31
|
-
{
|
|
32
|
-
hash: block_id,
|
|
33
|
-
height: entry.height,
|
|
34
|
-
version: entry.header.version,
|
|
35
|
-
versionHex: entry.header.version.to_even_length_hex,
|
|
36
|
-
merkleroot: entry.header.merkle_root.rhex,
|
|
37
|
-
time: entry.header.time,
|
|
38
|
-
mediantime: node.chain.mtp(block_hash),
|
|
39
|
-
nonce: entry.header.nonce,
|
|
40
|
-
bits: entry.header.bits.to_even_length_hex,
|
|
41
|
-
previousblockhash: entry.prev_hash.rhex,
|
|
42
|
-
nextblockhash: node.chain.next_hash(block_hash).rhex
|
|
43
|
-
}
|
|
44
|
-
else
|
|
45
|
-
entry.header.to_hex
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Returns connected peer information.
|
|
50
|
-
def getpeerinfo
|
|
51
|
-
node.pool.peers.map do |peer|
|
|
52
|
-
local_addr = "#{peer.remote_version.remote_addr.addr_string}:18333"
|
|
53
|
-
{
|
|
54
|
-
id: peer.id,
|
|
55
|
-
addr: "#{peer.host}:#{peer.port}",
|
|
56
|
-
addrlocal: local_addr,
|
|
57
|
-
services: peer.remote_version.services.to_even_length_hex.rjust(16, '0'),
|
|
58
|
-
relaytxes: peer.remote_version.relay,
|
|
59
|
-
lastsend: peer.last_send,
|
|
60
|
-
lastrecv: peer.last_recv,
|
|
61
|
-
bytessent: peer.bytes_sent,
|
|
62
|
-
bytesrecv: peer.bytes_recv,
|
|
63
|
-
conntime: peer.conn_time,
|
|
64
|
-
pingtime: peer.ping_time,
|
|
65
|
-
minping: peer.min_ping,
|
|
66
|
-
version: peer.remote_version.version,
|
|
67
|
-
subver: peer.remote_version.user_agent,
|
|
68
|
-
inbound: !peer.outbound?,
|
|
69
|
-
startingheight: peer.remote_version.start_height,
|
|
70
|
-
best_hash: peer.best_hash,
|
|
71
|
-
best_height: peer.best_height
|
|
72
|
-
}
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# broadcast transaction
|
|
77
|
-
def sendrawtransaction(hex_tx)
|
|
78
|
-
tx = Bitcoin::Tx.parse_from_payload(hex_tx.htb, strict: true)
|
|
79
|
-
# TODO check wether tx is valid
|
|
80
|
-
node.broadcast(tx)
|
|
81
|
-
tx.txid
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# decode tx data.
|
|
85
|
-
def decoderawtransaction(hex_tx)
|
|
86
|
-
begin
|
|
87
|
-
Bitcoin::Tx.parse_from_payload(hex_tx.htb, strict: true ).to_h
|
|
88
|
-
rescue Exception
|
|
89
|
-
raise ArgumentError.new('TX decode failed')
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# decode script data.
|
|
94
|
-
def decodescript(hex_script)
|
|
95
|
-
begin
|
|
96
|
-
script = Bitcoin::Script.parse_from_payload(hex_script.htb)
|
|
97
|
-
h = script.to_h
|
|
98
|
-
h.delete(:hex)
|
|
99
|
-
h[:p2sh] = script.to_p2sh.to_addr unless script.p2sh?
|
|
100
|
-
h
|
|
101
|
-
rescue Exception
|
|
102
|
-
raise ArgumentError.new('Script decode failed')
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# wallet api
|
|
107
|
-
|
|
108
|
-
# create wallet
|
|
109
|
-
def createwallet(wallet_id = 1, wallet_path_prefix = Bitcoin::Wallet::Base.default_path_prefix)
|
|
110
|
-
wallet = Bitcoin::Wallet::Base.create(wallet_id, wallet_path_prefix)
|
|
111
|
-
node.wallet = wallet unless node.wallet
|
|
112
|
-
{wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic}
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# get wallet list.
|
|
116
|
-
def listwallets(wallet_path_prefix = Bitcoin::Wallet::Base.default_path_prefix)
|
|
117
|
-
Bitcoin::Wallet::Base.wallet_paths(wallet_path_prefix)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# get current wallet information.
|
|
121
|
-
def getwalletinfo
|
|
122
|
-
node.wallet ? node.wallet.to_h : {}
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# get the list of current Wallet accounts.
|
|
126
|
-
def listaccounts
|
|
127
|
-
return {} unless node.wallet
|
|
128
|
-
accounts = {}
|
|
129
|
-
node.wallet.accounts.each do |a|
|
|
130
|
-
accounts[a.name] = node.wallet.get_balance(a)
|
|
131
|
-
end
|
|
132
|
-
accounts
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# encrypt wallet.
|
|
136
|
-
def encryptwallet(passphrase)
|
|
137
|
-
return nil unless node.wallet
|
|
138
|
-
node.wallet.encrypt(passphrase)
|
|
139
|
-
"The wallet 'wallet_id: #{node.wallet.wallet_id}' has been encrypted."
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# create new bitcoin address for receiving payments.
|
|
143
|
-
def getnewaddress(account_name)
|
|
144
|
-
node.wallet.generate_new_address(account_name)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
end
|
|
150
|
-
end
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
module Bitcoin
|
|
2
|
-
module Store
|
|
3
|
-
|
|
4
|
-
# wrap a block header object with extra data.
|
|
5
|
-
class ChainEntry
|
|
6
|
-
include Bitcoin::HexConverter
|
|
7
|
-
|
|
8
|
-
attr_reader :header
|
|
9
|
-
attr_reader :height
|
|
10
|
-
|
|
11
|
-
# @param [Bitcoin::BlockHeader] header a block header.
|
|
12
|
-
# @param [Integer] height a block height.
|
|
13
|
-
def initialize(header, height)
|
|
14
|
-
@header = header
|
|
15
|
-
@height = height
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# get database key
|
|
19
|
-
def key
|
|
20
|
-
Bitcoin::Store::KEY_PREFIX[:entry] + header.block_hash
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def hash
|
|
24
|
-
header.hash
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# block hash
|
|
28
|
-
def block_hash
|
|
29
|
-
header.block_hash
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# previous block hash
|
|
33
|
-
def prev_hash
|
|
34
|
-
header.prev_hash
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# whether genesis block
|
|
38
|
-
def genesis?
|
|
39
|
-
Bitcoin.chain_params.genesis_block.header == header
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# @param [String] payload a payload with binary format.
|
|
43
|
-
def self.parse_from_payload(payload)
|
|
44
|
-
buf = StringIO.new(payload)
|
|
45
|
-
len = Bitcoin.unpack_var_int_from_io(buf)
|
|
46
|
-
height = buf.read(len).reverse.bth.to_i(16)
|
|
47
|
-
new(Bitcoin::BlockHeader.parse_from_payload(buf.read(80)), height)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# build next block +StoredBlock+ instance.
|
|
51
|
-
# @param [Bitcoin::BlockHeader] next_block a next block candidate header.
|
|
52
|
-
# @return [Bitcoin::Store::ChainEntry] a next stored block (not saved).
|
|
53
|
-
def build_next_block(next_block)
|
|
54
|
-
ChainEntry.new(next_block, height + 1)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# generate payload
|
|
58
|
-
def to_payload
|
|
59
|
-
height_value = height.to_even_length_hex
|
|
60
|
-
height_value = height_value.htb.reverse
|
|
61
|
-
Bitcoin.pack_var_int(height_value.bytesize) + height_value + header.to_payload
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
end
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
require 'leveldb-native'
|
|
2
|
-
|
|
3
|
-
module Bitcoin
|
|
4
|
-
module Store
|
|
5
|
-
module DB
|
|
6
|
-
|
|
7
|
-
class LevelDB
|
|
8
|
-
|
|
9
|
-
attr_reader :db
|
|
10
|
-
attr_reader :logger
|
|
11
|
-
|
|
12
|
-
def initialize(path = "#{Bitcoin.base_dir}/db/spv")
|
|
13
|
-
# @logger = Bitcoin::Logger.create(:debug)
|
|
14
|
-
FileUtils.mkdir_p(path)
|
|
15
|
-
@db = ::LevelDBNative::DB.new(path)
|
|
16
|
-
# logger.debug 'Opened LevelDB successfully.'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# put data into LevelDB.
|
|
20
|
-
# @param [Object] key a key.
|
|
21
|
-
# @param [Object] value a value.
|
|
22
|
-
def put(key, value)
|
|
23
|
-
# logger.debug "put #{key} data"
|
|
24
|
-
db.put(key, value)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# get value from specified key.
|
|
28
|
-
# @param [Object] key a key.
|
|
29
|
-
# @return[Object] the stored value.
|
|
30
|
-
def get(key)
|
|
31
|
-
db.get(key)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# get best block hash.
|
|
35
|
-
def best_hash
|
|
36
|
-
db.get(KEY_PREFIX[:best])
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# delete specified key data.
|
|
40
|
-
def delete(key)
|
|
41
|
-
db.delete(key)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# get block hash specified +height+
|
|
45
|
-
def get_hash_from_height(height)
|
|
46
|
-
db.get(height_key(height))
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# get next block hash specified +hash+
|
|
50
|
-
def next_hash(hash)
|
|
51
|
-
db.get(KEY_PREFIX[:next] + hash)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# get entry payload
|
|
55
|
-
# @param [String] hash the hash with hex format.
|
|
56
|
-
# @return [String] the ChainEntry payload.
|
|
57
|
-
def get_entry_payload_from_hash(hash)
|
|
58
|
-
db.get(KEY_PREFIX[:entry] + hash)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def save_entry(entry)
|
|
62
|
-
db.batch do
|
|
63
|
-
db.put(entry.key ,entry.to_payload)
|
|
64
|
-
db.put(height_key(entry.height), entry.block_hash)
|
|
65
|
-
connect_entry(entry)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def close
|
|
70
|
-
db.close
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
# generate height key
|
|
76
|
-
def height_key(height)
|
|
77
|
-
height = height.to_even_length_hex
|
|
78
|
-
KEY_PREFIX[:height] + height.rhex
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def connect_entry(entry)
|
|
82
|
-
unless entry.genesis?
|
|
83
|
-
tip_block = Bitcoin::Store::ChainEntry.parse_from_payload(get_entry_payload_from_hash(best_hash))
|
|
84
|
-
unless tip_block.block_hash == entry.prev_hash
|
|
85
|
-
raise "entry(#{entry.block_hash}) does not reference current best block hash(#{tip_block.block_hash})"
|
|
86
|
-
end
|
|
87
|
-
unless tip_block.height + 1 == entry.height
|
|
88
|
-
raise "block height is small than current best block."
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
db.put(KEY_PREFIX[:best], entry.block_hash)
|
|
92
|
-
db.put(KEY_PREFIX[:next] + entry.prev_hash, entry.block_hash)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
data/lib/bitcoin/store/db.rb
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
module Bitcoin
|
|
2
|
-
|
|
3
|
-
module Store
|
|
4
|
-
|
|
5
|
-
KEY_PREFIX = {
|
|
6
|
-
entry: 'e', # key: block hash, value: Bitcoin::Store::ChainEntry payload
|
|
7
|
-
height: 'h', # key: block height, value: block hash.
|
|
8
|
-
best: 'B', # value: best block hash.
|
|
9
|
-
next: 'n' # key: block hash, value: A hash of the next block of the specified hash
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class SPVChain
|
|
13
|
-
|
|
14
|
-
attr_reader :db
|
|
15
|
-
attr_reader :logger
|
|
16
|
-
|
|
17
|
-
def initialize(db = Bitcoin::Store::DB::LevelDB.new)
|
|
18
|
-
@db = db # TODO multiple db switch
|
|
19
|
-
@logger = Bitcoin::Logger.create(:debug)
|
|
20
|
-
initialize_block
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# get latest block in the store.
|
|
24
|
-
# @return[Bitcoin::Store::ChainEntry]
|
|
25
|
-
def latest_block
|
|
26
|
-
hash = db.best_hash
|
|
27
|
-
return nil unless hash
|
|
28
|
-
find_entry_by_hash(hash)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# find block entry with the specified height.
|
|
32
|
-
def find_entry_by_height(height)
|
|
33
|
-
find_entry_by_hash(db.get_hash_from_height(height))
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# find block entry with the specified hash
|
|
37
|
-
def find_entry_by_hash(hash)
|
|
38
|
-
payload = db.get_entry_payload_from_hash(hash)
|
|
39
|
-
return nil unless payload
|
|
40
|
-
ChainEntry.parse_from_payload(payload)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# append block header to chain.
|
|
44
|
-
# @param [Bitcoin::BlockHeader] header a block header.
|
|
45
|
-
# @return [Bitcoin::Store::ChainEntry] appended block header entry.
|
|
46
|
-
def append_header(header)
|
|
47
|
-
logger.info("append header #{header.block_id}")
|
|
48
|
-
raise "this header is invalid. #{header.block_hash}" unless header.valid?
|
|
49
|
-
best_block = latest_block
|
|
50
|
-
current_height = best_block.height
|
|
51
|
-
if best_block.block_hash == header.prev_hash
|
|
52
|
-
entry = Bitcoin::Store::ChainEntry.new(header, current_height + 1)
|
|
53
|
-
db.save_entry(entry)
|
|
54
|
-
entry
|
|
55
|
-
else
|
|
56
|
-
unless find_entry_by_hash(header.block_hash)
|
|
57
|
-
# TODO implements recovery process
|
|
58
|
-
raise "header's previous hash(#{header.prev_hash}) does not match current best block's(#{best_block.block_hash})."
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# get next block hash for specified +hash+
|
|
64
|
-
# @param [String] hash the block hash(little endian)
|
|
65
|
-
# @return [String] the next block hash. If it does not exist yet, return nil.
|
|
66
|
-
def next_hash(hash)
|
|
67
|
-
db.next_hash(hash)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# get median time past for specified block +hash+
|
|
71
|
-
# @param [String] hash the block hash.
|
|
72
|
-
# @return [Integer] the median time past value.
|
|
73
|
-
def mtp(hash)
|
|
74
|
-
time = []
|
|
75
|
-
Bitcoin::MEDIAN_TIME_SPAN.times do
|
|
76
|
-
entry = find_entry_by_hash(hash)
|
|
77
|
-
break unless entry
|
|
78
|
-
|
|
79
|
-
time << entry.header.time
|
|
80
|
-
hash = entry.header.prev_hash
|
|
81
|
-
end
|
|
82
|
-
time.sort!
|
|
83
|
-
time[time.size / 2]
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
87
|
-
|
|
88
|
-
# if database is empty, put genesis block.
|
|
89
|
-
def initialize_block
|
|
90
|
-
unless latest_block
|
|
91
|
-
block = Bitcoin.chain_params.genesis_block
|
|
92
|
-
genesis = ChainEntry.new(block.header, 0)
|
|
93
|
-
db.save_entry(genesis)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
end
|