glueby 0.4.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +477 -387
  3. data/glueby.gemspec +33 -33
  4. data/lib/generators/glueby/contract/templates/initializer.rb.erb +8 -8
  5. data/lib/generators/glueby/contract/templates/key_table.rb.erb +16 -16
  6. data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +4 -1
  7. data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +16 -16
  8. data/lib/glueby/block_syncer.rb +97 -97
  9. data/lib/glueby/configuration.rb +32 -2
  10. data/lib/glueby/contract/active_record/timestamp.rb +3 -1
  11. data/lib/glueby/contract/errors.rb +1 -0
  12. data/lib/glueby/contract/fee_estimator.rb +5 -1
  13. data/lib/glueby/contract/timestamp/syncer.rb +13 -13
  14. data/lib/glueby/contract/timestamp.rb +153 -102
  15. data/lib/glueby/contract/token.rb +270 -244
  16. data/lib/glueby/contract/tx_builder.rb +97 -30
  17. data/lib/glueby/fee_provider/tasks.rb +140 -140
  18. data/lib/glueby/fee_provider.rb +11 -0
  19. data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +153 -151
  20. data/lib/glueby/internal/wallet/active_record/utxo.rb +51 -51
  21. data/lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb +13 -13
  22. data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +159 -151
  23. data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +194 -186
  24. data/lib/glueby/internal/wallet.rb +164 -162
  25. data/lib/glueby/railtie.rb +10 -9
  26. data/lib/glueby/utxo_provider/tasks.rb +135 -0
  27. data/lib/glueby/utxo_provider.rb +85 -0
  28. data/lib/glueby/version.rb +3 -3
  29. data/lib/glueby.rb +40 -39
  30. data/lib/tasks/glueby/block_syncer.rake +28 -28
  31. data/lib/tasks/glueby/contract/timestamp.rake +46 -39
  32. data/lib/tasks/glueby/fee_provider.rake +18 -18
  33. data/lib/tasks/glueby/utxo_provider.rake +18 -0
  34. metadata +5 -2
@@ -1,186 +1,194 @@
1
- # frozen_string_literal: true
2
-
3
- require 'securerandom'
4
- require 'bigdecimal'
5
-
6
- module Glueby
7
- module Internal
8
- class Wallet
9
- class TapyrusCoreWalletAdapter < AbstractWalletAdapter
10
- module Util
11
- module_function
12
-
13
- # Convert TPC to tapyrus. 1 TPC is 10**8 tapyrus.
14
- # @param [String] tpc
15
- # @return [Integer] tapyrus
16
- #
17
- # Example) "0.00000010" to 10.
18
- def tpc_to_tapyrus(tpc)
19
- (BigDecimal(tpc) * 10**8).to_i
20
- end
21
- end
22
-
23
- include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
24
-
25
- WALLET_PREFIX = 'wallet-'
26
-
27
- RPC_WALLET_ERROR_ERROR_CODE = -4 # Unspecified problem with wallet (key not found etc.)
28
- RPC_WALLET_NOT_FOUND_ERROR_CODE = -18 # Invalid wallet specified
29
-
30
- def create_wallet(wallet_id = nil)
31
- wallet_id = SecureRandom.hex(16) unless wallet_id
32
- begin
33
- RPC.client.createwallet(wallet_name(wallet_id))
34
- rescue Tapyrus::RPC::Error => ex
35
- if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Wallet wallet-#{wallet_id} already exists\./ =~ ex.rpc_error['message']
36
- raise Errors::WalletAlreadyCreated, "Wallet #{wallet_id} has been already created."
37
- else
38
- raise ex
39
- end
40
- end
41
-
42
- wallet_id
43
- end
44
-
45
- # On TapyrusCoreWallet, there are no way to delete real wallet via RPC.
46
- # So, here just unload.
47
- def delete_wallet(wallet_id)
48
- unload_wallet(wallet_id)
49
- end
50
-
51
- def load_wallet(wallet_id)
52
- RPC.client.loadwallet(wallet_name(wallet_id))
53
- rescue Tapyrus::RPC::Error => ex
54
- if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Duplicate -wallet filename specified/ =~ ex.rpc_error['message']
55
- raise Errors::WalletAlreadyLoaded, "Wallet #{wallet_id} has been already loaded."
56
- elsif ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
57
- raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found"
58
- else
59
- raise ex
60
- end
61
- end
62
-
63
- def unload_wallet(wallet_id)
64
- RPC.client.unloadwallet(wallet_name(wallet_id))
65
- end
66
-
67
- def wallets
68
- RPC.client.listwallets.map do |wallet_name|
69
- match = /\A#{WALLET_PREFIX}(?<wallet_id>[0-9A-Fa-f]{32})\z/.match(wallet_name)
70
- next unless match
71
-
72
- match[:wallet_id]
73
- end.compact
74
- end
75
-
76
- def balance(wallet_id, only_finalized = true)
77
- perform_as(wallet_id) do |client|
78
- confirmed = tpc_to_tapyrus(client.getbalance)
79
- return confirmed if only_finalized
80
-
81
- confirmed + tpc_to_tapyrus(client.getunconfirmedbalance)
82
- end
83
- end
84
-
85
- def list_unspent(wallet_id, only_finalized = true, label = nil)
86
- perform_as(wallet_id) do |client|
87
- min_conf = only_finalized ? 1 : 0
88
- res = client.listunspent(min_conf)
89
-
90
- res = res.filter { |i| i['label'] == label } if label && (label != :unlabeled)
91
- res = res.filter { |i| i['label'] == "" } if label == :unlabeled
92
-
93
- res.map do |i|
94
- script = Tapyrus::Script.parse_from_payload(i['scriptPubKey'].htb)
95
- color_id = if script.cp2pkh? || script.cp2sh?
96
- Tapyrus::Color::ColorIdentifier.parse_from_payload(script.chunks[0].pushed_data).to_hex
97
- end
98
- {
99
- txid: i['txid'],
100
- vout: i['vout'],
101
- script_pubkey: i['scriptPubKey'],
102
- color_id: color_id,
103
- amount: tpc_to_tapyrus(i['amount']),
104
- finalized: i['confirmations'] != 0
105
- }
106
- end
107
- end
108
- end
109
-
110
- def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
111
- perform_as(wallet_id) do |client|
112
- res = client.signrawtransactionwithwallet(tx.to_hex, prevtxs, encode_sighashtype(sighashtype))
113
- if res['complete']
114
- Tapyrus::Tx.parse_from_payload(res['hex'].htb)
115
- else
116
- raise res['errors'].to_json
117
- end
118
- end
119
- end
120
-
121
- def broadcast(wallet_id, tx, &block)
122
- perform_as(wallet_id) do |client|
123
- block.call(tx) if block
124
- client.sendrawtransaction(tx.to_hex)
125
- end
126
- end
127
-
128
- def receive_address(wallet_id, label = nil)
129
- perform_as(wallet_id) do |client|
130
- client.getnewaddress(label || '')
131
- end
132
- end
133
-
134
- def change_address(wallet_id)
135
- perform_as(wallet_id) do |client|
136
- client.getrawchangeaddress
137
- end
138
- end
139
-
140
- def create_pubkey(wallet_id)
141
- perform_as(wallet_id) do |client|
142
- address = client.getnewaddress('')
143
- info = client.getaddressinfo(address)
144
- Tapyrus::Key.new(pubkey: info['pubkey'])
145
- end
146
- end
147
-
148
- private
149
-
150
- def perform_as(wallet_id)
151
- RPC.perform_as(wallet_name(wallet_id)) do |client|
152
- begin
153
- yield(client)
154
- rescue Tapyrus::RPC::Error => ex
155
- if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
156
- raise Errors::WalletUnloaded, "The wallet #{wallet_id} is unloaded. You should load before use it."
157
- else
158
- raise ex
159
- end
160
- end
161
- end
162
- end
163
-
164
- def wallet_name(wallet_id)
165
- "#{WALLET_PREFIX}#{wallet_id}"
166
- end
167
-
168
- def encode_sighashtype(sighashtype)
169
- type = case sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
170
- when Tapyrus::SIGHASH_TYPE[:all] then 'ALL'
171
- when Tapyrus::SIGHASH_TYPE[:none] then 'NONE'
172
- when Tapyrus::SIGHASH_TYPE[:single] then 'SIGNLE'
173
- else
174
- raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
175
- end
176
-
177
- if sighashtype & Tapyrus::SIGHASH_TYPE[:anyonecanpay] == 0x80
178
- type += '|ANYONECANPAY'
179
- end
180
-
181
- type
182
- end
183
- end
184
- end
185
- end
186
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'bigdecimal'
5
+
6
+ module Glueby
7
+ module Internal
8
+ class Wallet
9
+ class TapyrusCoreWalletAdapter < AbstractWalletAdapter
10
+ module Util
11
+ module_function
12
+
13
+ # Convert TPC to tapyrus. 1 TPC is 10**8 tapyrus.
14
+ # @param [String] tpc
15
+ # @return [Integer] tapyrus
16
+ #
17
+ # Example) "0.00000010" to 10.
18
+ def tpc_to_tapyrus(tpc)
19
+ (BigDecimal(tpc) * 10**8).to_i
20
+ end
21
+ end
22
+
23
+ include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
24
+
25
+ WALLET_PREFIX = 'wallet-'
26
+
27
+ RPC_WALLET_ERROR_ERROR_CODE = -4 # Unspecified problem with wallet (key not found etc.)
28
+ RPC_WALLET_NOT_FOUND_ERROR_CODE = -18 # Invalid wallet specified
29
+
30
+ def create_wallet(wallet_id = nil)
31
+ wallet_id = SecureRandom.hex(16) unless wallet_id
32
+ begin
33
+ RPC.client.createwallet(wallet_name(wallet_id))
34
+ rescue Tapyrus::RPC::Error => ex
35
+ if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Wallet wallet-#{wallet_id} already exists\./ =~ ex.rpc_error['message']
36
+ raise Errors::WalletAlreadyCreated, "Wallet #{wallet_id} has been already created."
37
+ else
38
+ raise ex
39
+ end
40
+ end
41
+
42
+ wallet_id
43
+ end
44
+
45
+ # On TapyrusCoreWallet, there are no way to delete real wallet via RPC.
46
+ # So, here just unload.
47
+ def delete_wallet(wallet_id)
48
+ unload_wallet(wallet_id)
49
+ end
50
+
51
+ def load_wallet(wallet_id)
52
+ RPC.client.loadwallet(wallet_name(wallet_id))
53
+ rescue Tapyrus::RPC::Error => ex
54
+ if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Duplicate -wallet filename specified/ =~ ex.rpc_error['message']
55
+ raise Errors::WalletAlreadyLoaded, "Wallet #{wallet_id} has been already loaded."
56
+ elsif ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
57
+ raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found"
58
+ else
59
+ raise ex
60
+ end
61
+ end
62
+
63
+ def unload_wallet(wallet_id)
64
+ RPC.client.unloadwallet(wallet_name(wallet_id))
65
+ end
66
+
67
+ def wallets
68
+ RPC.client.listwallets.map do |wallet_name|
69
+ match = /\A#{WALLET_PREFIX}(?<wallet_id>[0-9A-Fa-f]{32})\z/.match(wallet_name)
70
+ next unless match
71
+
72
+ match[:wallet_id]
73
+ end.compact
74
+ end
75
+
76
+ def balance(wallet_id, only_finalized = true)
77
+ perform_as(wallet_id) do |client|
78
+ confirmed = tpc_to_tapyrus(client.getbalance)
79
+ return confirmed if only_finalized
80
+
81
+ confirmed + tpc_to_tapyrus(client.getunconfirmedbalance)
82
+ end
83
+ end
84
+
85
+ # If label=nil, it will return unlabeled utxos to protect labeled utxos for specific purpose
86
+ # If label=:all, it will return all utxos
87
+ def list_unspent(wallet_id, only_finalized = true, label = nil)
88
+ perform_as(wallet_id) do |client|
89
+ min_conf = only_finalized ? 1 : 0
90
+ res = client.listunspent(min_conf)
91
+
92
+ if [:unlabeled, nil].include?(label)
93
+ res = res.filter { |i| i['label'] == "" }
94
+ elsif label && (label != :all)
95
+ res = res.filter { |i| i['label'] == label }
96
+ else
97
+ res
98
+ end
99
+
100
+ res.map do |i|
101
+ script = Tapyrus::Script.parse_from_payload(i['scriptPubKey'].htb)
102
+ color_id = if script.cp2pkh? || script.cp2sh?
103
+ Tapyrus::Color::ColorIdentifier.parse_from_payload(script.chunks[0].pushed_data).to_hex
104
+ end
105
+ {
106
+ txid: i['txid'],
107
+ vout: i['vout'],
108
+ script_pubkey: i['scriptPubKey'],
109
+ color_id: color_id,
110
+ amount: tpc_to_tapyrus(i['amount']),
111
+ finalized: i['confirmations'] != 0,
112
+ label: i['label']
113
+ }
114
+ end
115
+ end
116
+ end
117
+
118
+ def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
119
+ perform_as(wallet_id) do |client|
120
+ res = client.signrawtransactionwithwallet(tx.to_hex, prevtxs, encode_sighashtype(sighashtype))
121
+ if res['complete']
122
+ Tapyrus::Tx.parse_from_payload(res['hex'].htb)
123
+ else
124
+ raise res['errors'].to_json
125
+ end
126
+ end
127
+ end
128
+
129
+ def broadcast(wallet_id, tx, &block)
130
+ perform_as(wallet_id) do |client|
131
+ block.call(tx) if block
132
+ client.sendrawtransaction(tx.to_hex)
133
+ end
134
+ end
135
+
136
+ def receive_address(wallet_id, label = nil)
137
+ perform_as(wallet_id) do |client|
138
+ client.getnewaddress(label || '')
139
+ end
140
+ end
141
+
142
+ def change_address(wallet_id)
143
+ perform_as(wallet_id) do |client|
144
+ client.getrawchangeaddress
145
+ end
146
+ end
147
+
148
+ def create_pubkey(wallet_id)
149
+ perform_as(wallet_id) do |client|
150
+ address = client.getnewaddress('')
151
+ info = client.getaddressinfo(address)
152
+ Tapyrus::Key.new(pubkey: info['pubkey'])
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def perform_as(wallet_id)
159
+ RPC.perform_as(wallet_name(wallet_id)) do |client|
160
+ begin
161
+ yield(client)
162
+ rescue Tapyrus::RPC::Error => ex
163
+ if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
164
+ raise Errors::WalletUnloaded, "The wallet #{wallet_id} is unloaded. You should load before use it."
165
+ else
166
+ raise ex
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ def wallet_name(wallet_id)
173
+ "#{WALLET_PREFIX}#{wallet_id}"
174
+ end
175
+
176
+ def encode_sighashtype(sighashtype)
177
+ type = case sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
178
+ when Tapyrus::SIGHASH_TYPE[:all] then 'ALL'
179
+ when Tapyrus::SIGHASH_TYPE[:none] then 'NONE'
180
+ when Tapyrus::SIGHASH_TYPE[:single] then 'SIGNLE'
181
+ else
182
+ raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
183
+ end
184
+
185
+ if sighashtype & Tapyrus::SIGHASH_TYPE[:anyonecanpay] == 0x80
186
+ type += '|ANYONECANPAY'
187
+ end
188
+
189
+ type
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end