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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/bitcoinrb.gemspec +1 -6
  4. data/lib/bitcoin/bip321_uri.rb +3 -6
  5. data/lib/bitcoin/bip324/fs_chacha20.rb +4 -6
  6. data/lib/bitcoin/bip324/fs_chacha_poly1305.rb +20 -17
  7. data/lib/bitcoin/bip324.rb +3 -1
  8. data/lib/bitcoin/descriptor/checksum.rb +6 -2
  9. data/lib/bitcoin/descriptor/expression.rb +2 -2
  10. data/lib/bitcoin/descriptor/sp.rb +219 -0
  11. data/lib/bitcoin/descriptor.rb +20 -0
  12. data/lib/bitcoin/ext_key.rb +11 -7
  13. data/lib/bitcoin/key.rb +1 -1
  14. data/lib/bitcoin/message/cf_parser.rb +2 -2
  15. data/lib/bitcoin/message/fee_filter.rb +2 -2
  16. data/lib/bitcoin/message/header_and_short_ids.rb +4 -4
  17. data/lib/bitcoin/message/inventory.rb +1 -1
  18. data/lib/bitcoin/message/network_addr.rb +7 -3
  19. data/lib/bitcoin/message/ping.rb +5 -3
  20. data/lib/bitcoin/message/pong.rb +2 -2
  21. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  22. data/lib/bitcoin/message/version.rb +4 -4
  23. data/lib/bitcoin/message.rb +12 -2
  24. data/lib/bitcoin/message_sign.rb +98 -38
  25. data/lib/bitcoin/mnemonic.rb +9 -4
  26. data/lib/bitcoin/psbt/input.rb +51 -33
  27. data/lib/bitcoin/psbt/key_origin_info.rb +2 -2
  28. data/lib/bitcoin/psbt/output.rb +9 -5
  29. data/lib/bitcoin/psbt/tx.rb +16 -10
  30. data/lib/bitcoin/psbt.rb +1 -1
  31. data/lib/bitcoin/rpc.rb +0 -2
  32. data/lib/bitcoin/script/script.rb +14 -4
  33. data/lib/bitcoin/secp256k1/rfc6979.rb +6 -3
  34. data/lib/bitcoin/sighash_generator.rb +2 -2
  35. data/lib/bitcoin/silent_payment/output.rb +46 -0
  36. data/lib/bitcoin/silent_payment.rb +113 -12
  37. data/lib/bitcoin/taproot/control_block.rb +1 -0
  38. data/lib/bitcoin/tx.rb +8 -6
  39. data/lib/bitcoin/tx_out.rb +2 -2
  40. data/lib/bitcoin/util.rb +5 -5
  41. data/lib/bitcoin/version.rb +1 -1
  42. data/lib/bitcoin/wallet/account.rb +2 -2
  43. data/lib/bitcoin/wallet/db.rb +3 -3
  44. data/lib/bitcoin/wallet/master_key.rb +2 -2
  45. data/lib/bitcoin/wallet/utxo.rb +2 -2
  46. data/lib/bitcoin.rb +1 -6
  47. metadata +18 -108
  48. data/exe/bitcoinrb-cli +0 -5
  49. data/exe/bitcoinrbd +0 -46
  50. data/lib/bitcoin/network/connection.rb +0 -73
  51. data/lib/bitcoin/network/message_handler.rb +0 -243
  52. data/lib/bitcoin/network/peer.rb +0 -228
  53. data/lib/bitcoin/network/peer_discovery.rb +0 -42
  54. data/lib/bitcoin/network/pool.rb +0 -135
  55. data/lib/bitcoin/network.rb +0 -13
  56. data/lib/bitcoin/node/cli.rb +0 -122
  57. data/lib/bitcoin/node/configuration.rb +0 -40
  58. data/lib/bitcoin/node/spv.rb +0 -87
  59. data/lib/bitcoin/node.rb +0 -7
  60. data/lib/bitcoin/rpc/http_server.rb +0 -84
  61. data/lib/bitcoin/rpc/request_handler.rb +0 -150
  62. data/lib/bitcoin/store/chain_entry.rb +0 -68
  63. data/lib/bitcoin/store/db/level_db.rb +0 -98
  64. data/lib/bitcoin/store/db.rb +0 -9
  65. data/lib/bitcoin/store/spv_chain.rb +0 -101
  66. data/lib/bitcoin/store/utxo_db.rb +0 -226
  67. 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
@@ -1,9 +0,0 @@
1
- module Bitcoin
2
- module Store
3
-
4
- module DB
5
- autoload :LevelDB, 'bitcoin/store/db/level_db'
6
- end
7
-
8
- end
9
- end
@@ -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