bitcoinrb 1.12.0 → 1.12.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.
Files changed (40) 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/bip324/fs_chacha20.rb +3 -5
  5. data/lib/bitcoin/bip324/fs_chacha_poly1305.rb +19 -17
  6. data/lib/bitcoin/bip324.rb +1 -1
  7. data/lib/bitcoin/descriptor/expression.rb +2 -2
  8. data/lib/bitcoin/descriptor/sp.rb +219 -0
  9. data/lib/bitcoin/descriptor.rb +20 -0
  10. data/lib/bitcoin/ext_key.rb +3 -3
  11. data/lib/bitcoin/message/ping.rb +4 -2
  12. data/lib/bitcoin/message/version.rb +1 -1
  13. data/lib/bitcoin/message_sign.rb +98 -38
  14. data/lib/bitcoin/rpc.rb +0 -2
  15. data/lib/bitcoin/silent_payment/output.rb +46 -0
  16. data/lib/bitcoin/silent_payment.rb +113 -12
  17. data/lib/bitcoin/util.rb +2 -2
  18. data/lib/bitcoin/version.rb +1 -1
  19. data/lib/bitcoin.rb +0 -6
  20. metadata +18 -108
  21. data/exe/bitcoinrb-cli +0 -5
  22. data/exe/bitcoinrbd +0 -46
  23. data/lib/bitcoin/network/connection.rb +0 -73
  24. data/lib/bitcoin/network/message_handler.rb +0 -243
  25. data/lib/bitcoin/network/peer.rb +0 -228
  26. data/lib/bitcoin/network/peer_discovery.rb +0 -42
  27. data/lib/bitcoin/network/pool.rb +0 -135
  28. data/lib/bitcoin/network.rb +0 -13
  29. data/lib/bitcoin/node/cli.rb +0 -122
  30. data/lib/bitcoin/node/configuration.rb +0 -40
  31. data/lib/bitcoin/node/spv.rb +0 -87
  32. data/lib/bitcoin/node.rb +0 -7
  33. data/lib/bitcoin/rpc/http_server.rb +0 -84
  34. data/lib/bitcoin/rpc/request_handler.rb +0 -150
  35. data/lib/bitcoin/store/chain_entry.rb +0 -68
  36. data/lib/bitcoin/store/db/level_db.rb +0 -98
  37. data/lib/bitcoin/store/db.rb +0 -9
  38. data/lib/bitcoin/store/spv_chain.rb +0 -101
  39. data/lib/bitcoin/store/utxo_db.rb +0 -226
  40. data/lib/bitcoin/store.rb +0 -12
@@ -1,122 +0,0 @@
1
- require 'net/http'
2
- require 'thor'
3
- require 'json'
4
-
5
- module Bitcoin
6
- module Node
7
-
8
- class CLI < Thor
9
-
10
- class_option :network, aliases: '-n', default: :mainnet
11
-
12
- desc 'decodepsbt <base64 psbt string>', "Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction."
13
- def decodepsbt(base64)
14
- psbt = Bitcoin::PSBT::Tx.parse_from_base64(base64)
15
- puts JSON.pretty_generate(psbt.to_h)
16
- end
17
-
18
- desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
19
- def getblockchaininfo
20
- request('getblockchaininfo')
21
- end
22
-
23
- desc 'stop', 'Stop Bitcoin server.'
24
- def stop
25
- request('stop')
26
- end
27
-
28
- desc 'getblockheader "hash" ( verbose )', 'If verbose is false, returns a string that is serialized, hex-encoded data for blockheader "hash". If verbose is true, returns an Object with information about blockheader <hash>.'
29
- def getblockheader(hash, verbose = true)
30
- verbose = verbose.is_a?(String) ? (verbose == 'true') : verbose
31
- request('getblockheader', hash, verbose)
32
- end
33
-
34
- desc 'getpeerinfo', 'Returns data about each connected network node as a json array of objects.'
35
- def getpeerinfo
36
- request('getpeerinfo')
37
- end
38
-
39
- desc 'decoderawtransaction "hexstring"', 'Return a JSON object representing the serialized, hex-encoded transaction.'
40
- def decoderawtransaction(hexstring)
41
- request('decoderawtransaction', hexstring)
42
- end
43
-
44
- desc 'decodescript "hexstring"', 'Decode a hex-encoded script.'
45
- def decodescript(hexstring)
46
- request('decodescript', hexstring)
47
- end
48
-
49
- # wallet cli
50
-
51
- desc 'sendrawtransaction', 'Submits raw transaction (serialized, hex-encoded) to local node and network.'
52
- def sendrawtransaction(hex_tx)
53
- request('sendrawtransaction', hex_tx)
54
- end
55
-
56
- desc 'createwallet "wallet_id"', 'Create new HD wallet. It returns an error if an existing wallet_id is specified. '
57
- def createwallet(wallet_id)
58
- request('createwallet', wallet_id)
59
- end
60
-
61
- desc 'listwallets', 'Returns a list of currently loaded wallets. For full information on the wallet, use "getwalletinfo"'
62
- def listwallets
63
- request('listwallets')
64
- end
65
-
66
- desc 'getwalletinfo', 'Returns an object containing various wallet state info.'
67
- def getwalletinfo
68
- request('getwalletinfo')
69
- end
70
-
71
- desc 'listaccounts', '[WIP]Returns Object that has account names as keys, account balances as values.'
72
- def listaccounts
73
- request('listaccounts')
74
- end
75
-
76
- desc 'encryptwallet "passphrase"', 'Encrypts the wallet with "passphrase". This is for first time encryption.After this, any calls that interact with private keys such as sending or signing will require the passphrase to be set prior the making these calls.'
77
- def encryptwallet(passhphrase)
78
- request('encryptwallet', passhphrase)
79
- end
80
-
81
- desc 'getnewaddress "account"', 'Returns a new Bitcoin address for receiving payments.'
82
- def getnewaddress(account)
83
- request('getnewaddress', account)
84
- end
85
-
86
- private
87
-
88
- def config
89
- opts = {}
90
- opts[:network] = options['network'] if options['network']
91
- @conf ||= Bitcoin::Node::Configuration.new(opts)
92
- end
93
-
94
- def request(command, *params)
95
- data = {
96
- :method => command,
97
- :params => params,
98
- :id => 'jsonrpc'
99
- }
100
- begin
101
- uri = URI.parse(config.server_url)
102
- http = Net::HTTP.new(uri.hostname, uri.port)
103
- http.use_ssl = uri.scheme === "https"
104
- request = Net::HTTP::Post.new('/')
105
- request.content_type = 'application/json'
106
- request.body = data.to_json
107
- response = http.request(request)
108
- body = response.body
109
- begin
110
- json = JSON.parse(body.to_str)
111
- puts JSON.pretty_generate(json)
112
- rescue Exception
113
- puts body.to_str
114
- end
115
- rescue Exception => e
116
- puts e.message
117
- end
118
- end
119
-
120
- end
121
- end
122
- end
@@ -1,40 +0,0 @@
1
- require 'iniparse'
2
-
3
- module Bitcoin
4
- module Node
5
- class Configuration
6
-
7
- attr_reader :conf # Hash
8
-
9
- # initialize configuration
10
- # @param [Hash] opts parameter for node.
11
- def initialize(opts = {})
12
- # TODO apply configuration file.
13
- opts[:network] = :mainnet unless opts[:network]
14
- opts[:relay] = false unless opts[:relay]
15
- Bitcoin.chain_params = opts[:network]
16
-
17
- begin
18
- ini_file = IniParse.parse(File.read("#{Bitcoin.base_dir}/bitcoinrb.conf"))
19
- @conf = Hash[ ini_file.to_h['__anonymous__'].map{|k,v| [k.to_sym, v] } ]
20
- rescue => e
21
- @conf = {}
22
- end
23
- @conf.merge!(opts)
24
- end
25
-
26
- def host
27
- 'localhost'
28
- end
29
-
30
- def port
31
- Bitcoin.chain_params.default_port - 1
32
- end
33
-
34
- def server_url
35
- "http://#{host}:#{port}"
36
- end
37
-
38
- end
39
- end
40
- end
@@ -1,87 +0,0 @@
1
- module Bitcoin
2
- module Node
3
-
4
- # SPV class
5
- class SPV
6
-
7
- attr_reader :chain
8
- attr_reader :pool
9
- attr_reader :logger
10
- attr_accessor :running
11
- attr_reader :configuration
12
- attr_accessor :server
13
- attr_accessor :wallet
14
- attr_accessor :bloom
15
-
16
- # Initialize spv settings
17
- # @param [Bitcoin::Node::Configuration] configuration configuration for spv.
18
- #
19
- # ```ruby
20
- # config = Bitcoin::Node::Configuration.new(network: :mainnet)
21
- # spv = Bitcoin::Node::SPV.new(config)
22
- # spv.run
23
- # ````
24
- def initialize(configuration)
25
- @chain = Bitcoin::Store::SPVChain.new
26
- @configuration = configuration
27
- @pool = Bitcoin::Network::Pool.new(self, @chain, @configuration)
28
- @logger = Bitcoin::Logger.create(:debug)
29
- @running = false
30
- @wallet = Bitcoin::Wallet::Base.current_wallet
31
- # TODO : optimize bloom filter parameters
32
- setup_filter
33
- end
34
-
35
- # open the node.
36
- def run
37
- # TODO need process running check.
38
- return if running
39
- logger.debug 'SPV node start running.'
40
- EM.run do
41
- # EM.start_server('0.0.0.0', Bitcoin.chain_params.default_port, Bitcoin::Network::InboundConnector, self)
42
- pool.start
43
- @server = Bitcoin::RPC::HttpServer.run(self, configuration.port)
44
- end
45
- end
46
-
47
- # close the node.
48
- def shutdown
49
- pool.terminate
50
- logger.debug 'SPV node shutdown.'
51
- end
52
-
53
- # broadcast a transaction
54
- def broadcast(tx)
55
- pool.broadcast(tx)
56
- logger.debug "broadcast tx: #{tx.to_hex}"
57
- end
58
-
59
- # add filter element to bloom filter.
60
- # [String] element. the hex string of txid, public key, public key hash or outpoint.
61
- def filter_add(element)
62
- bloom.add(element)
63
- pool.filter_add(element)
64
- end
65
-
66
- # clear bloom filter.
67
- def filter_clear
68
- pool.filter_clear
69
- end
70
-
71
- def add_observer(observer)
72
- pool.add_observer(observer)
73
- end
74
-
75
- def delete_observer(observer)
76
- pool.delete_observer(observer)
77
- end
78
-
79
- private
80
-
81
- def setup_filter
82
- @bloom = Bitcoin::BloomFilter.create_filter(512, 0.01)
83
- wallet.watch_targets.each{|t|bloom.add(t.htb)} if wallet
84
- end
85
- end
86
- end
87
- end
data/lib/bitcoin/node.rb DELETED
@@ -1,7 +0,0 @@
1
- module Bitcoin
2
- module Node
3
- autoload :SPV, 'bitcoin/node/spv'
4
- autoload :CLI, 'bitcoin/node/cli'
5
- autoload :Configuration, 'bitcoin/node/configuration'
6
- end
7
- end
@@ -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