glueby 0.8.0 → 0.10.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/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
|