bitcoinrb 0.1.4 → 0.1.5
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/exe/bitcoinrbd +21 -15
- data/lib/bitcoin/chain_params.rb +1 -0
- data/lib/bitcoin/chainparams/mainnet.yml +1 -0
- data/lib/bitcoin/chainparams/regtest.yml +2 -1
- data/lib/bitcoin/chainparams/testnet.yml +2 -1
- data/lib/bitcoin/ext_key.rb +18 -0
- data/lib/bitcoin/network/message_handler.rb +4 -0
- data/lib/bitcoin/node/cli.rb +16 -2
- data/lib/bitcoin/rpc/http_server.rb +12 -3
- data/lib/bitcoin/rpc/request_handler.rb +14 -0
- data/lib/bitcoin/script/script.rb +2 -4
- data/lib/bitcoin/util.rb +3 -6
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +27 -13
- data/lib/bitcoin/wallet/base.rb +18 -5
- data/lib/bitcoin/wallet/master_key.rb +2 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3af358507bac7836acb65b028e77391acc664890
|
4
|
+
data.tar.gz: a9bcb5f19f17b9a2ecd1e5c0a43b3aa29c0329ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32e8a8c8d0a1a80ec3ffcfa373f41a5d0987d83da9616a568acb5f9982bcc25a91b406f2163242e6e45d43c0f956e47913d4a280c821cc7f724da7836192b235
|
7
|
+
data.tar.gz: b476e81be5caff178b14161f0873d770c91795d59b0e0b518fcc68b8f138b525c023002b3ab3846e5ae24a9b46d61eff3ff0a32fb1b37aea4386c08ade25296e
|
data/exe/bitcoinrbd
CHANGED
@@ -1,30 +1,36 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'thor'
|
3
3
|
require 'bitcoin'
|
4
|
-
require '
|
4
|
+
require 'daemon_spawn'
|
5
|
+
|
6
|
+
class BitcoinDaemon < DaemonSpawn::Base
|
7
|
+
|
8
|
+
def start(args)
|
9
|
+
puts "Bitcoinrb daemon start : #{Time.now}"
|
10
|
+
conf = Bitcoin::Node::Configuration.new(network: args.first[:network])
|
11
|
+
node = Bitcoin::Node::SPV.new(conf)
|
12
|
+
node.run
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
5
16
|
|
6
17
|
class Bitcoinrbd < Thor
|
7
18
|
|
8
|
-
|
9
|
-
|
10
|
-
desc 'start', 'start
|
19
|
+
class_option :network, aliases: '-n', default: :mainnet
|
20
|
+
|
21
|
+
desc 'start', 'start bitcoinrb daemon.'
|
11
22
|
def start
|
12
|
-
|
13
|
-
Bitcoin.chain_params = network
|
23
|
+
network = options['network'] ? options['network'].to_sym : :mainnet
|
24
|
+
Bitcoin.chain_params = network
|
14
25
|
FileUtils.mkdir_p(Bitcoin.base_dir)
|
15
|
-
execute_daemon(['start'])
|
26
|
+
execute_daemon(['start', network: network])
|
16
27
|
end
|
17
28
|
|
18
|
-
|
19
|
-
option :network, default: 'mainnet'
|
20
|
-
desc 'stop', 'start bitcoinrbd daemon process'
|
21
|
-
def stop
|
22
|
-
execute_daemon(['stop'])
|
23
|
-
end
|
29
|
+
private
|
24
30
|
|
25
31
|
def execute_daemon(cmd_args)
|
26
|
-
|
27
|
-
log_file: "#{Bitcoin.base_dir}/bitcoinrbd.log",
|
32
|
+
BitcoinDaemon.spawn!({working_dir: Bitcoin.base_dir,
|
33
|
+
log_file: "#{Bitcoin.base_dir}/log/bitcoinrbd.log",
|
28
34
|
pid_file: "#{Bitcoin.base_dir}/bitcoinrbd.pid",
|
29
35
|
sync_log: true,
|
30
36
|
singleton: true}, cmd_args)
|
data/lib/bitcoin/chain_params.rb
CHANGED
data/lib/bitcoin/ext_key.rb
CHANGED
@@ -64,6 +64,10 @@ module Bitcoin
|
|
64
64
|
key.pubkey
|
65
65
|
end
|
66
66
|
|
67
|
+
def hash160
|
68
|
+
Bitcoin.hash160(pub)
|
69
|
+
end
|
70
|
+
|
67
71
|
# get address
|
68
72
|
def addr
|
69
73
|
key.to_p2pkh
|
@@ -84,6 +88,11 @@ module Bitcoin
|
|
84
88
|
identifier.slice(0..7)
|
85
89
|
end
|
86
90
|
|
91
|
+
# whether hardened key.
|
92
|
+
def hardened?
|
93
|
+
number >= 2**31
|
94
|
+
end
|
95
|
+
|
87
96
|
# derive new key
|
88
97
|
def derive(number)
|
89
98
|
new_key = ExtKey.new
|
@@ -139,6 +148,10 @@ module Bitcoin
|
|
139
148
|
pubkey
|
140
149
|
end
|
141
150
|
|
151
|
+
def hash160
|
152
|
+
Bitcoin.hash160(pub)
|
153
|
+
end
|
154
|
+
|
142
155
|
# get address
|
143
156
|
def addr
|
144
157
|
Bitcoin::Key.new(pubkey: pubkey).to_p2pkh
|
@@ -171,6 +184,11 @@ module Bitcoin
|
|
171
184
|
Base58.encode(hex)
|
172
185
|
end
|
173
186
|
|
187
|
+
# whether hardened key.
|
188
|
+
def hardened?
|
189
|
+
number >= 2**31
|
190
|
+
end
|
191
|
+
|
174
192
|
# derive child key
|
175
193
|
def derive(number)
|
176
194
|
new_key = ExtPubkey.new
|
@@ -107,6 +107,7 @@ module Bitcoin
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def handshake_done
|
110
|
+
return unless @incomming_handshake && @outgoing_handshake
|
110
111
|
logger.info 'handshake finished.'
|
111
112
|
@connected = true
|
112
113
|
post_handshake
|
@@ -116,10 +117,13 @@ module Bitcoin
|
|
116
117
|
logger.info("receive version message. #{version.build_json}")
|
117
118
|
@version = version
|
118
119
|
send_message(Bitcoin::Message::VerAck.new)
|
120
|
+
@incomming_handshake = true
|
121
|
+
handshake_done
|
119
122
|
end
|
120
123
|
|
121
124
|
def on_ver_ack
|
122
125
|
logger.info('receive verack message.')
|
126
|
+
@outgoing_handshake = true
|
123
127
|
handshake_done
|
124
128
|
end
|
125
129
|
|
data/lib/bitcoin/node/cli.rb
CHANGED
@@ -35,6 +35,16 @@ module Bitcoin
|
|
35
35
|
request('sendrawtransaction', hex_tx)
|
36
36
|
end
|
37
37
|
|
38
|
+
desc 'createwallet "wallet_id"', 'Create new HD wallet. It returns an error if an existing wallet_id is specified. '
|
39
|
+
def createwallet(wallet_id)
|
40
|
+
request('createwallet', wallet_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'listwallets', 'Returns a list of currently loaded wallets. For full information on the wallet, use "getwalletinfo"'
|
44
|
+
def listwallets
|
45
|
+
request('listwallets')
|
46
|
+
end
|
47
|
+
|
38
48
|
private
|
39
49
|
|
40
50
|
def config
|
@@ -53,8 +63,12 @@ module Bitcoin
|
|
53
63
|
RestClient::Request.execute(method: :post, url: config.server_url, payload: data.to_json,
|
54
64
|
headers: {content_type: :json}) do |response, request, result|
|
55
65
|
return false if !result.kind_of?(Net::HTTPSuccess) && response.empty?
|
56
|
-
|
57
|
-
|
66
|
+
begin
|
67
|
+
json = JSON.parse(response.to_str)
|
68
|
+
puts JSON.pretty_generate(json)
|
69
|
+
rescue Exception
|
70
|
+
puts response.to_str
|
71
|
+
end
|
58
72
|
end
|
59
73
|
rescue Exception => e
|
60
74
|
puts e.message
|
@@ -31,13 +31,22 @@ module Bitcoin
|
|
31
31
|
operation = proc {
|
32
32
|
command, args = parse_json_params
|
33
33
|
logger.debug("process http request. command = #{command}")
|
34
|
-
|
34
|
+
begin
|
35
|
+
send(command, *args).to_json
|
36
|
+
rescue Exception => e
|
37
|
+
e
|
38
|
+
end
|
35
39
|
}
|
36
40
|
callback = proc{ |result|
|
37
41
|
response = EM::DelegatedHttpResponse.new(self)
|
38
|
-
|
42
|
+
if result.is_a?(Exception)
|
43
|
+
response.status = 500
|
44
|
+
response.content = result.message
|
45
|
+
else
|
46
|
+
response.status = 200
|
47
|
+
response.content = result
|
48
|
+
end
|
39
49
|
response.content_type 'application/json'
|
40
|
-
response.content = result
|
41
50
|
response.send_response
|
42
51
|
}
|
43
52
|
EM.defer(operation, callback)
|
@@ -78,6 +78,20 @@ module Bitcoin
|
|
78
78
|
tx.txid
|
79
79
|
end
|
80
80
|
|
81
|
+
# wallet api
|
82
|
+
|
83
|
+
# create wallet
|
84
|
+
def createwallet(wallet_id = 1, wallet_path_prefix = Bitcoin::Wallet::Base::DEFAULT_PATH_PREFIX)
|
85
|
+
wallet = Bitcoin::Wallet::Base.create(wallet_id, wallet_path_prefix)
|
86
|
+
node.wallet = wallet unless node.wallet
|
87
|
+
{wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic}
|
88
|
+
end
|
89
|
+
|
90
|
+
# get wallet list.
|
91
|
+
def listwallets(wallet_path_prefix = Bitcoin::Wallet::Base::DEFAULT_PATH_PREFIX)
|
92
|
+
Bitcoin::Wallet::Base.wallet_paths(wallet_path_prefix)
|
93
|
+
end
|
94
|
+
|
81
95
|
end
|
82
96
|
|
83
97
|
end
|
@@ -413,8 +413,7 @@ module Bitcoin
|
|
413
413
|
return nil unless p2pkh?
|
414
414
|
hash160 = chunks[2].pushed_data.bth
|
415
415
|
return nil unless hash160.htb.bytesize == 20
|
416
|
-
|
417
|
-
Bitcoin.encode_base58_address(hex)
|
416
|
+
Bitcoin.encode_base58_address(hash160, Bitcoin.chain_params.address_version)
|
418
417
|
end
|
419
418
|
|
420
419
|
# generate p2wpkh address. if script dose not p2wpkh, return nil.
|
@@ -427,8 +426,7 @@ module Bitcoin
|
|
427
426
|
return nil unless p2sh?
|
428
427
|
hash160 = chunks[1].pushed_data.bth
|
429
428
|
return nil unless hash160.htb.bytesize == 20
|
430
|
-
|
431
|
-
Bitcoin.encode_base58_address(hex)
|
429
|
+
Bitcoin.encode_base58_address(hash160, Bitcoin.chain_params.p2sh_version)
|
432
430
|
end
|
433
431
|
|
434
432
|
# generate p2wsh address. if script dose not p2wsh, return nil.
|
data/lib/bitcoin/util.rb
CHANGED
@@ -82,12 +82,9 @@ module Bitcoin
|
|
82
82
|
Digest::RMD160.hexdigest(Digest::SHA256.digest(hex.htb))
|
83
83
|
end
|
84
84
|
|
85
|
-
def encode_base58_address(hex)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
def decode_base58_address(addr)
|
90
|
-
Base58.decode(addr)
|
85
|
+
def encode_base58_address(hex, addr_version)
|
86
|
+
base = addr_version + hex
|
87
|
+
Base58.encode(base + calc_checksum(base))
|
91
88
|
end
|
92
89
|
|
93
90
|
def calc_checksum(hex)
|
data/lib/bitcoin/version.rb
CHANGED
@@ -9,12 +9,12 @@ module Bitcoin
|
|
9
9
|
attr_reader :purpose # either 44 or 49
|
10
10
|
attr_reader :index # BIP-44 index
|
11
11
|
attr_reader :name # account name
|
12
|
-
attr_accessor :receive_depth # receive address depth
|
13
|
-
attr_accessor :change_depth # change address depth
|
12
|
+
attr_accessor :receive_depth # receive address depth(address index)
|
13
|
+
attr_accessor :change_depth # change address depth(address index)
|
14
14
|
attr_accessor :lookahead
|
15
15
|
attr_accessor :wallet
|
16
16
|
|
17
|
-
def initialize(purpose = PURPOSE_TYPE[:
|
17
|
+
def initialize(purpose = PURPOSE_TYPE[:nested_witness], index = 0, name = '')
|
18
18
|
@purpose = purpose
|
19
19
|
@index = index
|
20
20
|
@name = name
|
@@ -46,22 +46,20 @@ module Bitcoin
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def init
|
49
|
-
|
50
|
-
|
51
|
-
derive_change(index)
|
52
|
-
end
|
49
|
+
@receive_depth = lookahead
|
50
|
+
@change_depth = lookahead
|
53
51
|
@index = wallet.accounts.size
|
54
52
|
save
|
55
53
|
end
|
56
54
|
|
57
55
|
# derive receive key
|
58
|
-
def derive_receive(
|
59
|
-
|
56
|
+
def derive_receive(address_index)
|
57
|
+
derive_path(0, address_index)
|
60
58
|
end
|
61
59
|
|
62
60
|
# derive change key
|
63
|
-
def derive_change(
|
64
|
-
|
61
|
+
def derive_change(address_index)
|
62
|
+
derive_path(1, address_index)
|
65
63
|
end
|
66
64
|
|
67
65
|
# save this account payload to database.
|
@@ -69,11 +67,27 @@ module Bitcoin
|
|
69
67
|
wallet.db.save_account(self)
|
70
68
|
end
|
71
69
|
|
70
|
+
# get the list of derived keys for receive key.
|
71
|
+
def derived_receive_keys
|
72
|
+
receive_depth.times.map{|i|derive_key(0,i)}
|
73
|
+
end
|
74
|
+
|
75
|
+
# get the list of derived keys for change key.
|
76
|
+
def derived_change_keys
|
77
|
+
receive_depth.times.map{|i|derive_key(1,i)}
|
78
|
+
end
|
79
|
+
|
72
80
|
private
|
73
81
|
|
74
|
-
|
75
|
-
|
82
|
+
def derive_key(branch, address_index)
|
83
|
+
account_key.derive(branch).derive(address_index)
|
84
|
+
end
|
76
85
|
|
86
|
+
def account_key
|
87
|
+
return @cached_account_key if @cached_account_key
|
88
|
+
coin_type = Bitcoin.chain_params.bip44_coin_type
|
89
|
+
# m / purpose' / coin_type' / account_index'
|
90
|
+
@cached_account_key = wallet.master_key.key.derive(2**31 + purpose).derive(2**31 + coin_type).derive(2**31 + index)
|
77
91
|
end
|
78
92
|
|
79
93
|
end
|
data/lib/bitcoin/wallet/base.rb
CHANGED
@@ -6,8 +6,9 @@ module Bitcoin
|
|
6
6
|
|
7
7
|
attr_accessor :wallet_id
|
8
8
|
attr_reader :db
|
9
|
+
attr_reader :path
|
9
10
|
|
10
|
-
DEFAULT_PATH_PREFIX = "#{Bitcoin.base_dir}/db/wallet"
|
11
|
+
DEFAULT_PATH_PREFIX = "#{Bitcoin.base_dir}/db/wallet/"
|
11
12
|
|
12
13
|
# Create new wallet. If wallet already exist, throw error.
|
13
14
|
# The wallet generates a seed using SecureRandom and store to db at initialization.
|
@@ -21,6 +22,7 @@ module Bitcoin
|
|
21
22
|
raise RuntimeError, 'the seed already exist.' if w.db.registered_master?
|
22
23
|
master = Bitcoin::Wallet::MasterKey.generate
|
23
24
|
w.db.register_master_key(master)
|
25
|
+
w.create_account('Default')
|
24
26
|
w
|
25
27
|
end
|
26
28
|
|
@@ -31,12 +33,22 @@ module Bitcoin
|
|
31
33
|
self.new(wallet_id, path_prefix)
|
32
34
|
end
|
33
35
|
|
36
|
+
# get wallets path
|
37
|
+
# @return [Array] Array of paths for each wallet dir.
|
38
|
+
def self.wallet_paths(path_prefix = DEFAULT_PATH_PREFIX)
|
39
|
+
Dir.glob("#{path_prefix}wallet*/")
|
40
|
+
end
|
41
|
+
|
34
42
|
# get account list based on BIP-44
|
35
43
|
def accounts
|
36
|
-
db.accounts.map
|
44
|
+
db.accounts.map do |raw|
|
45
|
+
a = Account.parse_from_payload(raw)
|
46
|
+
a.wallet = self
|
47
|
+
a
|
48
|
+
end
|
37
49
|
end
|
38
50
|
|
39
|
-
def create_account(purpose = Account::PURPOSE_TYPE[:
|
51
|
+
def create_account(purpose = Account::PURPOSE_TYPE[:nested_witness], index = 0, name)
|
40
52
|
account = Account.new(purpose, index, name)
|
41
53
|
account.wallet = self
|
42
54
|
account.init
|
@@ -69,12 +81,13 @@ module Bitcoin
|
|
69
81
|
private
|
70
82
|
|
71
83
|
def initialize(wallet_id, path_prefix)
|
72
|
-
@
|
84
|
+
@path = "#{path_prefix}wallet#{wallet_id}/"
|
85
|
+
@db = Bitcoin::Wallet::DB.new(@path)
|
73
86
|
@wallet_id = wallet_id
|
74
87
|
end
|
75
88
|
|
76
89
|
def self.exist?(wallet_id, path_prefix)
|
77
|
-
path = "#{path_prefix}
|
90
|
+
path = "#{path_prefix}wallet#{wallet_id}"
|
78
91
|
Dir.exist?(path)
|
79
92
|
end
|
80
93
|
|
@@ -21,7 +21,7 @@ module Bitcoin
|
|
21
21
|
# generate new master key.
|
22
22
|
# @return Bitcoin::Wallet::MasterKey
|
23
23
|
def self.generate
|
24
|
-
entropy = SecureRandom.hex(
|
24
|
+
entropy = SecureRandom.hex(32)
|
25
25
|
mnemonic = Bitcoin::Mnemonic.new('english')
|
26
26
|
self.recover_from_words(mnemonic.to_mnemonic(entropy))
|
27
27
|
end
|
@@ -42,6 +42,7 @@ module Bitcoin
|
|
42
42
|
flag, payload = unpack_var_int(payload)
|
43
43
|
raise 'encrypted flag is invalid.' unless [0, 1].include?(flag)
|
44
44
|
salt, payload = unpack_var_string(payload)
|
45
|
+
salt = '' unless salt
|
45
46
|
seed, payload = unpack_var_string(payload)
|
46
47
|
self.new(seed.bth, salt: salt.bth, encrypted: flag == 1)
|
47
48
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitcoinrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-12-
|
11
|
+
date: 2017-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ecdsa
|
@@ -373,7 +373,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
373
373
|
version: '0'
|
374
374
|
requirements: []
|
375
375
|
rubyforge_project:
|
376
|
-
rubygems_version: 2.6.
|
376
|
+
rubygems_version: 2.6.13
|
377
377
|
signing_key:
|
378
378
|
specification_version: 4
|
379
379
|
summary: "[WIP]The implementation of Bitcoin Protocol for Ruby."
|