glueby 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,18 +11,32 @@ module Glueby
11
11
  end
12
12
 
13
13
  # 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
- tx = Tapyrus::Tx.new
16
- fee = fee_estimator.fee(dummy_tx(tx))
17
-
18
- sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee + FUNDING_TX_AMOUNT)
19
- fill_input(tx, outputs)
20
-
21
- receiver_script = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
22
- tx.outputs << Tapyrus::TxOut.new(value: FUNDING_TX_AMOUNT, script_pubkey: receiver_script)
23
-
24
- fill_change_tpc(tx, wallet, sum - fee - FUNDING_TX_AMOUNT)
25
- wallet.internal_wallet.sign_tx(tx)
14
+ def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new, utxo_provider: nil)
15
+ if utxo_provider
16
+ 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)
18
+ utxo_provider.wallet.sign_tx(funding_tx)
19
+ else
20
+ txb = Tapyrus::TxBuilder.new
21
+ fee = fee_estimator.fee(dummy_tx(txb.build))
22
+
23
+ sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee + FUNDING_TX_AMOUNT)
24
+ outputs.each do |utxo|
25
+ txb.add_utxo({
26
+ script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
27
+ txid: utxo[:txid],
28
+ index: utxo[:vout],
29
+ value: utxo[:amount]
30
+ })
31
+ end
32
+
33
+ receiver_address = script ? script.addresses.first : wallet.internal_wallet.receive_address
34
+ tx = txb.pay(receiver_address, FUNDING_TX_AMOUNT)
35
+ .change_address(wallet.internal_wallet.change_address)
36
+ .fee(fee)
37
+ .build
38
+ wallet.internal_wallet.sign_tx(tx)
39
+ end
26
40
  end
27
41
 
28
42
  def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
@@ -48,21 +62,27 @@ module Glueby
48
62
  issuer.internal_wallet.sign_tx(tx, prev_txs)
49
63
  end
50
64
 
51
- def create_issue_tx_for_non_reissuable_token(issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
52
- create_issue_tx_from_out_point(token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, fee_estimator: fee_estimator)
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)
53
67
  end
54
68
 
55
- def create_issue_tx_for_nft_token(issuer:, fee_estimator: FixedFeeEstimator.new)
56
- create_issue_tx_from_out_point(token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_estimator: fee_estimator)
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)
57
71
  end
58
72
 
59
- def create_issue_tx_from_out_point(token_type:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
73
+ def create_issue_tx_from_out_point(funding_tx: nil, token_type:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
60
74
  tx = Tapyrus::Tx.new
61
75
 
62
76
  fee = fee_estimator.fee(dummy_issue_tx_from_out_point)
63
- sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
64
- fill_input(tx, outputs)
65
-
77
+ sum = if funding_tx
78
+ out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
79
+ tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
80
+ funding_tx.outputs.first.value
81
+ else
82
+ sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
83
+ fill_input(tx, outputs)
84
+ sum
85
+ end
66
86
  out_point = tx.inputs.first.out_point
67
87
  color_id = case token_type
68
88
  when Tapyrus::Color::TokenTypes::NON_REISSUABLE
@@ -78,7 +98,18 @@ module Glueby
78
98
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
79
99
 
80
100
  fill_change_tpc(tx, issuer, sum - fee)
81
- issuer.internal_wallet.sign_tx(tx)
101
+ prev_txs = if funding_tx
102
+ output = funding_tx.outputs.first
103
+ [{
104
+ txid: funding_tx.txid,
105
+ vout: 0,
106
+ scriptPubKey: output.script_pubkey.to_hex,
107
+ amount: output.value
108
+ }]
109
+ else
110
+ []
111
+ end
112
+ issuer.internal_wallet.sign_tx(tx, prev_txs)
82
113
  end
83
114
 
84
115
  def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, fee_estimator: FixedFeeEstimator.new)
@@ -103,7 +134,7 @@ module Glueby
103
134
  issuer.internal_wallet.sign_tx(tx, prev_txs)
104
135
  end
105
136
 
106
- def create_transfer_tx(color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
137
+ def create_transfer_tx(funding_tx:nil, color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
107
138
  tx = Tapyrus::Tx.new
108
139
 
109
140
  utxos = sender.internal_wallet.list_unspent
@@ -117,14 +148,32 @@ module Glueby
117
148
  fill_change_token(tx, sender, sum_token - amount, color_id)
118
149
 
119
150
  fee = fee_estimator.fee(dummy_tx(tx))
120
- sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
121
- fill_input(tx, outputs)
151
+ sum_tpc = if funding_tx
152
+ out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
153
+ tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
154
+ funding_tx.outputs.first.value
155
+ else
156
+ sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
157
+ fill_input(tx, outputs)
158
+ sum_tpc
159
+ end
122
160
 
123
161
  fill_change_tpc(tx, sender, sum_tpc - fee)
124
- sender.internal_wallet.sign_tx(tx)
162
+ prev_txs = if funding_tx
163
+ output = funding_tx.outputs.first
164
+ [{
165
+ txid: funding_tx.txid,
166
+ vout: 0,
167
+ scriptPubKey: output.script_pubkey.to_hex,
168
+ amount: output.value
169
+ }]
170
+ else
171
+ []
172
+ end
173
+ sender.internal_wallet.sign_tx(tx, prev_txs)
125
174
  end
126
175
 
127
- def create_burn_tx(color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new)
176
+ def create_burn_tx(funding_tx:nil, color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new)
128
177
  tx = Tapyrus::Tx.new
129
178
 
130
179
  utxos = sender.internal_wallet.list_unspent
@@ -135,12 +184,30 @@ module Glueby
135
184
 
136
185
  fee = fee_estimator.fee(dummy_tx(tx))
137
186
 
138
- dust = 600 # in case that the wallet has output which has just fee amount.
139
- sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + dust)
140
- fill_input(tx, outputs)
187
+ sum_tpc = if funding_tx
188
+ out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
189
+ tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
190
+ funding_tx.outputs.first.value
191
+ 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)
194
+ fill_input(tx, outputs)
195
+ sum_tpc
196
+ end
141
197
 
142
198
  fill_change_tpc(tx, sender, sum_tpc - fee)
143
- sender.internal_wallet.sign_tx(tx)
199
+ prev_txs = if funding_tx
200
+ output = funding_tx.outputs.first
201
+ [{
202
+ txid: funding_tx.txid,
203
+ vout: 0,
204
+ scriptPubKey: output.script_pubkey.to_hex,
205
+ amount: output.value
206
+ }]
207
+ else
208
+ []
209
+ end
210
+ sender.internal_wallet.sign_tx(tx, prev_txs)
144
211
  end
145
212
 
146
213
  def fill_input(tx, outputs)
@@ -1,141 +1,141 @@
1
- module Glueby
2
- class FeeProvider
3
- class Tasks
4
- attr_reader :fee_provider
5
-
6
- STATUS = {
7
- # FeeProvider is ready to pay fees.
8
- ready: 'Ready',
9
- # FeeProvider is ready to pay fees, but it doesn't have enough amount to fill the UTXO pool by UTXOs which is for paying fees.
10
- insufficient_amount: 'Insufficient Amount',
11
- # FeeProvider is not ready to pay fees. It has no UTXOs for paying fee and amounts.
12
- not_ready: 'Not Ready'
13
- }
14
-
15
- def initialize
16
- @fee_provider = Glueby::FeeProvider.new
17
- end
18
-
19
- # Create UTXOs for paying fee from TPC amount of the wallet FeeProvider has. Then show the status.
20
- #
21
- # About the UTXO Pool
22
- # FeeProvider have the UTXO pool. the pool is manged to keep some number of UTXOs that have fixed fee value. The
23
- # value is configurable by :fixed_fee. This method do the management to the pool.
24
- def manage_utxo_pool
25
- txb = Tapyrus::TxBuilder.new
26
-
27
- sum, utxos = collect_outputs
28
- return if utxos.empty?
29
-
30
- utxos.each { |utxo| txb.add_utxo(utxo) }
31
- address = wallet.receive_address
32
-
33
- shortage = [fee_provider.utxo_pool_size - current_utxo_pool_size, 0].max
34
- can_create = (sum - fee_provider.fixed_fee) / fee_provider.fixed_fee
35
- fee_outputs_count_to_be_created = [shortage, can_create].min
36
-
37
- return if fee_outputs_count_to_be_created == 0
38
-
39
- fee_outputs_count_to_be_created.times do
40
- txb.pay(address, fee_provider.fixed_fee)
41
- end
42
-
43
- tx = txb.change_address(address)
44
- .fee(fee_provider.fixed_fee)
45
- .build
46
- tx = wallet.sign_tx(tx)
47
- wallet.broadcast(tx, without_fee_provider: true)
48
- ensure
49
- status
50
- end
51
-
52
- # Show the status of the UTXO pool
53
- def status
54
- status = :ready
55
-
56
- if current_utxo_pool_size < fee_provider.utxo_pool_size
57
- if tpc_amount < value_to_fill_utxo_pool
58
- status = :insufficient_amount
59
- message = <<~MESSAGE
60
- 1. Please replenishment TPC which is for paying fee to FeeProvider.
61
- FeeProvider needs #{value_to_fill_utxo_pool} tapyrus at least for paying 20 transaction fees.
62
- FeeProvider wallet's address is '#{wallet.receive_address}'
63
- 2. Then create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'
64
- MESSAGE
65
- else
66
- message = "Please create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'\n"
67
- end
68
- end
69
-
70
- status = :not_ready if current_utxo_pool_size == 0
71
-
72
- puts <<~EOS
73
- Status: #{STATUS[status]}
74
- TPC amount: #{delimit(tpc_amount)}
75
- UTXO pool size: #{delimit(current_utxo_pool_size)}
76
- #{"\n" if message}#{message}
77
- Configuration:
78
- fixed_fee = #{delimit(fee_provider.fixed_fee)}
79
- utxo_pool_size = #{delimit(fee_provider.utxo_pool_size)}
80
- EOS
81
- end
82
-
83
- # Show the address of Fee Provider
84
- def address
85
- puts wallet.receive_address
86
- end
87
-
88
- private
89
-
90
- def check_wallet_amount!
91
- if tpc_amount < fee_provider.fixed_fee
92
- raise InsufficientTPC, <<~MESSAGE
93
- FeeProvider has insufficient TPC to create fee outputs to fill the UTXO pool.
94
- 1. Please replenishment TPC which is for paying fee to FeeProvider. FeeProvider needs #{fee_provider.utxo_pool_size * fee_provider.fixed_fee} tapyrus at least. FeeProvider wallet's address is '#{wallet.receive_address}'
95
- 2. Then create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'
96
- MESSAGE
97
- end
98
- end
99
-
100
- def tpc_amount
101
- wallet.balance(false)
102
- end
103
-
104
- def collect_outputs
105
- wallet.list_unspent.inject([0, []]) do |sum, output|
106
- next sum if output[:color_id] || output[:amount] == fee_provider.fixed_fee
107
-
108
- new_sum = sum[0] + output[:amount]
109
- new_outputs = sum[1] << {
110
- txid: output[:txid],
111
- script_pubkey: output[:script_pubkey],
112
- value: output[:amount],
113
- index: output[:vout] ,
114
- finalized: output[:finalized]
115
- }
116
- return [new_sum, new_outputs] if new_sum >= value_to_fill_utxo_pool
117
-
118
- [new_sum, new_outputs]
119
- end
120
- end
121
-
122
- def current_utxo_pool_size
123
- wallet
124
- .list_unspent(false)
125
- .count { |o| !o[:color_id] && o[:amount] == fee_provider.fixed_fee }
126
- end
127
-
128
- def value_to_fill_utxo_pool
129
- fee_provider.fixed_fee * (fee_provider.utxo_pool_size + 1) # +1 is for paying fee
130
- end
131
-
132
- def wallet
133
- fee_provider.wallet
134
- end
135
-
136
- def delimit(num)
137
- num.to_s.reverse.scan(/.{1,3}/).join('_').reverse
138
- end
139
- end
140
- end
1
+ module Glueby
2
+ class FeeProvider
3
+ class Tasks
4
+ attr_reader :fee_provider
5
+
6
+ STATUS = {
7
+ # FeeProvider is ready to pay fees.
8
+ ready: 'Ready',
9
+ # FeeProvider is ready to pay fees, but it doesn't have enough amount to fill the UTXO pool by UTXOs which is for paying fees.
10
+ insufficient_amount: 'Insufficient Amount',
11
+ # FeeProvider is not ready to pay fees. It has no UTXOs for paying fee and amounts.
12
+ not_ready: 'Not Ready'
13
+ }
14
+
15
+ def initialize
16
+ @fee_provider = Glueby::FeeProvider.new
17
+ end
18
+
19
+ # Create UTXOs for paying fee from TPC amount of the wallet FeeProvider has. Then show the status.
20
+ #
21
+ # About the UTXO Pool
22
+ # FeeProvider have the UTXO pool. the pool is manged to keep some number of UTXOs that have fixed fee value. The
23
+ # value is configurable by :fixed_fee. This method do the management to the pool.
24
+ def manage_utxo_pool
25
+ txb = Tapyrus::TxBuilder.new
26
+
27
+ sum, utxos = collect_outputs
28
+ return if utxos.empty?
29
+
30
+ utxos.each { |utxo| txb.add_utxo(utxo) }
31
+ address = wallet.receive_address
32
+
33
+ shortage = [fee_provider.utxo_pool_size - current_utxo_pool_size, 0].max
34
+ can_create = (sum - fee_provider.fixed_fee) / fee_provider.fixed_fee
35
+ fee_outputs_count_to_be_created = [shortage, can_create].min
36
+
37
+ return if fee_outputs_count_to_be_created == 0
38
+
39
+ fee_outputs_count_to_be_created.times do
40
+ txb.pay(address, fee_provider.fixed_fee)
41
+ end
42
+
43
+ tx = txb.change_address(address)
44
+ .fee(fee_provider.fixed_fee)
45
+ .build
46
+ tx = wallet.sign_tx(tx)
47
+ wallet.broadcast(tx, without_fee_provider: true)
48
+ ensure
49
+ status
50
+ end
51
+
52
+ # Show the status of the UTXO pool
53
+ def status
54
+ status = :ready
55
+
56
+ if current_utxo_pool_size < fee_provider.utxo_pool_size
57
+ if tpc_amount < value_to_fill_utxo_pool
58
+ status = :insufficient_amount
59
+ message = <<~MESSAGE
60
+ 1. Please replenishment TPC which is for paying fee to FeeProvider.
61
+ FeeProvider needs #{value_to_fill_utxo_pool} tapyrus at least for paying 20 transaction fees.
62
+ FeeProvider wallet's address is '#{wallet.receive_address}'
63
+ 2. Then create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'
64
+ MESSAGE
65
+ else
66
+ message = "Please create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'\n"
67
+ end
68
+ end
69
+
70
+ status = :not_ready if current_utxo_pool_size == 0
71
+
72
+ puts <<~EOS
73
+ Status: #{STATUS[status]}
74
+ TPC amount: #{delimit(tpc_amount)}
75
+ UTXO pool size: #{delimit(current_utxo_pool_size)}
76
+ #{"\n" if message}#{message}
77
+ Configuration:
78
+ fixed_fee = #{delimit(fee_provider.fixed_fee)}
79
+ utxo_pool_size = #{delimit(fee_provider.utxo_pool_size)}
80
+ EOS
81
+ end
82
+
83
+ # Show the address of Fee Provider
84
+ def print_address
85
+ puts wallet.receive_address
86
+ end
87
+
88
+ private
89
+
90
+ def check_wallet_amount!
91
+ if tpc_amount < fee_provider.fixed_fee
92
+ raise InsufficientTPC, <<~MESSAGE
93
+ FeeProvider has insufficient TPC to create fee outputs to fill the UTXO pool.
94
+ 1. Please replenishment TPC which is for paying fee to FeeProvider. FeeProvider needs #{fee_provider.utxo_pool_size * fee_provider.fixed_fee} tapyrus at least. FeeProvider wallet's address is '#{wallet.receive_address}'
95
+ 2. Then create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'
96
+ MESSAGE
97
+ end
98
+ end
99
+
100
+ def tpc_amount
101
+ wallet.balance(false)
102
+ end
103
+
104
+ def collect_outputs
105
+ wallet.list_unspent.inject([0, []]) do |sum, output|
106
+ next sum if output[:color_id] || output[:amount] == fee_provider.fixed_fee
107
+
108
+ new_sum = sum[0] + output[:amount]
109
+ new_outputs = sum[1] << {
110
+ txid: output[:txid],
111
+ script_pubkey: output[:script_pubkey],
112
+ value: output[:amount],
113
+ index: output[:vout] ,
114
+ finalized: output[:finalized]
115
+ }
116
+ return [new_sum, new_outputs] if new_sum >= value_to_fill_utxo_pool
117
+
118
+ [new_sum, new_outputs]
119
+ end
120
+ end
121
+
122
+ def current_utxo_pool_size
123
+ wallet
124
+ .list_unspent(false)
125
+ .count { |o| !o[:color_id] && o[:amount] == fee_provider.fixed_fee }
126
+ end
127
+
128
+ def value_to_fill_utxo_pool
129
+ fee_provider.fixed_fee * (fee_provider.utxo_pool_size + 1) # +1 is for paying fee
130
+ end
131
+
132
+ def wallet
133
+ fee_provider.wallet
134
+ end
135
+
136
+ def delimit(num)
137
+ num.to_s.reverse.scan(/.{1,3}/).join('_').reverse
138
+ end
139
+ end
140
+ end
141
141
  end
@@ -8,6 +8,7 @@ module Glueby
8
8
  WALLET_ID = 'FEE_PROVIDER_WALLET'
9
9
  DEFAULT_FIXED_FEE = 1000
10
10
  DEFAULT_UTXO_POOL_SIZE = 20
11
+ MAX_UTXO_POOL_SIZE = 2_000
11
12
 
12
13
  attr_reader :fixed_fee, :utxo_pool_size, :wallet,
13
14
 
@@ -33,6 +34,7 @@ module Glueby
33
34
  Internal::Wallet.create(WALLET_ID)
34
35
  end
35
36
 
37
+ validate_config!
36
38
  @fixed_fee = (FeeProvider.config && FeeProvider.config[:fixed_fee]) || DEFAULT_FIXED_FEE
37
39
  @utxo_pool_size = (FeeProvider.config && FeeProvider.config[:utxo_pool_size]) || DEFAULT_UTXO_POOL_SIZE
38
40
  end
@@ -69,5 +71,14 @@ module Glueby
69
71
  def get_signature(script_sig)
70
72
  script_sig.chunks.first.pushed_data
71
73
  end
74
+
75
+ def validate_config!
76
+ if FeeProvider.config
77
+ utxo_pool_size = FeeProvider.config[:utxo_pool_size]
78
+ if utxo_pool_size && (!utxo_pool_size.is_a?(Integer) || utxo_pool_size > MAX_UTXO_POOL_SIZE)
79
+ raise Glueby::Configuration::Errors::InvalidConfiguration, "utxo_pool_size(#{utxo_pool_size}) should not be greater than #{MAX_UTXO_POOL_SIZE}"
80
+ end
81
+ end
82
+ end
72
83
  end
73
84
  end