glueby 0.8.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/glueby.gemspec +1 -1
- data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +4 -0
- data/lib/glueby/active_record/system_information.rb +69 -0
- data/lib/glueby/configuration.rb +1 -1
- data/lib/glueby/contract/active_record/timestamp.rb +142 -16
- data/lib/glueby/contract/errors.rb +16 -10
- data/lib/glueby/contract/timestamp/tx_builder/simple.rb +97 -0
- data/lib/glueby/contract/timestamp/tx_builder/trackable.rb +23 -0
- data/lib/glueby/contract/timestamp/tx_builder/updating_trackable.rb +28 -0
- data/lib/glueby/contract/timestamp/tx_builder.rb +14 -0
- data/lib/glueby/contract/timestamp.rb +36 -92
- data/lib/glueby/contract/token.rb +4 -1
- data/lib/glueby/contract.rb +0 -1
- data/lib/glueby/fee_provider.rb +2 -2
- data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +25 -0
- data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +45 -0
- data/lib/glueby/internal/wallet/errors.rb +6 -6
- data/lib/glueby/internal/wallet.rb +8 -0
- data/lib/glueby/utxo_provider/tasks.rb +15 -37
- data/lib/glueby/utxo_provider.rb +19 -1
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby.rb +5 -0
- data/lib/tasks/glueby/contract/timestamp.rake +0 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53718ece4b629ff6474c139b047e08c4803739cb992b61aa4f49c10ca96bc90f
|
4
|
+
data.tar.gz: 774199828793ab8c00c3b5d3a501ffa5e265395e7e036407d7ef0edd7f798998
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89cb7a6e1a9573bd840811bf75da2d83f9a77eaf1d265e0ba617093e788eeb785f23d80ea67e934714f2773565d26065a4a3daa56bc8d544cc13d2bbd66f3049
|
7
|
+
data.tar.gz: d1616df8862ede1f1046119f321b22faf8dd7f6193d4a14ca7a38388d60c6f9e5ac97408480b4c38c2d817036424aeba9f2c50574c655b7ac7f9f3e0522a0cac
|
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.
|
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'
|
@@ -9,6 +9,10 @@ class CreateTimestamp < ActiveRecord::Migration<%= migration_version %>
|
|
9
9
|
t.integer :timestamp_type, null: false, default: 0
|
10
10
|
t.string :p2c_address
|
11
11
|
t.string :payment_base
|
12
|
+
t.bigint :prev_id
|
13
|
+
t.boolean :latest, null: false, default: true
|
12
14
|
end
|
15
|
+
|
16
|
+
add_index :glueby_timestamps, [:prev_id], unique: true
|
13
17
|
end
|
14
18
|
end
|
@@ -12,22 +12,91 @@ module Glueby
|
|
12
12
|
find_by(info_key: "use_only_finalized_utxo")&.int_value != 0
|
13
13
|
end
|
14
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
|
+
|
15
30
|
# Return default value of the utxo provider
|
16
31
|
# @return [Integer] default value of utxo provider
|
17
32
|
def self.utxo_provider_default_value
|
18
33
|
find_by(info_key: "utxo_provider_default_value")&.int_value
|
19
34
|
end
|
20
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
|
+
|
21
50
|
# Return pool size of the utxo provider
|
22
51
|
# @return [Integer] pool size of utxo provider
|
23
52
|
def self.utxo_provider_pool_size
|
24
53
|
find_by(info_key: "utxo_provider_pool_size")&.int_value
|
25
54
|
end
|
26
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
|
88
|
+
end
|
89
|
+
|
27
90
|
def int_value
|
28
91
|
info_value.to_i
|
29
92
|
end
|
30
93
|
|
94
|
+
private
|
95
|
+
|
96
|
+
def self.boolean_to_string(status)
|
97
|
+
status ? "1" : "0"
|
98
|
+
end
|
99
|
+
|
31
100
|
end
|
32
101
|
end
|
33
102
|
end
|
data/lib/glueby/configuration.rb
CHANGED
@@ -3,53 +3,179 @@ module Glueby
|
|
3
3
|
module AR
|
4
4
|
class Timestamp < ::ActiveRecord::Base
|
5
5
|
include Glueby::GluebyLogger
|
6
|
-
include Glueby::Contract::Timestamp::Util
|
7
|
-
include Glueby::Contract::TxBuilder
|
8
6
|
|
9
7
|
enum status: { init: 0, unconfirmed: 1, confirmed: 2 }
|
10
8
|
enum timestamp_type: { simple: 0, trackable: 1 }
|
11
9
|
|
10
|
+
attr_reader :tx
|
11
|
+
|
12
|
+
belongs_to :prev, class_name: 'Glueby::Contract::AR::Timestamp'
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def digest_content(content, digest)
|
16
|
+
case digest&.downcase
|
17
|
+
when :sha256
|
18
|
+
Tapyrus.sha256(content).bth
|
19
|
+
when :double_sha256
|
20
|
+
Tapyrus.double_sha256(content).bth
|
21
|
+
when :none
|
22
|
+
content
|
23
|
+
else
|
24
|
+
raise Glueby::Contract::Errors::UnsupportedDigestType
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
12
29
|
# @param [Hash] attributes attributes which consist of:
|
13
30
|
# - wallet_id
|
14
31
|
# - content
|
15
32
|
# - prefix(optional)
|
16
33
|
# - timestamp_type(optional)
|
34
|
+
# @raise [Glueby::ArgumentError] If the timestamp_type is not in :simple or :trackable
|
17
35
|
def initialize(attributes = nil)
|
18
|
-
|
19
|
-
|
20
|
-
|
36
|
+
# Set content_hash from :content attribute
|
37
|
+
content_hash = Timestamp.digest_content(attributes[:content], attributes[:digest] || :sha256)
|
38
|
+
super(
|
39
|
+
wallet_id: attributes[:wallet_id],
|
40
|
+
content_hash: content_hash,
|
41
|
+
prefix: attributes[:prefix] ? attributes[:prefix] : '',
|
42
|
+
status: :init,
|
43
|
+
timestamp_type: attributes[:timestamp_type] || :simple,
|
44
|
+
prev_id: attributes[:prev_id]
|
45
|
+
)
|
46
|
+
rescue ::ArgumentError => e
|
47
|
+
raise Glueby::ArgumentError, e.message
|
21
48
|
end
|
22
49
|
|
23
50
|
# Return true if timestamp type is 'trackable' and output in timestamp transaction has not been spent yet, otherwise return false.
|
24
|
-
def latest
|
25
|
-
trackable?
|
51
|
+
def latest?
|
52
|
+
trackable? && attributes['latest']
|
53
|
+
end
|
54
|
+
alias_method :latest, :latest?
|
55
|
+
|
56
|
+
# Returns a UTXO that corresponds to the timestamp
|
57
|
+
# @return [Hash] UTXO
|
58
|
+
# - [String] script_pubkey A script pubkey hex string
|
59
|
+
# - [String] txid A txid
|
60
|
+
# - [Integer] vout An index of the tx
|
61
|
+
# - [Integer] amount A value of
|
62
|
+
def utxo
|
63
|
+
{
|
64
|
+
script_pubkey: Tapyrus::Script.parse_from_addr(p2c_address).to_hex,
|
65
|
+
txid: txid,
|
66
|
+
vout: Contract::Timestamp::TxBuilder::PAY_TO_CONTRACT_INPUT_INDEX,
|
67
|
+
amount: Glueby::Contract::Timestamp::P2C_DEFAULT_VALUE
|
68
|
+
}
|
26
69
|
end
|
27
70
|
|
28
71
|
# Broadcast and save timestamp
|
29
72
|
# @param [Glueby::Contract::FixedFeeEstimator] fee_estimator
|
73
|
+
# @param [Glueby::UtxoProvider] utxo_provider
|
30
74
|
# @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
|
-
|
33
|
-
|
34
|
-
|
75
|
+
def save_with_broadcast(fee_estimator: Glueby::Contract::FixedFeeEstimator.new, utxo_provider: nil)
|
76
|
+
save_with_broadcast!(fee_estimator: fee_estimator, utxo_provider: utxo_provider)
|
77
|
+
rescue Errors::FailedToBroadcast, Errors::PrevTimestampNotFound, Errors::PrevTimestampIsNotTrackable => e
|
78
|
+
logger.error("failed to broadcast (id=#{id}, reason=#{e.message})")
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
# Broadcast and save timestamp, and it raises errors
|
83
|
+
# @param [Glueby::Contract::FixedFeeEstimator] fee_estimator
|
84
|
+
# @param [Glueby::UtxoProvider] utxo_provider
|
85
|
+
# @return true if tapyrus transactions were broadcasted and the timestamp was updated successfully
|
86
|
+
# @raise [Glueby::Contract::Errors::FailedToBroadcast] If the broadcasting is failure
|
87
|
+
# @raise [Glueby::Contract::Errors::PrevTimestampNotFound] If it is not available that the timestamp record which correspond with the prev_id attribute
|
88
|
+
# @raise [Glueby::Contract::Errors::PrevTimestampIsNotTrackable] If the timestamp record by prev_id is not trackable
|
89
|
+
def save_with_broadcast!(fee_estimator: Glueby::Contract::FixedFeeEstimator.new, utxo_provider: nil)
|
90
|
+
utxo_provider = Glueby::UtxoProvider.new if !utxo_provider && Glueby.configuration.use_utxo_provider?
|
91
|
+
|
92
|
+
funding_tx, tx, p2c_address, payment_base = create_txs(fee_estimator, utxo_provider)
|
93
|
+
|
35
94
|
if funding_tx
|
36
95
|
::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
37
96
|
wallet.internal_wallet.broadcast(funding_tx)
|
38
|
-
logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
|
39
97
|
end
|
98
|
+
logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
|
40
99
|
end
|
41
100
|
::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
42
101
|
wallet.internal_wallet.broadcast(tx) do |tx|
|
43
102
|
assign_attributes(txid: tx.txid, status: :unconfirmed, p2c_address: p2c_address, payment_base: payment_base)
|
103
|
+
@tx = tx
|
44
104
|
save!
|
105
|
+
|
106
|
+
if update_trackable?
|
107
|
+
prev.latest = false
|
108
|
+
prev.save!
|
109
|
+
end
|
45
110
|
end
|
46
|
-
logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
|
47
111
|
end
|
112
|
+
logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
|
48
113
|
true
|
49
|
-
rescue
|
50
|
-
|
114
|
+
rescue ActiveRecord::RecordInvalid,
|
115
|
+
Tapyrus::RPC::Error,
|
116
|
+
Internal::Wallet::Errors::WalletAlreadyLoaded,
|
117
|
+
Internal::Wallet::Errors::WalletNotFound,
|
118
|
+
Errors::InsufficientFunds => e
|
51
119
|
errors.add(:base, "failed to broadcast (id=#{id}, reason=#{e.message})")
|
52
|
-
|
120
|
+
raise Errors::FailedToBroadcast, "failed to broadcast (id=#{id}, reason=#{e.message})"
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def wallet
|
126
|
+
@wallet ||= Glueby::Wallet.load(wallet_id)
|
127
|
+
end
|
128
|
+
|
129
|
+
def create_txs(fee_estimator, utxo_provider)
|
130
|
+
builder = builder_class.new(wallet, fee_estimator)
|
131
|
+
|
132
|
+
if builder.instance_of?(Contract::Timestamp::TxBuilder::UpdatingTrackable)
|
133
|
+
unless prev
|
134
|
+
message = "The previous timestamp(id: #{prev_id}) not found."
|
135
|
+
errors.add(:prev_id, message)
|
136
|
+
raise Errors::PrevTimestampNotFound, message
|
137
|
+
end
|
138
|
+
|
139
|
+
unless prev.trackable?
|
140
|
+
message = "The previous timestamp(id: #{prev_id}) type must be trackable"
|
141
|
+
errors.add(:prev_id, message)
|
142
|
+
raise Errors::PrevTimestampIsNotTrackable, message
|
143
|
+
end
|
144
|
+
|
145
|
+
builder.set_prev_timestamp_info(
|
146
|
+
timestamp_utxo: prev.utxo,
|
147
|
+
payment_base: prev.payment_base,
|
148
|
+
prefix: prev.prefix,
|
149
|
+
data: prev.content_hash
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
tx = builder.set_data(prefix, content_hash)
|
154
|
+
.set_inputs(utxo_provider)
|
155
|
+
.build
|
156
|
+
|
157
|
+
if builder.instance_of?(Contract::Timestamp::TxBuilder::Simple)
|
158
|
+
[builder.funding_tx, tx, nil, nil]
|
159
|
+
else
|
160
|
+
[builder.funding_tx, tx, builder.p2c_address, builder.payment_base]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def builder_class
|
165
|
+
case timestamp_type.to_sym
|
166
|
+
when :simple
|
167
|
+
Contract::Timestamp::TxBuilder::Simple
|
168
|
+
when :trackable
|
169
|
+
if prev_id
|
170
|
+
Contract::Timestamp::TxBuilder::UpdatingTrackable
|
171
|
+
else
|
172
|
+
Contract::Timestamp::TxBuilder::Trackable
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def update_trackable?
|
178
|
+
trackable? && prev_id
|
53
179
|
end
|
54
180
|
end
|
55
181
|
end
|
@@ -1,16 +1,22 @@
|
|
1
1
|
module Glueby
|
2
2
|
module Contract
|
3
3
|
module Errors
|
4
|
-
class InsufficientFunds <
|
5
|
-
class InsufficientTokens <
|
6
|
-
class
|
7
|
-
class
|
8
|
-
|
9
|
-
|
10
|
-
class
|
11
|
-
class
|
12
|
-
class
|
13
|
-
class
|
4
|
+
class InsufficientFunds < Error; end
|
5
|
+
class InsufficientTokens < Error; end
|
6
|
+
class TxAlreadyBroadcasted < Error; end
|
7
|
+
class FailedToBroadcast < Error; end
|
8
|
+
|
9
|
+
# Argument Errors
|
10
|
+
class ArgumentError < ArgumentError; end
|
11
|
+
class InvalidAmount < ArgumentError; end
|
12
|
+
class InvalidSplit < ArgumentError; end
|
13
|
+
class InvalidTokenType < ArgumentError; end
|
14
|
+
class InvalidTimestampType < ArgumentError; end
|
15
|
+
class UnsupportedTokenType < ArgumentError; end
|
16
|
+
class UnknownScriptPubkey < ArgumentError; end
|
17
|
+
class UnsupportedDigestType < ArgumentError; end
|
18
|
+
class PrevTimestampNotFound < ArgumentError; end
|
19
|
+
class PrevTimestampIsNotTrackable < ArgumentError; end
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Contract
|
3
|
+
class Timestamp
|
4
|
+
module TxBuilder
|
5
|
+
# The simple Timestamp method
|
6
|
+
class Simple
|
7
|
+
include Glueby::Contract::TxBuilder
|
8
|
+
|
9
|
+
attr_reader :funding_tx
|
10
|
+
|
11
|
+
def initialize(wallet, fee_estimator)
|
12
|
+
@wallet = wallet
|
13
|
+
@fee_estimator = fee_estimator
|
14
|
+
|
15
|
+
@txb = Tapyrus::TxBuilder.new
|
16
|
+
@prev_txs = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def build
|
20
|
+
@txb.fee(fee).change_address(@wallet.internal_wallet.change_address)
|
21
|
+
sign_tx
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_data(prefix, data)
|
25
|
+
@prefix = prefix
|
26
|
+
@data = data
|
27
|
+
|
28
|
+
@txb.data(prefix + data)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_inputs(utxo_provider)
|
33
|
+
if utxo_provider
|
34
|
+
script_pubkey = Tapyrus::Script.parse_from_addr(@wallet.internal_wallet.receive_address)
|
35
|
+
@funding_tx, index = utxo_provider.get_utxo(script_pubkey, fee)
|
36
|
+
|
37
|
+
utxo = {
|
38
|
+
script_pubkey: @funding_tx.outputs[index].script_pubkey.to_hex,
|
39
|
+
txid: @funding_tx.txid,
|
40
|
+
vout: index,
|
41
|
+
amount: funding_tx.outputs[index].value
|
42
|
+
}
|
43
|
+
|
44
|
+
@txb.add_utxo(to_tapyrusrb_utxo_hash(utxo))
|
45
|
+
@prev_txs << to_sign_tx_utxo_hash(utxo)
|
46
|
+
else
|
47
|
+
_, outputs = @wallet.internal_wallet.collect_uncolored_outputs(fee)
|
48
|
+
outputs.each do |utxo|
|
49
|
+
@txb.add_utxo(to_tapyrusrb_utxo_hash(utxo))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def fee
|
58
|
+
@fee ||= @fee_estimator.fee(dummy_tx(@txb.build))
|
59
|
+
end
|
60
|
+
|
61
|
+
def sign_tx
|
62
|
+
# General signing process skips signing to p2c inputs because no key of the p2c address in the wallet.
|
63
|
+
@wallet.internal_wallet.sign_tx(@txb.build, @prev_txs)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param utxo
|
67
|
+
# @option utxo [String] :txid The txid
|
68
|
+
# @option utxo [Integer] :vout The index of the output in the tx
|
69
|
+
# @option utxo [Integer] :amount The value of the output
|
70
|
+
# @option utxo [String] :script_pubkey The hex string of the script pubkey
|
71
|
+
def to_tapyrusrb_utxo_hash(utxo)
|
72
|
+
{
|
73
|
+
script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
|
74
|
+
txid: utxo[:txid],
|
75
|
+
index: utxo[:vout],
|
76
|
+
value: utxo[:amount]
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param utxo
|
81
|
+
# @option utxo [String] :txid The txid
|
82
|
+
# @option utxo [Integer] :vout The index of the output in the tx
|
83
|
+
# @option utxo [Integer] :amount The value of the output
|
84
|
+
# @option utxo [String] :script_pubkey The hex string of the script pubkey
|
85
|
+
def to_sign_tx_utxo_hash(utxo)
|
86
|
+
{
|
87
|
+
scriptPubKey: utxo[:script_pubkey],
|
88
|
+
txid: utxo[:txid],
|
89
|
+
vout: utxo[:vout],
|
90
|
+
amount: utxo[:amount]
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Contract
|
3
|
+
class Timestamp
|
4
|
+
module TxBuilder
|
5
|
+
class Trackable < Simple
|
6
|
+
attr_reader :p2c_address, :payment_base
|
7
|
+
|
8
|
+
# @override
|
9
|
+
def set_data(prefix, data)
|
10
|
+
@prefix = prefix
|
11
|
+
@data = data
|
12
|
+
|
13
|
+
# Create a new trackable timestamp
|
14
|
+
@p2c_address, @payment_base = @wallet.internal_wallet
|
15
|
+
.create_pay_to_contract_address([prefix, data].join)
|
16
|
+
@txb.pay(p2c_address, P2C_DEFAULT_VALUE)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Contract
|
3
|
+
class Timestamp
|
4
|
+
module TxBuilder
|
5
|
+
class UpdatingTrackable < Trackable
|
6
|
+
def set_prev_timestamp_info(timestamp_utxo:, payment_base:, prefix:, data:)
|
7
|
+
@prev_timestamp_utxo = timestamp_utxo
|
8
|
+
@prev_payment_base = payment_base
|
9
|
+
@prev_prefix = prefix
|
10
|
+
@prev_data = data
|
11
|
+
@txb.add_utxo(to_tapyrusrb_utxo_hash(@prev_timestamp_utxo))
|
12
|
+
end
|
13
|
+
|
14
|
+
def sign_tx
|
15
|
+
tx = super
|
16
|
+
# Generates signature for the remain p2c input.
|
17
|
+
@wallet.internal_wallet.sign_to_pay_to_contract_address(
|
18
|
+
tx,
|
19
|
+
@prev_timestamp_utxo,
|
20
|
+
@prev_payment_base,
|
21
|
+
[@prev_prefix, @prev_data].join
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Contract
|
3
|
+
class Timestamp
|
4
|
+
module TxBuilder
|
5
|
+
autoload :Simple, 'glueby/contract/timestamp/tx_builder/simple'
|
6
|
+
autoload :Trackable, 'glueby/contract/timestamp/tx_builder/trackable'
|
7
|
+
autoload :UpdatingTrackable, 'glueby/contract/timestamp/tx_builder/updating_trackable'
|
8
|
+
|
9
|
+
# A transaction input to update trackable timestamp must placed at this index in the tx inputs.
|
10
|
+
PAY_TO_CONTRACT_INPUT_INDEX = 0
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -8,87 +8,16 @@ module Glueby
|
|
8
8
|
#
|
9
9
|
# Storing timestamp transaction to the blockchain enables everyone to verify that the data existed at that time and a user signed it.
|
10
10
|
class Timestamp
|
11
|
-
include Glueby::Contract::TxBuilder
|
12
|
-
|
13
11
|
P2C_DEFAULT_VALUE = 1_000
|
14
12
|
|
15
13
|
autoload :Syncer, 'glueby/contract/timestamp/syncer'
|
16
|
-
|
17
|
-
module Util
|
18
|
-
include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
|
19
|
-
module_function
|
20
|
-
|
21
|
-
# @param [Glueby::Wallet] wallet
|
22
|
-
# @param [Array] contents The data to be used for generating pay-to-contract address
|
23
|
-
# @return [pay-to-contract address, public key used for generating address]
|
24
|
-
def create_pay_to_contract_address(wallet, contents: nil)
|
25
|
-
pubkey = wallet.internal_wallet.create_pubkey.pubkey
|
26
|
-
# Calculate P + H(P || contents)G
|
27
|
-
group = ECDSA::Group::Secp256k1
|
28
|
-
p = Tapyrus::Key.new(pubkey: pubkey).to_point # P
|
29
|
-
commitment = Tapyrus.sha256(p.to_hex(true).htb + contents.join).bth.to_i(16) % group.order # H(P || contents)
|
30
|
-
point = p + group.generator.multiply_by_scalar(commitment) # P + H(P || contents)G
|
31
|
-
[Tapyrus::Key.new(pubkey: point.to_hex(true)).to_p2pkh, pubkey] # [p2c address, P]
|
32
|
-
end
|
33
|
-
|
34
|
-
def create_txs(wallet, prefix, data, fee_estimator, utxo_provider, type: :simple)
|
35
|
-
txb = Tapyrus::TxBuilder.new
|
36
|
-
if type == :simple
|
37
|
-
txb.data(prefix + data)
|
38
|
-
elsif type == :trackable
|
39
|
-
p2c_address, payment_base = create_pay_to_contract_address(wallet, contents: [prefix, data])
|
40
|
-
txb.pay(p2c_address, P2C_DEFAULT_VALUE)
|
41
|
-
end
|
42
|
-
|
43
|
-
fee = fee_estimator.fee(dummy_tx(txb.build))
|
44
|
-
if utxo_provider
|
45
|
-
script_pubkey = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
|
46
|
-
funding_tx, index = utxo_provider.get_utxo(script_pubkey, fee)
|
47
|
-
txb.add_utxo({
|
48
|
-
script_pubkey: funding_tx.outputs[index].script_pubkey,
|
49
|
-
txid: funding_tx.txid,
|
50
|
-
index: index,
|
51
|
-
value: funding_tx.outputs[index].value
|
52
|
-
})
|
53
|
-
else
|
54
|
-
sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee)
|
55
|
-
outputs.each do |utxo|
|
56
|
-
txb.add_utxo({
|
57
|
-
script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
|
58
|
-
txid: utxo[:txid],
|
59
|
-
index: utxo[:vout],
|
60
|
-
value: utxo[:amount]
|
61
|
-
})
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
prev_txs = if funding_tx
|
66
|
-
output = funding_tx.outputs.first
|
67
|
-
[{
|
68
|
-
txid: funding_tx.txid,
|
69
|
-
vout: 0,
|
70
|
-
scriptPubKey: output.script_pubkey.to_hex,
|
71
|
-
amount: output.value
|
72
|
-
}]
|
73
|
-
else
|
74
|
-
[]
|
75
|
-
end
|
76
|
-
|
77
|
-
txb.fee(fee).change_address(wallet.internal_wallet.change_address)
|
78
|
-
[funding_tx, wallet.internal_wallet.sign_tx(txb.build, prev_txs), p2c_address, payment_base]
|
79
|
-
end
|
80
|
-
|
81
|
-
def get_transaction(tx)
|
82
|
-
Glueby::Internal::RPC.client.getrawtransaction(tx.txid, 1)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
include Glueby::Contract::Timestamp::Util
|
14
|
+
autoload :TxBuilder, 'glueby/contract/timestamp/tx_builder'
|
86
15
|
|
87
16
|
attr_reader :tx, :txid
|
88
|
-
|
89
17
|
# p2c_address and payment_base is used in `trackable` type
|
90
18
|
attr_reader :p2c_address, :payment_base
|
91
19
|
|
20
|
+
# @param [Gleuby::Wallet] wallet The wallet that is sender of the timestamp transaction.
|
92
21
|
# @param [String] content Data to be hashed and stored in blockchain.
|
93
22
|
# @param [String] prefix prefix of op_return data
|
94
23
|
# @param [Glueby::Contract::FeeEstimator] fee_estimator
|
@@ -99,6 +28,7 @@ module Glueby
|
|
99
28
|
# @param [Symbol] timestamp_type
|
100
29
|
# - :simple
|
101
30
|
# - :trackable
|
31
|
+
# @param [Integer] prev_timestamp_id The id column of glueby_timestamps that will be updated by the timestamp that will be created
|
102
32
|
# @raise [Glueby::Contract::Errors::UnsupportedDigestType] if digest is unsupported
|
103
33
|
# @raise [Glueby::Contract::Errors::InvalidTimestampType] if timestamp_type is unsupported
|
104
34
|
def initialize(
|
@@ -108,7 +38,8 @@ module Glueby
|
|
108
38
|
fee_estimator: Glueby::Contract::FixedFeeEstimator.new,
|
109
39
|
digest: :sha256,
|
110
40
|
utxo_provider: nil,
|
111
|
-
timestamp_type: :simple
|
41
|
+
timestamp_type: :simple,
|
42
|
+
prev_timestamp_id: nil
|
112
43
|
)
|
113
44
|
@wallet = wallet
|
114
45
|
@content = content
|
@@ -119,33 +50,46 @@ module Glueby
|
|
119
50
|
@utxo_provider = utxo_provider
|
120
51
|
raise Glueby::Contract::Errors::InvalidTimestampType, "#{timestamp_type} is invalid type, supported types are :simple, and :trackable." unless [:simple, :trackable].include?(timestamp_type)
|
121
52
|
@timestamp_type = timestamp_type
|
53
|
+
@prev_timestamp_id = prev_timestamp_id
|
122
54
|
end
|
123
55
|
|
124
56
|
# broadcast to Tapyrus Core
|
125
57
|
# @return [String] txid
|
126
|
-
# @raise [TxAlreadyBroadcasted] if tx has been broadcasted.
|
127
|
-
# @raise [InsufficientFunds] if result of listunspent is not enough to pay the specified amount
|
58
|
+
# @raise [Glueby::Contract::Errors::TxAlreadyBroadcasted] if tx has been broadcasted.
|
59
|
+
# @raise [Glueby::Contract::Errors::InsufficientFunds] if result of listunspent is not enough to pay the specified amount
|
128
60
|
def save!
|
129
|
-
raise Glueby::Contract::Errors::TxAlreadyBroadcasted if @
|
61
|
+
raise Glueby::Contract::Errors::TxAlreadyBroadcasted if @ar
|
62
|
+
|
63
|
+
@ar = Glueby::Contract::AR::Timestamp.new(
|
64
|
+
wallet_id: @wallet.id,
|
65
|
+
prefix: @prefix,
|
66
|
+
content: @content,
|
67
|
+
timestamp_type: @timestamp_type,
|
68
|
+
digest: @digest,
|
69
|
+
prev_id: @prev_timestamp_id
|
70
|
+
)
|
71
|
+
@ar.save_with_broadcast!(fee_estimator: @fee_estimator, utxo_provider: @utxo_provider)
|
72
|
+
@ar.txid
|
73
|
+
end
|
130
74
|
|
131
|
-
|
132
|
-
|
133
|
-
@txid
|
75
|
+
def txid
|
76
|
+
raise Glueby::Error, 'The timestamp tx is not broadcasted yet.' unless @ar
|
77
|
+
@ar.txid
|
134
78
|
end
|
135
79
|
|
136
|
-
|
80
|
+
def tx
|
81
|
+
raise Glueby::Error, 'The timestamp tx is not broadcasted yet.' unless @ar
|
82
|
+
@ar.tx
|
83
|
+
end
|
84
|
+
|
85
|
+
def p2c_address
|
86
|
+
raise Glueby::Error, 'The timestamp tx is not broadcasted yet.' unless @ar
|
87
|
+
@ar.p2c_address
|
88
|
+
end
|
137
89
|
|
138
|
-
def
|
139
|
-
|
140
|
-
|
141
|
-
Tapyrus.sha256(@content)
|
142
|
-
when :double_sha256
|
143
|
-
Tapyrus.double_sha256(@content)
|
144
|
-
when :none
|
145
|
-
@content
|
146
|
-
else
|
147
|
-
raise Glueby::Contract::Errors::UnsupportedDigestType
|
148
|
-
end
|
90
|
+
def payment_base
|
91
|
+
raise Glueby::Error, 'The timestamp tx is not broadcasted yet.' unless @ar
|
92
|
+
@ar.payment_base
|
149
93
|
end
|
150
94
|
end
|
151
95
|
end
|
@@ -91,11 +91,14 @@ module Glueby
|
|
91
91
|
script_pubkey = funding_tx.outputs.first.script_pubkey
|
92
92
|
color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
|
93
93
|
|
94
|
+
ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
95
|
+
funding_tx = issuer.internal_wallet.broadcast(funding_tx)
|
96
|
+
end
|
97
|
+
|
94
98
|
ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
95
99
|
# Store the script_pubkey for reissue the token.
|
96
100
|
Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
|
97
101
|
|
98
|
-
funding_tx = issuer.internal_wallet.broadcast(funding_tx)
|
99
102
|
tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
|
100
103
|
tx = issuer.internal_wallet.broadcast(tx)
|
101
104
|
[[funding_tx, tx], color_id]
|
data/lib/glueby/contract.rb
CHANGED
data/lib/glueby/fee_provider.rb
CHANGED
@@ -3,7 +3,7 @@ module Glueby
|
|
3
3
|
|
4
4
|
autoload :Tasks, 'glueby/fee_provider/tasks'
|
5
5
|
|
6
|
-
class NoUtxosInUtxoPool <
|
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|
|
@@ -148,6 +148,31 @@ module Glueby
|
|
148
148
|
def get_addresses(wallet_id, label = nil)
|
149
149
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
150
150
|
end
|
151
|
+
|
152
|
+
# Create and returns pay to contract address
|
153
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
154
|
+
# @param [String] contents - The data to be used for generating pay-to-contract address
|
155
|
+
# @return [String] pay to contract P2PKH address
|
156
|
+
def create_pay_to_contract_address(wallet_id, contents)
|
157
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Sign to the pay to contract input
|
161
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
162
|
+
# @param [Tapyrus::Tx] tx - The tx that has pay to contract input in the inputs list
|
163
|
+
# @param [Hash] utxo - The utxo that indicates pay to contract output to be signed
|
164
|
+
# @option utxo [String] txid - Transaction id
|
165
|
+
# @option utxo [Integer] vout - Output index
|
166
|
+
# @option utxo [Integer] amount - The amount the output has
|
167
|
+
# @option utxo [String] script_pubkey - The script_pubkey hex string
|
168
|
+
# @param [String] payment_base - The public key that is used to generate pay to contract public key
|
169
|
+
# @param [String] contents - The data to be used for generating pay-to-contract address
|
170
|
+
# @param [Integer] sighashtype - The sighash flag for each signature that would be produced here.
|
171
|
+
# @return [Tapyrus::Tx]
|
172
|
+
# @raise [Glueby::Internal::Wallet::Errors::InvalidSighashType] when the specified sighashtype is invalid
|
173
|
+
def sign_to_pay_to_contract_address(wallet_id, tx, utxo, payment_base, contents, sighashtype: Tapyrus::SIGHASH_TYPE[:all])
|
174
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
175
|
+
end
|
151
176
|
end
|
152
177
|
end
|
153
178
|
end
|
@@ -153,6 +153,51 @@ module Glueby
|
|
153
153
|
keys = keys.where(label: label) if label
|
154
154
|
keys.map(&:address)
|
155
155
|
end
|
156
|
+
|
157
|
+
def create_pay_to_contract_address(wallet_id, contents)
|
158
|
+
# Calculate P + H(P || contents)G
|
159
|
+
group = ECDSA::Group::Secp256k1
|
160
|
+
pubkey = create_pubkey(wallet_id)
|
161
|
+
p = pubkey.to_point # P
|
162
|
+
commitment = create_pay_to_contract_commitment(pubkey, contents)
|
163
|
+
point = p + group.generator.multiply_by_scalar(commitment) # P + H(P || contents)G
|
164
|
+
[Tapyrus::Key.new(pubkey: point.to_hex(true)).to_p2pkh, pubkey.pubkey] # [p2c address, P]
|
165
|
+
end
|
166
|
+
|
167
|
+
def sign_to_pay_to_contract_address(wallet_id, tx, utxo, payment_base, contents)
|
168
|
+
key = create_pay_to_contract_private_key(wallet_id, payment_base, contents)
|
169
|
+
sighash = tx.sighash_for_input(utxo[:vout], Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb))
|
170
|
+
|
171
|
+
sig = key.sign(sighash, algo: :schnorr) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C')
|
172
|
+
script_sig = Tapyrus::Script.parse_from_payload(Tapyrus::Script.pack_pushdata(sig) + Tapyrus::Script.pack_pushdata(key.pubkey.htb))
|
173
|
+
tx.inputs[utxo[:vout]].script_sig = script_sig
|
174
|
+
tx
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
# Calculate commitment = H(P || contents)
|
180
|
+
# @param [Tapyrus::Key] pubkey The public key
|
181
|
+
# @param [String] contents
|
182
|
+
# @return Integer
|
183
|
+
def create_pay_to_contract_commitment(pubkey, contents)
|
184
|
+
group = ECDSA::Group::Secp256k1
|
185
|
+
p = pubkey.to_point # P
|
186
|
+
Tapyrus.sha256(p.to_hex(true).htb + contents).bth.to_i(16) % group.order # H(P || contents)
|
187
|
+
end
|
188
|
+
|
189
|
+
# @param [String] wallet_id
|
190
|
+
# @param [String] payment_base The public key hex string
|
191
|
+
# @param [String] contents
|
192
|
+
# @return [Tapyrus::Key] pay to contract private key
|
193
|
+
def create_pay_to_contract_private_key(wallet_id, payment_base, contents)
|
194
|
+
group = ECDSA::Group::Secp256k1
|
195
|
+
wallet = AR::Wallet.find_by(wallet_id: wallet_id)
|
196
|
+
ar_key = wallet.keys.where(public_key: payment_base).first
|
197
|
+
key = Tapyrus::Key.new(pubkey: payment_base)
|
198
|
+
commitment = create_pay_to_contract_commitment(key, contents)
|
199
|
+
Tapyrus::Key.new(priv_key: ((ar_key.private_key.to_i(16) + commitment) % group.order).to_even_length_hex) # K + commitment
|
200
|
+
end
|
156
201
|
end
|
157
202
|
end
|
158
203
|
end
|
@@ -2,12 +2,12 @@ module Glueby
|
|
2
2
|
module Internal
|
3
3
|
class Wallet
|
4
4
|
module Errors
|
5
|
-
class ShouldInitializeWalletAdapter <
|
6
|
-
class WalletUnloaded <
|
7
|
-
class WalletAlreadyLoaded <
|
8
|
-
class WalletAlreadyCreated <
|
9
|
-
class WalletNotFound <
|
10
|
-
class InvalidSighashType <
|
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
|
@@ -154,6 +154,14 @@ module Glueby
|
|
154
154
|
wallet_adapter.get_addresses(id, label)
|
155
155
|
end
|
156
156
|
|
157
|
+
def create_pay_to_contract_address(contents)
|
158
|
+
wallet_adapter.create_pay_to_contract_address(id, contents)
|
159
|
+
end
|
160
|
+
|
161
|
+
def sign_to_pay_to_contract_address(tx, utxo, payment_base, contents)
|
162
|
+
wallet_adapter.sign_to_pay_to_contract_address(id, tx, utxo, payment_base, contents)
|
163
|
+
end
|
164
|
+
|
157
165
|
private
|
158
166
|
|
159
167
|
def wallet_adapter
|
@@ -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
|
data/lib/glueby/utxo_provider.rb
CHANGED
@@ -27,7 +27,7 @@ module Glueby
|
|
27
27
|
@fee_estimator = (UtxoProvider.config && UtxoProvider.config[:fee_estimator]) || Glueby::Contract::FixedFeeEstimator.new
|
28
28
|
end
|
29
29
|
|
30
|
-
attr_reader :wallet, :fee_estimator
|
30
|
+
attr_reader :wallet, :fee_estimator, :address
|
31
31
|
|
32
32
|
# Provide a UTXO
|
33
33
|
# @param [Tapyrus::Script] script_pubkey The script to be provided
|
@@ -78,6 +78,24 @@ module Glueby
|
|
78
78
|
)
|
79
79
|
end
|
80
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
|
+
|
81
99
|
private
|
82
100
|
|
83
101
|
# Create wallet for provider
|
data/lib/glueby/version.rb
CHANGED
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.
|
4
|
+
version: 0.10.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-
|
11
|
+
date: 2022-02-28 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.
|
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.
|
26
|
+
version: 0.2.13
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -113,6 +113,10 @@ files:
|
|
113
113
|
- lib/glueby/contract/payment.rb
|
114
114
|
- lib/glueby/contract/timestamp.rb
|
115
115
|
- lib/glueby/contract/timestamp/syncer.rb
|
116
|
+
- lib/glueby/contract/timestamp/tx_builder.rb
|
117
|
+
- lib/glueby/contract/timestamp/tx_builder/simple.rb
|
118
|
+
- lib/glueby/contract/timestamp/tx_builder/trackable.rb
|
119
|
+
- lib/glueby/contract/timestamp/tx_builder/updating_trackable.rb
|
116
120
|
- lib/glueby/contract/token.rb
|
117
121
|
- lib/glueby/contract/tx_builder.rb
|
118
122
|
- lib/glueby/fee_provider.rb
|