glueby 0.6.2 → 0.8.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: bd80045038551e7ffc41a613fb67401f3675c79098222e335359209070ff280a
4
- data.tar.gz: f966a44bb7de60429e91d39681f63c6b827dbcb8806700cbfe6b5e961f013b2d
3
+ metadata.gz: e2f53ca4e5784495dc9ddc7669bc021985614b3719205247c42005ec5d12a5c4
4
+ data.tar.gz: d5bb0540f3db7cf5ccdb96d4487ff627c4090f69e8719f746badae31689f864d
5
5
  SHA512:
6
- metadata.gz: 4e443a845c4d65eeba06c83d40617497b984f8a0c158e54debb1ac82d762af53ebba8073e8fc18855f93816370dc8969657bec8d8e95d5990df3fe50c9c7471a
7
- data.tar.gz: 2c5f2670b48f6a1149052b239d89c626c323e21f222bd2deb201074b96d54ac935080624cb92a2399414aed03de44258a41da5b0fc4ecc5221d854ef201c06de
6
+ metadata.gz: 037ac4f566439facb0971a34c086bb128afd4b00566b7ab0f9013d4b4c357082c88a620eb8262221a245586429387228811e940077868b254b1ec82d155de8ab
7
+ data.tar.gz: 9a031d6505750f550abe9948ff977cd01f4fbcad8ab9b733f0b322101cc77d9856c6d2e3f633e61c9e49b4657dc7f286db1abdd4df0511cedf96dba9c27a2d3d
@@ -3,7 +3,25 @@ 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
+ # Return default value of the utxo provider
16
+ # @return [Integer] default value of utxo provider
17
+ def self.utxo_provider_default_value
18
+ find_by(info_key: "utxo_provider_default_value")&.int_value
19
+ end
20
+
21
+ # Return pool size of the utxo provider
22
+ # @return [Integer] pool size of utxo provider
23
+ def self.utxo_provider_pool_size
24
+ find_by(info_key: "utxo_provider_pool_size")&.int_value
7
25
  end
8
26
 
9
27
  def int_value
@@ -0,0 +1,4 @@
1
+ module Glueby
2
+ # The minimum limit of transaction output value to avoid to be recognized as a dust output.
3
+ DUST_LIMIT = 600
4
+ end
@@ -2,7 +2,10 @@ module Glueby
2
2
  module Contract
3
3
  module AR
4
4
  class Timestamp < ::ActiveRecord::Base
5
+ include Glueby::GluebyLogger
5
6
  include Glueby::Contract::Timestamp::Util
7
+ include Glueby::Contract::TxBuilder
8
+
6
9
  enum status: { init: 0, unconfirmed: 1, confirmed: 2 }
7
10
  enum timestamp_type: { simple: 0, trackable: 1 }
8
11
 
@@ -21,6 +24,33 @@ module Glueby
21
24
  def latest
22
25
  trackable?
23
26
  end
27
+
28
+ # Broadcast and save timestamp
29
+ # @param [Glueby::Contract::FixedFeeEstimator] fee_estimator
30
+ # @return true if tapyrus transactions were broadcasted and the timestamp was updated successfully, otherwise false.
31
+ def save_with_broadcast(fee_estimator: Glueby::Contract::FixedFeeEstimator.new)
32
+ utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
33
+ wallet = Glueby::Wallet.load(wallet_id)
34
+ funding_tx, tx, p2c_address, payment_base = create_txs(wallet, prefix, content_hash, fee_estimator, utxo_provider, type: timestamp_type.to_sym)
35
+ if funding_tx
36
+ ::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
37
+ wallet.internal_wallet.broadcast(funding_tx)
38
+ logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
39
+ end
40
+ end
41
+ ::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
42
+ wallet.internal_wallet.broadcast(tx) do |tx|
43
+ assign_attributes(txid: tx.txid, status: :unconfirmed, p2c_address: p2c_address, payment_base: payment_base)
44
+ save!
45
+ end
46
+ logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
47
+ end
48
+ true
49
+ rescue => e
50
+ logger.error("failed to broadcast (id=#{id}, reason=#{e.message})")
51
+ errors.add(:base, "failed to broadcast (id=#{id}, reason=#{e.message})")
52
+ false
53
+ end
24
54
  end
25
55
  end
26
56
  end
@@ -4,6 +4,7 @@ module Glueby
4
4
  class InsufficientFunds < StandardError; end
5
5
  class InsufficientTokens < StandardError; end
6
6
  class InvalidAmount < StandardError; end
7
+ class InvalidSplit < StandardError; end
7
8
  class InvalidTokenType < StandardError; end
8
9
  class InvalidTimestampType < StandardError; end
9
10
  class TxAlreadyBroadcasted < StandardError; end
@@ -22,6 +22,8 @@ module Glueby
22
22
  class FixedFeeEstimator
23
23
  include FeeEstimator
24
24
 
25
+ attr_reader :fixed_fee
26
+
25
27
  class << self
26
28
  attr_accessor :default_fixed_fee
27
29
  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,11 +80,14 @@ 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
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
84
- funding_tx = create_funding_tx(wallet: issuer, utxo_provider: utxo_provider)
89
+ def issue_reissuable_token(issuer:, amount:, split: 1)
90
+ funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?)
85
91
  script_pubkey = funding_tx.outputs.first.script_pubkey
86
92
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
87
93
 
@@ -90,18 +96,17 @@ module Glueby
90
96
  Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
91
97
 
92
98
  funding_tx = issuer.internal_wallet.broadcast(funding_tx)
93
- tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
99
+ tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
94
100
  tx = issuer.internal_wallet.broadcast(tx)
95
101
  [[funding_tx, tx], color_id]
96
102
  end
97
103
  end
98
104
 
99
- def issue_non_reissuable_token(issuer:, amount:)
100
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
101
- funding_tx = create_funding_tx(wallet: issuer, utxo_provider: utxo_provider) if utxo_provider
105
+ def issue_non_reissuable_token(issuer:, amount:, split: 1)
106
+ funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
102
107
  funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
103
108
 
104
- tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
109
+ tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
105
110
  tx = issuer.internal_wallet.broadcast(tx)
106
111
 
107
112
  out_point = tx.inputs.first.out_point
@@ -114,8 +119,7 @@ module Glueby
114
119
  end
115
120
 
116
121
  def issue_nft_token(issuer:)
117
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
118
- funding_tx = create_funding_tx(wallet: issuer, utxo_provider: utxo_provider) if utxo_provider
122
+ funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
119
123
  funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
120
124
 
121
125
  tx = create_issue_tx_for_nft_token(funding_tx: funding_tx, issuer: issuer)
@@ -137,20 +141,20 @@ module Glueby
137
141
  # A wallet can issue the token only when it is REISSUABLE token.
138
142
  # @param issuer [Glueby::Wallet]
139
143
  # @param amount [Integer]
144
+ # @param split [Integer]
140
145
  # @return [Array<String, tx>] Tuple of color_id and tx object
141
146
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
142
147
  # @raise [InvalidAmount] if amount is not positive integer.
143
148
  # @raise [InvalidTokenType] if token is not reissuable.
144
149
  # @raise [UnknownScriptPubkey] when token is reissuable but it doesn't know script pubkey to issue token.
145
- def reissue!(issuer:, amount:)
150
+ def reissue!(issuer:, amount:, split: 1)
146
151
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
147
152
  raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE
148
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
149
153
 
150
154
  if validate_reissuer(wallet: issuer)
151
- funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey, utxo_provider: utxo_provider)
155
+ funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey, only_finalized: only_finalized?)
152
156
  funding_tx = issuer.internal_wallet.broadcast(funding_tx)
153
- tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id)
157
+ tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id, split: split)
154
158
  tx = issuer.internal_wallet.broadcast(tx)
155
159
 
156
160
  [color_id, tx]
@@ -171,11 +175,43 @@ module Glueby
171
175
  def transfer!(sender:, receiver_address:, amount: 1)
172
176
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
173
177
 
174
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
175
- funding_tx = create_funding_tx(wallet: sender, utxo_provider: utxo_provider) if utxo_provider
178
+ funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
179
+ funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
180
+
181
+ tx = create_transfer_tx(
182
+ funding_tx: funding_tx,
183
+ color_id: color_id,
184
+ sender: sender,
185
+ receiver_address: receiver_address,
186
+ amount: amount,
187
+ only_finalized: only_finalized?
188
+ )
189
+ sender.internal_wallet.broadcast(tx)
190
+ [color_id, tx]
191
+ end
192
+
193
+ # Send the tokens to multiple wallets
194
+ #
195
+ # @param sender [Glueby::Wallet] wallet to send this token
196
+ # @param receivers [Array<Hash>] array of hash, which keys are :address and :amount
197
+ # @return [Array<String, tx>] Tuple of color_id and tx object
198
+ # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
199
+ # @raise [InsufficientTokens] if wallet does not have enough token to send.
200
+ # @raise [InvalidAmount] if amount is not positive integer.
201
+ def multi_transfer!(sender:, receivers:)
202
+ receivers.each do |r|
203
+ raise Glueby::Contract::Errors::InvalidAmount unless r[:amount].positive?
204
+ end
205
+ funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
176
206
  funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
177
207
 
178
- tx = create_transfer_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, receiver_address: receiver_address, amount: amount)
208
+ tx = create_multi_transfer_tx(
209
+ funding_tx: funding_tx,
210
+ color_id: color_id,
211
+ sender: sender,
212
+ receivers: receivers,
213
+ only_finalized: only_finalized?
214
+ )
179
215
  sender.internal_wallet.broadcast(tx)
180
216
  [color_id, tx]
181
217
  end
@@ -190,12 +226,28 @@ module Glueby
190
226
  # @raise [InvalidAmount] if amount is not positive integer.
191
227
  def burn!(sender:, amount: 0)
192
228
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
229
+ balance = sender.balances(only_finalized?)[color_id.to_hex]
230
+ raise Glueby::Contract::Errors::InsufficientTokens unless balance
231
+ raise Glueby::Contract::Errors::InsufficientTokens if balance < amount
232
+
233
+ burn_all_amount_flag = true if balance - amount == 0
193
234
 
194
235
  utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
195
- funding_tx = create_funding_tx(wallet: sender, utxo_provider: utxo_provider) if utxo_provider
236
+ if utxo_provider
237
+ funding_tx = create_funding_tx(
238
+ wallet: sender,
239
+ # When it burns all the amount of the color id, burn tx is not going to be have any output
240
+ # because change outputs is not necessary. Transactions needs one output at least.
241
+ # At that time, set true to this option to get more value to be created change output to
242
+ # the tx.
243
+ need_value_for_change_output: burn_all_amount_flag,
244
+ only_finalized: only_finalized?
245
+ )
246
+ end
247
+
196
248
  funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
197
249
 
198
- tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount)
250
+ tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount, only_finalized: only_finalized?)
199
251
  sender.internal_wallet.broadcast(tx)
200
252
  end
201
253
 
@@ -204,7 +256,7 @@ module Glueby
204
256
  # @return [Integer] amount of utxo value associated with this token.
205
257
  def amount(wallet:)
206
258
  # collect utxo associated with this address
207
- utxos = wallet.internal_wallet.list_unspent
259
+ utxos = wallet.internal_wallet.list_unspent(only_finalized?)
208
260
  _, results = collect_colored_outputs(utxos, color_id)
209
261
  results.sum { |result| result[:amount] }
210
262
  end
@@ -252,6 +304,10 @@ module Glueby
252
304
 
253
305
  private
254
306
 
307
+ def only_finalized?
308
+ @only_finalized ||= Token.only_finalized?
309
+ end
310
+
255
311
  # Verify that wallet is the issuer of the reissuable token
256
312
  # reutrn [Boolean]
257
313
  def validate_reissuer(wallet:)
@@ -3,24 +3,22 @@
3
3
  module Glueby
4
4
  module Contract
5
5
  module TxBuilder
6
- # The amount of output in funding tx for Reissuable token.
7
- FUNDING_TX_AMOUNT = 10_000
8
-
9
6
  def receive_address(wallet:)
10
7
  wallet.receive_address
11
8
  end
12
9
 
13
10
  # Create new public key, and new transaction that sends TPC to it
14
- def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new, utxo_provider: nil)
15
- if utxo_provider
11
+ def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new, need_value_for_change_output: false, only_finalized: true)
12
+ if Glueby.configuration.use_utxo_provider?
13
+ utxo_provider = UtxoProvider.new
16
14
  script_pubkey = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
17
- funding_tx, _index = utxo_provider.get_utxo(script_pubkey, FUNDING_TX_AMOUNT)
15
+ funding_tx, _index = utxo_provider.get_utxo(script_pubkey, funding_tx_amount(need_value_for_change_output: need_value_for_change_output))
18
16
  utxo_provider.wallet.sign_tx(funding_tx)
19
17
  else
20
18
  txb = Tapyrus::TxBuilder.new
21
19
  fee = fee_estimator.fee(dummy_tx(txb.build))
22
-
23
- sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee + FUNDING_TX_AMOUNT)
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)
24
22
  outputs.each do |utxo|
25
23
  txb.add_utxo({
26
24
  script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
@@ -31,7 +29,7 @@ module Glueby
31
29
  end
32
30
 
33
31
  receiver_address = script ? script.addresses.first : wallet.internal_wallet.receive_address
34
- tx = txb.pay(receiver_address, FUNDING_TX_AMOUNT)
32
+ tx = txb.pay(receiver_address, funding_tx_amount)
35
33
  .change_address(wallet.internal_wallet.change_address)
36
34
  .fee(fee)
37
35
  .build
@@ -39,7 +37,7 @@ module Glueby
39
37
  end
40
38
  end
41
39
 
42
- 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)
43
41
  tx = Tapyrus::Tx.new
44
42
 
45
43
  out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
@@ -49,7 +47,8 @@ module Glueby
49
47
  receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
50
48
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(receiver_script)
51
49
  receiver_colored_script = receiver_script.add_color(color_id)
52
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
50
+
51
+ add_split_output(tx, amount, split, receiver_colored_script)
53
52
 
54
53
  fee = fee_estimator.fee(dummy_tx(tx))
55
54
  fill_change_tpc(tx, issuer, output.value - fee)
@@ -62,15 +61,15 @@ module Glueby
62
61
  issuer.internal_wallet.sign_tx(tx, prev_txs)
63
62
  end
64
63
 
65
- def create_issue_tx_for_non_reissuable_token(funding_tx: nil, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
66
- 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)
67
66
  end
68
67
 
69
- def create_issue_tx_for_nft_token(funding_tx: nil, issuer:, fee_estimator: FixedFeeEstimator.new)
70
- 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)
71
70
  end
72
71
 
73
- 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)
74
73
  tx = Tapyrus::Tx.new
75
74
 
76
75
  fee = fee_estimator.fee(dummy_issue_tx_from_out_point)
@@ -79,7 +78,7 @@ module Glueby
79
78
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
80
79
  funding_tx.outputs.first.value
81
80
  else
82
- sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
81
+ sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
83
82
  fill_input(tx, outputs)
84
83
  sum
85
84
  end
@@ -95,7 +94,7 @@ module Glueby
95
94
 
96
95
  receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
97
96
  receiver_colored_script = receiver_script.add_color(color_id)
98
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
97
+ add_split_output(tx, amount, split, receiver_colored_script)
99
98
 
100
99
  fill_change_tpc(tx, issuer, sum - fee)
101
100
  prev_txs = if funding_tx
@@ -112,7 +111,7 @@ module Glueby
112
111
  issuer.internal_wallet.sign_tx(tx, prev_txs)
113
112
  end
114
113
 
115
- 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)
116
115
  tx = Tapyrus::Tx.new
117
116
 
118
117
  out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
@@ -121,7 +120,7 @@ module Glueby
121
120
 
122
121
  receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
123
122
  receiver_colored_script = receiver_script.add_color(color_id)
124
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
123
+ add_split_output(tx, amount, split, receiver_colored_script)
125
124
 
126
125
  fee = fee_estimator.fee(dummy_tx(tx))
127
126
  fill_change_tpc(tx, issuer, output.value - fee)
@@ -134,16 +133,31 @@ module Glueby
134
133
  issuer.internal_wallet.sign_tx(tx, prev_txs)
135
134
  end
136
135
 
137
- 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)
137
+ receivers = [{ address: receiver_address, amount: amount }]
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
+ )
146
+ end
147
+
148
+ def create_multi_transfer_tx(funding_tx:nil, color_id:, sender:, receivers:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
138
149
  tx = Tapyrus::Tx.new
139
150
 
140
- utxos = sender.internal_wallet.list_unspent
151
+ amount = receivers.reduce(0) { |sum, r| sum + r[:amount].to_i }
152
+ utxos = sender.internal_wallet.list_unspent(only_finalized)
141
153
  sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
142
154
  fill_input(tx, outputs)
143
155
 
144
- receiver_script = Tapyrus::Script.parse_from_addr(receiver_address)
145
- receiver_colored_script = receiver_script.add_color(color_id)
146
- tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
156
+ receivers.each do |r|
157
+ receiver_script = Tapyrus::Script.parse_from_addr(r[:address])
158
+ receiver_colored_script = receiver_script.add_color(color_id)
159
+ tx.outputs << Tapyrus::TxOut.new(value: r[:amount].to_i, script_pubkey: receiver_colored_script)
160
+ end
147
161
 
148
162
  fill_change_token(tx, sender, sum_token - amount, color_id)
149
163
 
@@ -153,7 +167,7 @@ module Glueby
153
167
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
154
168
  funding_tx.outputs.first.value
155
169
  else
156
- sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
170
+ sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
157
171
  fill_input(tx, outputs)
158
172
  sum_tpc
159
173
  end
@@ -173,10 +187,10 @@ module Glueby
173
187
  sender.internal_wallet.sign_tx(tx, prev_txs)
174
188
  end
175
189
 
176
- 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)
177
191
  tx = Tapyrus::Tx.new
178
192
 
179
- utxos = sender.internal_wallet.list_unspent
193
+ utxos = sender.internal_wallet.list_unspent(only_finalized)
180
194
  sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
181
195
  fill_input(tx, outputs)
182
196
 
@@ -189,8 +203,7 @@ module Glueby
189
203
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
190
204
  funding_tx.outputs.first.value
191
205
  else
192
- dust = 600 # in case that the wallet has output which has just fee amount.
193
- sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + dust)
206
+ sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + DUST_LIMIT, nil, only_finalized)
194
207
  fill_input(tx, outputs)
195
208
  sum_tpc
196
209
  end
@@ -210,6 +223,17 @@ module Glueby
210
223
  sender.internal_wallet.sign_tx(tx, prev_txs)
211
224
  end
212
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
+
213
237
  def fill_input(tx, outputs)
214
238
  outputs.each do |output|
215
239
  out_point = Tapyrus::OutPoint.new(output[:txid].rhex, output[:vout])
@@ -220,7 +244,12 @@ module Glueby
220
244
  def fill_change_tpc(tx, wallet, change)
221
245
  return unless change.positive?
222
246
 
223
- change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
247
+ if Glueby.configuration.use_utxo_provider?
248
+ change_script = Tapyrus::Script.parse_from_addr(UtxoProvider.new.wallet.change_address)
249
+ else
250
+ change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
251
+ end
252
+
224
253
  tx.outputs << Tapyrus::TxOut.new(value: change, script_pubkey: change_script)
225
254
  end
226
255
 
@@ -278,6 +307,21 @@ module Glueby
278
307
  tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: receiver_colored_script)
279
308
  dummy_tx(tx)
280
309
  end
310
+
311
+ private
312
+
313
+ # The amount of output in funding tx
314
+ # It returns same amount with FixedFeeEstimator's fixed fee. Because it is enough for paying fee for consumer
315
+ # transactions of the funding transactions.
316
+ #
317
+ # @option [Boolean] need_value_for_change_output If it is true, adds more value than the fee for producing change output.
318
+ def funding_tx_amount(need_value_for_change_output: false)
319
+ if need_value_for_change_output
320
+ FixedFeeEstimator.new.fixed_fee + DUST_LIMIT
321
+ else
322
+ FixedFeeEstimator.new.fixed_fee
323
+ end
324
+ end
281
325
  end
282
326
  end
283
327
  end
@@ -29,7 +29,6 @@ module Glueby
29
29
  return if utxos.empty?
30
30
 
31
31
  utxos.each { |utxo| txb.add_utxo(utxo) }
32
- address = wallet.receive_address
33
32
 
34
33
  shortage = [utxo_provider.utxo_pool_size - current_utxo_pool_size, 0].max
35
34
  return if shortage == 0
@@ -65,7 +64,7 @@ module Glueby
65
64
  message = <<~MESSAGE
66
65
  1. Please replenishment TPC which is for paying tpc to UtxoProvider.
67
66
  UtxoProvider needs #{value_to_fill_utxo_pool} tapyrus in UTXO pool.
68
- UtxoProvider wallet's address is '#{wallet.receive_address}'
67
+ UtxoProvider wallet's address is '#{address}'
69
68
  2. Then create UTXOs for paying in UTXO pool with 'rake glueby:utxo_provider:manage_utxo_pool'
70
69
  MESSAGE
71
70
  else
@@ -88,7 +87,7 @@ module Glueby
88
87
 
89
88
  # Show the address of Utxo Provider
90
89
  def print_address
91
- puts wallet.receive_address
90
+ puts address
92
91
  end
93
92
 
94
93
  private
@@ -130,6 +129,10 @@ module Glueby
130
129
  def delimit(num)
131
130
  num.to_s.reverse.scan(/.{1,3}/).join('_').reverse
132
131
  end
132
+
133
+ def address
134
+ @address ||= wallet.get_addresses.first || wallet.receive_address
135
+ end
133
136
  end
134
137
  end
135
138
  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
33
31
 
34
32
  # Provide a UTXO
35
33
  # @param [Tapyrus::Script] script_pubkey The script to be provided
@@ -62,6 +60,24 @@ 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
+
65
81
  private
66
82
 
67
83
  # Create wallet for provider
@@ -74,7 +90,7 @@ module Glueby
74
90
  end
75
91
 
76
92
  def collect_uncolored_outputs(wallet, amount)
77
- utxos = wallet.list_unspent.select { |o| !o[:color_id] && o[:amount] == @default_value }
93
+ utxos = wallet.list_unspent.select { |o| !o[:color_id] && o[:amount] == default_value }
78
94
  utxos.shuffle!
79
95
 
80
96
  utxos.inject([0, []]) do |sum, output|
@@ -88,11 +104,8 @@ module Glueby
88
104
  end
89
105
 
90
106
  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
107
+ if !utxo_pool_size.is_a?(Integer) || utxo_pool_size > MAX_UTXO_POOL_SIZE
108
+ raise Glueby::Configuration::Errors::InvalidConfiguration, "utxo_pool_size(#{utxo_pool_size}) should not be greater than #{MAX_UTXO_POOL_SIZE}"
96
109
  end
97
110
  end
98
111
  end
@@ -1,3 +1,3 @@
1
1
  module Glueby
2
- VERSION = "0.6.2"
2
+ VERSION = "0.8.0"
3
3
  end
data/lib/glueby.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "glueby/version"
2
+ require "glueby/constants"
2
3
  require 'tapyrus'
3
4
 
4
5
  module Glueby
@@ -16,6 +17,16 @@ module Glueby
16
17
  require 'glueby/railtie'
17
18
  end
18
19
 
20
+ module GluebyLogger
21
+ def logger
22
+ if defined?(Rails)
23
+ Rails.logger
24
+ else
25
+ Logger.new(STDOUT)
26
+ end
27
+ end
28
+ end
29
+
19
30
  # Add prefix to activerecord table names
20
31
  def self.table_name_prefix
21
32
  'glueby_'
@@ -8,27 +8,8 @@ module Glueby
8
8
 
9
9
  def create
10
10
  timestamps = Glueby::Contract::AR::Timestamp.where(status: :init)
11
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
12
- timestamps.each do |t|
13
- begin
14
- wallet = Glueby::Wallet.load(t.wallet_id)
15
- funding_tx, tx, p2c_address, payment_base = create_txs(wallet, t.prefix, t.content_hash, Glueby::Contract::FixedFeeEstimator.new, utxo_provider, type: t.timestamp_type.to_sym)
16
- if funding_tx
17
- ::ActiveRecord::Base.transaction do
18
- wallet.internal_wallet.broadcast(funding_tx)
19
- puts "funding tx was broadcasted(id=#{t.id}, funding_tx.txid=#{funding_tx.txid})"
20
- end
21
- end
22
- ::ActiveRecord::Base.transaction do
23
- wallet.internal_wallet.broadcast(tx) do |tx|
24
- t.update(txid: tx.txid, status: :unconfirmed, p2c_address: p2c_address, payment_base: payment_base)
25
- end
26
- puts "timestamp tx was broadcasted (id=#{t.id}, txid=#{tx.txid})"
27
- end
28
- rescue => e
29
- puts "failed to broadcast (id=#{t.id}, reason=#{e.message})"
30
- end
31
- end
11
+ fee_estimator = Glueby::Contract::FixedFeeEstimator.new
12
+ timestamps.each { |t| t.save_with_broadcast(fee_estimator: fee_estimator) }
32
13
  end
33
14
  end
34
15
  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.6.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-15 00:00:00.000000000 Z
11
+ date: 2022-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tapyrus
@@ -103,6 +103,7 @@ files:
103
103
  - lib/glueby/active_record/system_information.rb
104
104
  - lib/glueby/block_syncer.rb
105
105
  - lib/glueby/configuration.rb
106
+ - lib/glueby/constants.rb
106
107
  - lib/glueby/contract.rb
107
108
  - lib/glueby/contract/active_record.rb
108
109
  - lib/glueby/contract/active_record/reissuable_token.rb