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 +4 -4
- data/lib/glueby/active_record/system_information.rb +19 -1
- data/lib/glueby/constants.rb +4 -0
- data/lib/glueby/contract/active_record/timestamp.rb +30 -0
- data/lib/glueby/contract/errors.rb +1 -0
- data/lib/glueby/contract/fee_estimator.rb +2 -0
- data/lib/glueby/contract/token.rb +79 -23
- data/lib/glueby/contract/tx_builder.rb +75 -31
- data/lib/glueby/utxo_provider/tasks.rb +6 -3
- data/lib/glueby/utxo_provider.rb +22 -9
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby.rb +11 -0
- data/lib/tasks/glueby/contract/timestamp.rake +2 -21
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2f53ca4e5784495dc9ddc7669bc021985614b3719205247c42005ec5d12a5c4
|
4
|
+
data.tar.gz: d5bb0540f3db7cf5ccdb96d4487ff627c4090f69e8719f746badae31689f864d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
@@ -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
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
175
|
-
funding_tx =
|
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 =
|
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
|
-
|
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,
|
15
|
-
if
|
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,
|
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(
|
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,
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
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
|
-
|
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 '#{
|
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
|
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
|
data/lib/glueby/utxo_provider.rb
CHANGED
@@ -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
|
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] ==
|
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
|
92
|
-
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
|
data/lib/glueby/version.rb
CHANGED
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
|
-
|
12
|
-
timestamps.each
|
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.
|
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:
|
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
|