glueby 0.7.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 165b5d6b9fc1cfae9bb7ba852632a0f099ef356bcb52fe0a9937b2c2d1289b51
4
- data.tar.gz: 6d1a3b9cc1c3668a40ce11920af51cb8af8fc9d94163e43eca295a40d48889f0
3
+ metadata.gz: 5cae8a0e06be77affdb1895724a26137b5fde2bab8cc27fb73d9aa45f2c351c5
4
+ data.tar.gz: 2cc677f69e1cc1ad3e6468662e476b4bd5082b0e765f15f44b6b63426b2663f9
5
5
  SHA512:
6
- metadata.gz: d7d6f4cf39f9b2c4580d1360817baa960bf72fe77075a038a7784b2f9191bb0cb9925c27d8b697c5d0a75e0d0bc31d35cfda14e3a88e18da88dba09e38aa5246
7
- data.tar.gz: 691dd9cec6eb183f537c70efab553bd9224bea93fe21da6cb8ce2afea14549e74bd02fa8e73dd3518703b6b390f217d0b02ad3d85649b8eea4e6b6583c1fd196
6
+ metadata.gz: 3e0a80b28635f37a37b4d37eac3fc3bc697bfb3ce580a8c481c60e40fe7776bfcfcb24720d33771e5d73d19c745d03afbee7b380dedd3d0ec51b7258bbb9363b
7
+ data.tar.gz: fd3d0edae5b99212cea7e4a710dd8f52173e1b707617beb881e7f9715afac575e76d5abc18b291f7defa62c7acb4c4e5772163a0953126a2f3fcfeb6a72aa953
data/README.md CHANGED
@@ -457,6 +457,11 @@ Glueby.configure do |config|
457
457
  end
458
458
  ```
459
459
 
460
+ ## Error handling
461
+
462
+ Glueby has base error classes like `Glueby::Error` and `Glueby::ArgumentError`.
463
+ `Glueby::Error` is the base class for the all errors that are raises in the glueby.
464
+ `Glueby::ArgumentError` is the error class for argument errors in public contracts. This notifies the arguments is something wrong to glueby library user-side.
460
465
 
461
466
  ## Development
462
467
 
data/glueby.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ["lib"]
28
28
 
29
- spec.add_runtime_dependency 'tapyrus', '>= 0.2.9'
29
+ spec.add_runtime_dependency 'tapyrus', '>= 0.2.13'
30
30
  spec.add_runtime_dependency 'activerecord', '~> 6.1.3'
31
31
  spec.add_development_dependency 'sqlite3'
32
32
  spec.add_development_dependency 'rails', '~> 6.1.3'
@@ -3,13 +3,100 @@ module Glueby
3
3
  class SystemInformation < ::ActiveRecord::Base
4
4
 
5
5
  def self.synced_block_height
6
- SystemInformation.find_by(info_key: "synced_block_number")
6
+ find_by(info_key: "synced_block_number")
7
+ end
8
+
9
+ # Return if wallet allows to use only finalized utxo.
10
+ # @return [Boolean] true if wallet allows to use only finalized utxo, otherwise false.
11
+ def self.use_only_finalized_utxo?
12
+ find_by(info_key: "use_only_finalized_utxo")&.int_value != 0
13
+ end
14
+
15
+
16
+ # Set use_only_finalized_utxo
17
+ # @param [Boolean] status status of whether to use only finalized utxo
18
+ def self.set_use_only_finalized_utxo(status)
19
+ current = find_by(info_key: "use_only_finalized_utxo")
20
+ if current
21
+ current.update!(info_value: boolean_to_string(status))
22
+ else
23
+ create!(
24
+ info_key: "use_only_finalized_utxo",
25
+ info_value: boolean_to_string(status)
26
+ )
27
+ end
28
+ end
29
+
30
+ # Return default value of the utxo provider
31
+ # @return [Integer] default value of utxo provider
32
+ def self.utxo_provider_default_value
33
+ find_by(info_key: "utxo_provider_default_value")&.int_value
34
+ end
35
+
36
+ # Set utxo_provider_default_value
37
+ # @param [Integer] value default value for utxo provider
38
+ def self.set_utxo_provider_default_value(value)
39
+ current = find_by(info_key: "utxo_provider_default_value")
40
+ if current
41
+ current.update!(info_value: value)
42
+ else
43
+ create!(
44
+ info_key: "utxo_provider_default_value",
45
+ info_value: value
46
+ )
47
+ end
48
+ end
49
+
50
+ # Return pool size of the utxo provider
51
+ # @return [Integer] pool size of utxo provider
52
+ def self.utxo_provider_pool_size
53
+ find_by(info_key: "utxo_provider_pool_size")&.int_value
54
+ end
55
+
56
+ # Set utxo_provider_pool_size
57
+ # @param [Integer] size pool size of the utxo provider
58
+ def self.set_utxo_provider_pool_size(size)
59
+ current = find_by(info_key: "utxo_provider_pool_size")
60
+ if current
61
+ current.update!(info_value: size)
62
+ else
63
+ create!(
64
+ info_key: "utxo_provider_pool_size",
65
+ info_value: size
66
+ )
67
+ end
68
+ end
69
+
70
+ # If return timestamp is to be executed immediately
71
+ # @return [Boolean] true status of broadcast_on_background
72
+ def self.broadcast_on_background?
73
+ find_by(info_key: "broadcast_on_background")&.int_value != 0
74
+ end
75
+
76
+ # Set the status of broadcast_on_background
77
+ # @param [Boolean] status status of broadcast_on_background
78
+ def self.set_broadcast_on_background(status)
79
+ current = find_by(info_key: "broadcast_on_background")
80
+ if current
81
+ current.update!(info_value: boolean_to_string(status))
82
+ else
83
+ create!(
84
+ info_key: "broadcast_on_background",
85
+ info_value: boolean_to_string(status)
86
+ )
87
+ end
7
88
  end
8
89
 
9
90
  def int_value
10
91
  info_value.to_i
11
92
  end
12
93
 
94
+ private
95
+
96
+ def self.boolean_to_string(status)
97
+ status ? "1" : "0"
98
+ end
99
+
13
100
  end
14
101
  end
15
102
  end
@@ -15,7 +15,7 @@ module Glueby
15
15
  alias_method :use_utxo_provider?, :use_utxo_provider
16
16
 
17
17
  module Errors
18
- class InvalidConfiguration < StandardError; end
18
+ class InvalidConfiguration < Error; end
19
19
  end
20
20
 
21
21
  def initialize
@@ -35,16 +35,16 @@ module Glueby
35
35
  if funding_tx
36
36
  ::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
37
37
  wallet.internal_wallet.broadcast(funding_tx)
38
- logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
39
38
  end
39
+ logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
40
40
  end
41
41
  ::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
42
42
  wallet.internal_wallet.broadcast(tx) do |tx|
43
43
  assign_attributes(txid: tx.txid, status: :unconfirmed, p2c_address: p2c_address, payment_base: payment_base)
44
44
  save!
45
45
  end
46
- logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
47
46
  end
47
+ logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
48
48
  true
49
49
  rescue => e
50
50
  logger.error("failed to broadcast (id=#{id}, reason=#{e.message})")
@@ -1,15 +1,18 @@
1
1
  module Glueby
2
2
  module Contract
3
3
  module Errors
4
- class InsufficientFunds < StandardError; end
5
- class InsufficientTokens < StandardError; end
6
- class InvalidAmount < StandardError; end
7
- class InvalidTokenType < StandardError; end
8
- class InvalidTimestampType < StandardError; end
9
- class TxAlreadyBroadcasted < StandardError; end
10
- class UnsupportedTokenType < StandardError; end
11
- class UnknownScriptPubkey < StandardError; end
12
- class UnsupportedDigestType < StandardError; end
4
+ class InsufficientFunds < Error; end
5
+ class InsufficientTokens < Error; end
6
+ class TxAlreadyBroadcasted < Error; end
7
+
8
+ # Argument Errors
9
+ class InvalidAmount < ArgumentError; end
10
+ class InvalidSplit < ArgumentError; end
11
+ class InvalidTokenType < ArgumentError; end
12
+ class InvalidTimestampType < ArgumentError; end
13
+ class UnsupportedTokenType < ArgumentError; end
14
+ class UnknownScriptPubkey < ArgumentError; end
15
+ class UnsupportedDigestType < ArgumentError; end
13
16
  end
14
17
  end
15
18
  end
@@ -56,18 +56,21 @@ module Glueby
56
56
  # @param issuer [Glueby::Wallet]
57
57
  # @param token_type [TokenTypes]
58
58
  # @param amount [Integer]
59
+ # @param split [Integer] The tx outputs should be split by specified number.
59
60
  # @return [Array<token, Array<tx>>] Tuple of tx array and token object
60
61
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
61
62
  # @raise [InvalidAmount] if amount is not positive integer.
63
+ # @raise [InvalidSplit] if split is greater than 1 for NFT token.
62
64
  # @raise [UnspportedTokenType] if token is not supported.
63
- def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1)
65
+ def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1, split: 1)
64
66
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
67
+ raise Glueby::Contract::Errors::InvalidSplit if token_type == Tapyrus::Color::TokenTypes::NFT && split > 1
65
68
 
66
69
  txs, color_id = case token_type
67
70
  when Tapyrus::Color::TokenTypes::REISSUABLE
68
- issue_reissuable_token(issuer: issuer, amount: amount)
71
+ issue_reissuable_token(issuer: issuer, amount: amount, split: split)
69
72
  when Tapyrus::Color::TokenTypes::NON_REISSUABLE
70
- issue_non_reissuable_token(issuer: issuer, amount: amount)
73
+ issue_non_reissuable_token(issuer: issuer, amount: amount, split: split)
71
74
  when Tapyrus::Color::TokenTypes::NFT
72
75
  issue_nft_token(issuer: issuer)
73
76
  else
@@ -77,29 +80,36 @@ module Glueby
77
80
  [new(color_id: color_id), txs]
78
81
  end
79
82
 
83
+ def only_finalized?
84
+ Glueby::AR::SystemInformation.use_only_finalized_utxo?
85
+ end
86
+
80
87
  private
81
88
 
82
- def issue_reissuable_token(issuer:, amount:)
83
- funding_tx = create_funding_tx(wallet: issuer)
89
+ def issue_reissuable_token(issuer:, amount:, split: 1)
90
+ funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?)
84
91
  script_pubkey = funding_tx.outputs.first.script_pubkey
85
92
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
86
93
 
94
+ ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
95
+ funding_tx = issuer.internal_wallet.broadcast(funding_tx)
96
+ end
97
+
87
98
  ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
88
99
  # Store the script_pubkey for reissue the token.
89
100
  Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
90
101
 
91
- funding_tx = issuer.internal_wallet.broadcast(funding_tx)
92
- tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
102
+ tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
93
103
  tx = issuer.internal_wallet.broadcast(tx)
94
104
  [[funding_tx, tx], color_id]
95
105
  end
96
106
  end
97
107
 
98
- def issue_non_reissuable_token(issuer:, amount:)
99
- funding_tx = create_funding_tx(wallet: issuer) if Glueby.configuration.use_utxo_provider?
108
+ def issue_non_reissuable_token(issuer:, amount:, split: 1)
109
+ funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
100
110
  funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
101
111
 
102
- tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
112
+ tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
103
113
  tx = issuer.internal_wallet.broadcast(tx)
104
114
 
105
115
  out_point = tx.inputs.first.out_point
@@ -112,7 +122,7 @@ module Glueby
112
122
  end
113
123
 
114
124
  def issue_nft_token(issuer:)
115
- funding_tx = create_funding_tx(wallet: issuer) if Glueby.configuration.use_utxo_provider?
125
+ funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
116
126
  funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
117
127
 
118
128
  tx = create_issue_tx_for_nft_token(funding_tx: funding_tx, issuer: issuer)
@@ -134,19 +144,20 @@ module Glueby
134
144
  # A wallet can issue the token only when it is REISSUABLE token.
135
145
  # @param issuer [Glueby::Wallet]
136
146
  # @param amount [Integer]
147
+ # @param split [Integer]
137
148
  # @return [Array<String, tx>] Tuple of color_id and tx object
138
149
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
139
150
  # @raise [InvalidAmount] if amount is not positive integer.
140
151
  # @raise [InvalidTokenType] if token is not reissuable.
141
152
  # @raise [UnknownScriptPubkey] when token is reissuable but it doesn't know script pubkey to issue token.
142
- def reissue!(issuer:, amount:)
153
+ def reissue!(issuer:, amount:, split: 1)
143
154
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
144
155
  raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE
145
156
 
146
157
  if validate_reissuer(wallet: issuer)
147
- funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey)
158
+ funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey, only_finalized: only_finalized?)
148
159
  funding_tx = issuer.internal_wallet.broadcast(funding_tx)
149
- tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id)
160
+ tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id, split: split)
150
161
  tx = issuer.internal_wallet.broadcast(tx)
151
162
 
152
163
  [color_id, tx]
@@ -167,10 +178,17 @@ module Glueby
167
178
  def transfer!(sender:, receiver_address:, amount: 1)
168
179
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
169
180
 
170
- funding_tx = create_funding_tx(wallet: sender) if Glueby.configuration.use_utxo_provider?
181
+ funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
171
182
  funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
172
183
 
173
- tx = create_transfer_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, receiver_address: receiver_address, amount: amount)
184
+ tx = create_transfer_tx(
185
+ funding_tx: funding_tx,
186
+ color_id: color_id,
187
+ sender: sender,
188
+ receiver_address: receiver_address,
189
+ amount: amount,
190
+ only_finalized: only_finalized?
191
+ )
174
192
  sender.internal_wallet.broadcast(tx)
175
193
  [color_id, tx]
176
194
  end
@@ -187,10 +205,16 @@ module Glueby
187
205
  receivers.each do |r|
188
206
  raise Glueby::Contract::Errors::InvalidAmount unless r[:amount].positive?
189
207
  end
190
- funding_tx = create_funding_tx(wallet: sender) if Glueby.configuration.use_utxo_provider?
208
+ funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
191
209
  funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
192
210
 
193
- tx = create_multi_transfer_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, receivers: receivers)
211
+ tx = create_multi_transfer_tx(
212
+ funding_tx: funding_tx,
213
+ color_id: color_id,
214
+ sender: sender,
215
+ receivers: receivers,
216
+ only_finalized: only_finalized?
217
+ )
194
218
  sender.internal_wallet.broadcast(tx)
195
219
  [color_id, tx]
196
220
  end
@@ -205,7 +229,7 @@ module Glueby
205
229
  # @raise [InvalidAmount] if amount is not positive integer.
206
230
  def burn!(sender:, amount: 0)
207
231
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
208
- balance = sender.balances[color_id.to_hex]
232
+ balance = sender.balances(only_finalized?)[color_id.to_hex]
209
233
  raise Glueby::Contract::Errors::InsufficientTokens unless balance
210
234
  raise Glueby::Contract::Errors::InsufficientTokens if balance < amount
211
235
 
@@ -219,13 +243,14 @@ module Glueby
219
243
  # because change outputs is not necessary. Transactions needs one output at least.
220
244
  # At that time, set true to this option to get more value to be created change output to
221
245
  # the tx.
222
- need_value_for_change_output: burn_all_amount_flag
246
+ need_value_for_change_output: burn_all_amount_flag,
247
+ only_finalized: only_finalized?
223
248
  )
224
249
  end
225
250
 
226
251
  funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
227
252
 
228
- tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount)
253
+ tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount, only_finalized: only_finalized?)
229
254
  sender.internal_wallet.broadcast(tx)
230
255
  end
231
256
 
@@ -234,7 +259,7 @@ module Glueby
234
259
  # @return [Integer] amount of utxo value associated with this token.
235
260
  def amount(wallet:)
236
261
  # collect utxo associated with this address
237
- utxos = wallet.internal_wallet.list_unspent
262
+ utxos = wallet.internal_wallet.list_unspent(only_finalized?)
238
263
  _, results = collect_colored_outputs(utxos, color_id)
239
264
  results.sum { |result| result[:amount] }
240
265
  end
@@ -282,6 +307,10 @@ module Glueby
282
307
 
283
308
  private
284
309
 
310
+ def only_finalized?
311
+ @only_finalized ||= Token.only_finalized?
312
+ end
313
+
285
314
  # Verify that wallet is the issuer of the reissuable token
286
315
  # reutrn [Boolean]
287
316
  def validate_reissuer(wallet:)
@@ -8,7 +8,7 @@ module Glueby
8
8
  end
9
9
 
10
10
  # Create new public key, and new transaction that sends TPC to it
11
- def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new, need_value_for_change_output: false)
11
+ def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new, need_value_for_change_output: false, only_finalized: true)
12
12
  if Glueby.configuration.use_utxo_provider?
13
13
  utxo_provider = UtxoProvider.new
14
14
  script_pubkey = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
@@ -17,8 +17,8 @@ module Glueby
17
17
  else
18
18
  txb = Tapyrus::TxBuilder.new
19
19
  fee = fee_estimator.fee(dummy_tx(txb.build))
20
-
21
- sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee + funding_tx_amount(need_value_for_change_output: need_value_for_change_output))
20
+ amount = fee + funding_tx_amount(need_value_for_change_output: need_value_for_change_output)
21
+ sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(amount, nil, only_finalized)
22
22
  outputs.each do |utxo|
23
23
  txb.add_utxo({
24
24
  script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
@@ -37,7 +37,7 @@ module Glueby
37
37
  end
38
38
  end
39
39
 
40
- def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
40
+ def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, split: 1, fee_estimator: FixedFeeEstimator.new)
41
41
  tx = Tapyrus::Tx.new
42
42
 
43
43
  out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
@@ -47,7 +47,8 @@ module Glueby
47
47
  receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
48
48
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(receiver_script)
49
49
  receiver_colored_script = receiver_script.add_color(color_id)
50
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
50
+
51
+ add_split_output(tx, amount, split, receiver_colored_script)
51
52
 
52
53
  fee = fee_estimator.fee(dummy_tx(tx))
53
54
  fill_change_tpc(tx, issuer, output.value - fee)
@@ -60,15 +61,15 @@ module Glueby
60
61
  issuer.internal_wallet.sign_tx(tx, prev_txs)
61
62
  end
62
63
 
63
- def create_issue_tx_for_non_reissuable_token(funding_tx: nil, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
64
- create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, fee_estimator: fee_estimator)
64
+ def create_issue_tx_for_non_reissuable_token(funding_tx: nil, issuer:, amount:, split: 1, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
65
+ create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator, only_finalized: only_finalized)
65
66
  end
66
67
 
67
- def create_issue_tx_for_nft_token(funding_tx: nil, issuer:, fee_estimator: FixedFeeEstimator.new)
68
- create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_estimator: fee_estimator)
68
+ def create_issue_tx_for_nft_token(funding_tx: nil, issuer:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
69
+ create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_estimator: fee_estimator, only_finalized: only_finalized)
69
70
  end
70
71
 
71
- def create_issue_tx_from_out_point(funding_tx: nil, token_type:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
72
+ def create_issue_tx_from_out_point(funding_tx: nil, token_type:, issuer:, amount:, split: 1, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
72
73
  tx = Tapyrus::Tx.new
73
74
 
74
75
  fee = fee_estimator.fee(dummy_issue_tx_from_out_point)
@@ -77,7 +78,7 @@ module Glueby
77
78
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
78
79
  funding_tx.outputs.first.value
79
80
  else
80
- sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
81
+ sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
81
82
  fill_input(tx, outputs)
82
83
  sum
83
84
  end
@@ -93,7 +94,7 @@ module Glueby
93
94
 
94
95
  receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
95
96
  receiver_colored_script = receiver_script.add_color(color_id)
96
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
97
+ add_split_output(tx, amount, split, receiver_colored_script)
97
98
 
98
99
  fill_change_tpc(tx, issuer, sum - fee)
99
100
  prev_txs = if funding_tx
@@ -110,7 +111,7 @@ module Glueby
110
111
  issuer.internal_wallet.sign_tx(tx, prev_txs)
111
112
  end
112
113
 
113
- def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, fee_estimator: FixedFeeEstimator.new)
114
+ def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, split: 1, fee_estimator: FixedFeeEstimator.new)
114
115
  tx = Tapyrus::Tx.new
115
116
 
116
117
  out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
@@ -119,7 +120,7 @@ module Glueby
119
120
 
120
121
  receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
121
122
  receiver_colored_script = receiver_script.add_color(color_id)
122
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
123
+ add_split_output(tx, amount, split, receiver_colored_script)
123
124
 
124
125
  fee = fee_estimator.fee(dummy_tx(tx))
125
126
  fill_change_tpc(tx, issuer, output.value - fee)
@@ -132,16 +133,23 @@ module Glueby
132
133
  issuer.internal_wallet.sign_tx(tx, prev_txs)
133
134
  end
134
135
 
135
- def create_transfer_tx(funding_tx:nil, color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
136
+ def create_transfer_tx(funding_tx:nil, color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
136
137
  receivers = [{ address: receiver_address, amount: amount }]
137
- create_multi_transfer_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, receivers: receivers, fee_estimator: fee_estimator)
138
+ create_multi_transfer_tx(
139
+ funding_tx: funding_tx,
140
+ color_id: color_id,
141
+ sender: sender,
142
+ receivers: receivers,
143
+ fee_estimator: fee_estimator,
144
+ only_finalized: only_finalized
145
+ )
138
146
  end
139
147
 
140
- def create_multi_transfer_tx(funding_tx:nil, color_id:, sender:, receivers:, fee_estimator: FixedFeeEstimator.new)
148
+ def create_multi_transfer_tx(funding_tx:nil, color_id:, sender:, receivers:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
141
149
  tx = Tapyrus::Tx.new
142
150
 
143
151
  amount = receivers.reduce(0) { |sum, r| sum + r[:amount].to_i }
144
- utxos = sender.internal_wallet.list_unspent
152
+ utxos = sender.internal_wallet.list_unspent(only_finalized)
145
153
  sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
146
154
  fill_input(tx, outputs)
147
155
 
@@ -159,7 +167,7 @@ module Glueby
159
167
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
160
168
  funding_tx.outputs.first.value
161
169
  else
162
- sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
170
+ sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
163
171
  fill_input(tx, outputs)
164
172
  sum_tpc
165
173
  end
@@ -179,10 +187,10 @@ module Glueby
179
187
  sender.internal_wallet.sign_tx(tx, prev_txs)
180
188
  end
181
189
 
182
- def create_burn_tx(funding_tx:nil, color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new)
190
+ def create_burn_tx(funding_tx:nil, color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
183
191
  tx = Tapyrus::Tx.new
184
192
 
185
- utxos = sender.internal_wallet.list_unspent
193
+ utxos = sender.internal_wallet.list_unspent(only_finalized)
186
194
  sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
187
195
  fill_input(tx, outputs)
188
196
 
@@ -195,7 +203,7 @@ module Glueby
195
203
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
196
204
  funding_tx.outputs.first.value
197
205
  else
198
- sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + DUST_LIMIT)
206
+ sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + DUST_LIMIT, nil, only_finalized)
199
207
  fill_input(tx, outputs)
200
208
  sum_tpc
201
209
  end
@@ -215,6 +223,17 @@ module Glueby
215
223
  sender.internal_wallet.sign_tx(tx, prev_txs)
216
224
  end
217
225
 
226
+ def add_split_output(tx, amount, split, script_pubkey)
227
+ if amount < split
228
+ split = amount
229
+ value = 1
230
+ else
231
+ value = (amount/split).to_i
232
+ end
233
+ (split - 1).times { tx.outputs << Tapyrus::TxOut.new(value: value, script_pubkey: script_pubkey) }
234
+ tx.outputs << Tapyrus::TxOut.new(value: amount - value * (split - 1), script_pubkey: script_pubkey)
235
+ end
236
+
218
237
  def fill_input(tx, outputs)
219
238
  outputs.each do |output|
220
239
  out_point = Tapyrus::OutPoint.new(output[:txid].rhex, output[:vout])
@@ -3,7 +3,7 @@ module Glueby
3
3
 
4
4
  autoload :Tasks, 'glueby/fee_provider/tasks'
5
5
 
6
- class NoUtxosInUtxoPool < StandardError; end
6
+ class NoUtxosInUtxoPool < Error; end
7
7
 
8
8
  WALLET_ID = 'FEE_PROVIDER_WALLET'
9
9
  DEFAULT_FIXED_FEE = 1000
@@ -42,7 +42,7 @@ module Glueby
42
42
  # Provide an input for fee to the tx.
43
43
  # @param [Tapyrus::Tx] tx - The tx that is provided fee as a input. It should be signed with ANYONECANPAY flag.
44
44
  # @return [Tapyrus::Tx]
45
- # @raise [ArgumentError] If the signatures that the tx inputs has don't have ANYONECANPAY flag.
45
+ # @raise [Glueby::ArgumentError] If the signatures that the tx inputs has don't have ANYONECANPAY flag.
46
46
  # @raise [Glueby::FeeProvider::NoUtxosInUtxoPool] If there are no UTXOs for paying fee in FeeProvider's UTXO pool
47
47
  def provide(tx)
48
48
  tx.inputs.each do |txin|
@@ -2,12 +2,12 @@ module Glueby
2
2
  module Internal
3
3
  class Wallet
4
4
  module Errors
5
- class ShouldInitializeWalletAdapter < StandardError; end
6
- class WalletUnloaded < StandardError; end
7
- class WalletAlreadyLoaded < StandardError; end
8
- class WalletAlreadyCreated < StandardError; end
9
- class WalletNotFound < StandardError; end
10
- class InvalidSighashType < StandardError; end
5
+ class ShouldInitializeWalletAdapter < Error; end
6
+ class WalletUnloaded < Error; end
7
+ class WalletAlreadyLoaded < Error; end
8
+ class WalletAlreadyCreated < Error; end
9
+ class WalletNotFound < Error; end
10
+ class InvalidSighashType < Error; end
11
11
  end
12
12
  end
13
13
  end
@@ -30,14 +30,14 @@ module Glueby
30
30
 
31
31
  utxos.each { |utxo| txb.add_utxo(utxo) }
32
32
 
33
- shortage = [utxo_provider.utxo_pool_size - current_utxo_pool_size, 0].max
33
+ shortage = [utxo_provider.utxo_pool_size - utxo_provider.current_utxo_pool_size, 0].max
34
34
  return if shortage == 0
35
35
 
36
36
  added_outputs = 0
37
37
  shortage.times do
38
38
  fee = utxo_provider.fee_estimator.fee(dummy_tx(txb.build))
39
39
  break if (sum - fee) < utxo_provider.default_value
40
- txb.pay(address, utxo_provider.default_value)
40
+ txb.pay(utxo_provider.address, utxo_provider.default_value)
41
41
  sum -= utxo_provider.default_value
42
42
  added_outputs += 1
43
43
  end
@@ -45,11 +45,11 @@ module Glueby
45
45
  return if added_outputs == 0
46
46
 
47
47
  fee = utxo_provider.fee_estimator.fee(dummy_tx(txb.build))
48
- tx = txb.change_address(address)
48
+ tx = txb.change_address(utxo_provider.address)
49
49
  .fee(fee)
50
50
  .build
51
- tx = wallet.sign_tx(tx)
52
- wallet.broadcast(tx)
51
+ tx = utxo_provider.wallet.sign_tx(tx)
52
+ utxo_provider.wallet.broadcast(tx)
53
53
  ensure
54
54
  status
55
55
  end
@@ -58,13 +58,13 @@ module Glueby
58
58
  def status
59
59
  status = :ready
60
60
 
61
- if current_utxo_pool_size < utxo_provider.utxo_pool_size
62
- if tpc_amount < value_to_fill_utxo_pool
61
+ if utxo_provider.current_utxo_pool_size < utxo_provider.utxo_pool_size
62
+ if utxo_provider.tpc_amount < utxo_provider.value_to_fill_utxo_pool
63
63
  status = :insufficient_amount
64
64
  message = <<~MESSAGE
65
65
  1. Please replenishment TPC which is for paying tpc to UtxoProvider.
66
- UtxoProvider needs #{value_to_fill_utxo_pool} tapyrus in UTXO pool.
67
- UtxoProvider wallet's address is '#{address}'
66
+ UtxoProvider needs #{utxo_provider.value_to_fill_utxo_pool} tapyrus in UTXO pool.
67
+ UtxoProvider wallet's address is '#{utxo_provider.address}'
68
68
  2. Then create UTXOs for paying in UTXO pool with 'rake glueby:utxo_provider:manage_utxo_pool'
69
69
  MESSAGE
70
70
  else
@@ -72,12 +72,12 @@ module Glueby
72
72
  end
73
73
  end
74
74
 
75
- status = :not_ready if current_utxo_pool_size == 0
75
+ status = :not_ready if utxo_provider.current_utxo_pool_size == 0
76
76
 
77
77
  puts <<~EOS
78
78
  Status: #{STATUS[status]}
79
- TPC amount: #{delimit(tpc_amount)}
80
- UTXO pool size: #{delimit(current_utxo_pool_size)}
79
+ TPC amount: #{delimit(utxo_provider.tpc_amount)}
80
+ UTXO pool size: #{delimit(utxo_provider.current_utxo_pool_size)}
81
81
  #{"\n" if message}#{message}
82
82
  Configuration:
83
83
  default_value = #{delimit(utxo_provider.default_value)}
@@ -87,17 +87,13 @@ module Glueby
87
87
 
88
88
  # Show the address of Utxo Provider
89
89
  def print_address
90
- puts address
90
+ puts utxo_provider.address
91
91
  end
92
92
 
93
93
  private
94
94
 
95
- def tpc_amount
96
- wallet.balance(false)
97
- end
98
-
99
95
  def collect_outputs
100
- wallet.list_unspent.inject([0, []]) do |sum, output|
96
+ utxo_provider.wallet.list_unspent.inject([0, []]) do |sum, output|
101
97
  next sum if output[:color_id] || output[:amount] == utxo_provider.default_value
102
98
 
103
99
  new_sum = sum[0] + output[:amount]
@@ -105,34 +101,16 @@ module Glueby
105
101
  txid: output[:txid],
106
102
  script_pubkey: output[:script_pubkey],
107
103
  value: output[:amount],
108
- index: output[:vout] ,
104
+ index: output[:vout],
109
105
  finalized: output[:finalized]
110
106
  }
111
107
  [new_sum, new_outputs]
112
108
  end
113
109
  end
114
110
 
115
- def current_utxo_pool_size
116
- wallet
117
- .list_unspent(false)
118
- .count { |o| !o[:color_id] && o[:amount] == utxo_provider.default_value }
119
- end
120
-
121
- def value_to_fill_utxo_pool
122
- utxo_provider.default_value * utxo_provider.utxo_pool_size
123
- end
124
-
125
- def wallet
126
- utxo_provider.wallet
127
- end
128
-
129
111
  def delimit(num)
130
112
  num.to_s.reverse.scan(/.{1,3}/).join('_').reverse
131
113
  end
132
-
133
- def address
134
- @address ||= wallet.get_addresses.first || wallet.receive_address
135
- end
136
114
  end
137
115
  end
138
116
  end
@@ -25,11 +25,9 @@ module Glueby
25
25
  @wallet = load_wallet
26
26
  validate_config!
27
27
  @fee_estimator = (UtxoProvider.config && UtxoProvider.config[:fee_estimator]) || Glueby::Contract::FixedFeeEstimator.new
28
- @default_value = (UtxoProvider.config && UtxoProvider.config[:default_value]) || DEFAULT_VALUE
29
- @utxo_pool_size = (UtxoProvider.config && UtxoProvider.config[:utxo_pool_size]) || DEFAULT_UTXO_POOL_SIZE
30
28
  end
31
29
 
32
- attr_reader :wallet, :fee_estimator, :default_value, :utxo_pool_size
30
+ attr_reader :wallet, :fee_estimator, :address
33
31
 
34
32
  # Provide a UTXO
35
33
  # @param [Tapyrus::Script] script_pubkey The script to be provided
@@ -62,6 +60,42 @@ module Glueby
62
60
  [signed_tx, 0]
63
61
  end
64
62
 
63
+ def default_value
64
+ @default_value ||=
65
+ (
66
+ Glueby::AR::SystemInformation.utxo_provider_default_value ||
67
+ (UtxoProvider.config && UtxoProvider.config[:default_value]) ||
68
+ DEFAULT_VALUE
69
+ )
70
+ end
71
+
72
+ def utxo_pool_size
73
+ @utxo_pool_size ||=
74
+ (
75
+ Glueby::AR::SystemInformation.utxo_provider_pool_size ||
76
+ (UtxoProvider.config && UtxoProvider.config[:utxo_pool_size]) ||
77
+ DEFAULT_UTXO_POOL_SIZE
78
+ )
79
+ end
80
+
81
+ def tpc_amount
82
+ wallet.balance(false)
83
+ end
84
+
85
+ def current_utxo_pool_size
86
+ wallet
87
+ .list_unspent(false)
88
+ .count { |o| !o[:color_id] && o[:amount] == default_value }
89
+ end
90
+
91
+ def address
92
+ @address ||= wallet.get_addresses.first || wallet.receive_address
93
+ end
94
+
95
+ def value_to_fill_utxo_pool
96
+ default_value * utxo_pool_size
97
+ end
98
+
65
99
  private
66
100
 
67
101
  # Create wallet for provider
@@ -74,7 +108,7 @@ module Glueby
74
108
  end
75
109
 
76
110
  def collect_uncolored_outputs(wallet, amount)
77
- utxos = wallet.list_unspent.select { |o| !o[:color_id] && o[:amount] == @default_value }
111
+ utxos = wallet.list_unspent.select { |o| !o[:color_id] && o[:amount] == default_value }
78
112
  utxos.shuffle!
79
113
 
80
114
  utxos.inject([0, []]) do |sum, output|
@@ -88,11 +122,8 @@ module Glueby
88
122
  end
89
123
 
90
124
  def validate_config!
91
- if UtxoProvider.config
92
- utxo_pool_size = UtxoProvider.config[:utxo_pool_size]
93
- if utxo_pool_size && (!utxo_pool_size.is_a?(Integer) || utxo_pool_size > MAX_UTXO_POOL_SIZE)
94
- raise Glueby::Configuration::Errors::InvalidConfiguration, "utxo_pool_size(#{utxo_pool_size}) should not be greater than #{MAX_UTXO_POOL_SIZE}"
95
- end
125
+ if !utxo_pool_size.is_a?(Integer) || utxo_pool_size > MAX_UTXO_POOL_SIZE
126
+ raise Glueby::Configuration::Errors::InvalidConfiguration, "utxo_pool_size(#{utxo_pool_size}) should not be greater than #{MAX_UTXO_POOL_SIZE}"
96
127
  end
97
128
  end
98
129
  end
@@ -1,3 +1,3 @@
1
1
  module Glueby
2
- VERSION = "0.7.0"
2
+ VERSION = "0.9.0"
3
3
  end
data/lib/glueby.rb CHANGED
@@ -48,4 +48,9 @@ module Glueby
48
48
  def self.configure
49
49
  yield configuration if block_given?
50
50
  end
51
+
52
+ # Base error classes. These error classes must be used as a super class in all error classes that is defined and
53
+ # raised in glueby library.
54
+ class Error < StandardError; end
55
+ class ArgumentError < Error; end
51
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glueby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-25 00:00:00.000000000 Z
11
+ date: 2022-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tapyrus
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.9
19
+ version: 0.2.13
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.2.9
26
+ version: 0.2.13
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement