glueby 0.3.0 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +35 -0
  3. data/Gemfile +1 -0
  4. data/README.md +227 -16
  5. data/glueby.gemspec +3 -2
  6. data/lib/generators/glueby/contract/reissuable_token_generator.rb +26 -0
  7. data/lib/generators/glueby/contract/templates/initializer.rb.erb +7 -2
  8. data/lib/generators/glueby/contract/templates/key_table.rb.erb +4 -3
  9. data/lib/generators/glueby/contract/templates/reissuable_token_table.rb.erb +10 -0
  10. data/lib/generators/glueby/contract/templates/system_information_table.rb.erb +2 -2
  11. data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +1 -1
  12. data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +3 -2
  13. data/lib/generators/glueby/contract/templates/wallet_table.rb.erb +2 -2
  14. data/lib/glueby/block_syncer.rb +98 -0
  15. data/lib/glueby/configuration.rb +62 -0
  16. data/lib/glueby/contract/active_record/reissuable_token.rb +26 -0
  17. data/lib/glueby/contract/active_record.rb +1 -0
  18. data/lib/glueby/contract/fee_estimator.rb +38 -0
  19. data/lib/glueby/contract/payment.rb +4 -4
  20. data/lib/glueby/contract/timestamp/syncer.rb +13 -0
  21. data/lib/glueby/contract/timestamp.rb +8 -6
  22. data/lib/glueby/contract/token.rb +70 -22
  23. data/lib/glueby/contract/tx_builder.rb +22 -19
  24. data/lib/glueby/contract.rb +2 -2
  25. data/lib/glueby/fee_provider/tasks.rb +141 -0
  26. data/lib/glueby/fee_provider.rb +73 -0
  27. data/lib/glueby/generator/migrate_generator.rb +1 -1
  28. data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +25 -6
  29. data/lib/glueby/internal/wallet/active_record/utxo.rb +1 -0
  30. data/lib/glueby/internal/wallet/active_record/wallet.rb +15 -5
  31. data/lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb +14 -0
  32. data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +25 -8
  33. data/lib/glueby/internal/wallet/errors.rb +3 -0
  34. data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +42 -14
  35. data/lib/glueby/internal/wallet.rb +56 -13
  36. data/lib/glueby/railtie.rb +10 -0
  37. data/lib/glueby/version.rb +1 -1
  38. data/lib/glueby/wallet.rb +3 -2
  39. data/lib/glueby.rb +27 -12
  40. data/lib/tasks/glueby/block_syncer.rake +29 -0
  41. data/lib/tasks/glueby/contract/timestamp.rake +4 -26
  42. data/lib/tasks/glueby/fee_provider.rake +18 -0
  43. metadata +40 -16
  44. data/.travis.yml +0 -7
  45. data/lib/glueby/contract/fee_provider.rb +0 -21
  46. data/lib/tasks/glueby/contract/block_syncer.rake +0 -36
  47. data/lib/tasks/glueby/contract/wallet_adapter.rake +0 -42
@@ -1,15 +1,16 @@
1
1
  class CreateUtxo < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- create_table :utxos<%= table_options %> do |t|
3
+ create_table :glueby_utxos<%= table_options %> do |t|
4
4
  t.string :txid
5
5
  t.integer :index
6
6
  t.bigint :value
7
7
  t.string :script_pubkey
8
+ t.string :label, index: true
8
9
  t.integer :status
9
10
  t.belongs_to :key, null: true
10
11
  t.timestamps
11
12
  end
12
13
 
13
- add_index :utxos, [:txid, :index], unique: true
14
+ add_index :glueby_utxos, [:txid, :index], unique: true
14
15
  end
15
16
  end
@@ -1,10 +1,10 @@
1
1
  class CreateWallet < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- create_table :wallets<%= table_options %> do |t|
3
+ create_table :glueby_wallets<%= table_options %> do |t|
4
4
  t.string :wallet_id
5
5
  t.timestamps
6
6
  end
7
7
 
8
- add_index :wallets, [:wallet_id], unique: true
8
+ add_index :glueby_wallets, [:wallet_id], unique: true
9
9
  end
10
10
  end
@@ -0,0 +1,98 @@
1
+ module Glueby
2
+ # You can use BlockSyncer when you need to synchronize the state of
3
+ # an application with the state of a blockchain. When BlockSyncer
4
+ # detects the generation of a new block, it executes the registered
5
+ # syncer code on a block-by-block or transaction-by-transaction basis.
6
+ # By using this, an application can detect that the issued transaction
7
+ # has been captured in blocks, receive a new remittance, and so on.
8
+ #
9
+ # # Syncer logic registration
10
+ #
11
+ # For registration, create a class that implements the method that performs
12
+ # synchronization processing and registers it in BlockSyncer. Implement
13
+ # methods with the following name in that class.
14
+ #
15
+ # Method name | Arguments | Call conditions
16
+ # ------------------ | --------------------- | ------------------------------
17
+ # block_sync (block) | block: Tapyrus::Block | When a new block is created
18
+ # block_tx (tx) | tx: Tapyrus::Tx | When a new block is created, it is executed for each tx contained in that block.
19
+ #
20
+ # @example Register a synchronous logic
21
+ # class Syncer
22
+ # def block_sync (block)
23
+ # # sync a block
24
+ # end
25
+ #
26
+ # def tx_sync (tx)
27
+ # # sync a tx
28
+ # end
29
+ # end
30
+ # BlockSyncer.register_syncer(Syncer)
31
+ #
32
+ # @example Unregister the synchronous logic
33
+ # BlockSyncer.unregister_syncer(Syncer)
34
+ #
35
+ # # Run BlockSyncer
36
+ #
37
+ # Run the `glueby: block_syncer: start` rake task periodically with a program
38
+ # for periodic execution such as cron. If it detects the generation of a new
39
+ # block when it is executed, the synchronization process will be executed.
40
+ # Determine the execution interval according to the requirements of the application.
41
+ class BlockSyncer
42
+ # @!attribute [r] height
43
+ # @return [Integer] The block height to be synced
44
+ attr_reader :height
45
+
46
+ class << self
47
+ # @!attribute r syncers
48
+ # @return [Array<Class>] The syncer classes that is registered
49
+ attr_reader :syncers
50
+
51
+ # Register syncer class
52
+ # @param [Class] syncer The syncer to be registered.
53
+ def register_syncer(syncer)
54
+ @syncers ||= []
55
+ @syncers << syncer
56
+ end
57
+
58
+ # Unregister syncer class
59
+ # @param [Class] syncer The syncer to be unregistered.
60
+ def unregister_syncer(syncer)
61
+ @syncers ||= []
62
+ @syncers.delete(syncer)
63
+ end
64
+ end
65
+
66
+ # @param [Integer] height The block height to be synced in the instance
67
+ def initialize(height)
68
+ @height = height
69
+ end
70
+
71
+ # Run a block synchronization
72
+ def run
73
+ return if self.class.syncers.nil?
74
+
75
+ self.class.syncers.each do |syncer|
76
+ instance = syncer.new
77
+ instance.block_sync(block) if instance.respond_to?(:block_sync)
78
+
79
+ if instance.respond_to?(:tx_sync)
80
+ block.transactions.each { |tx| instance.tx_sync(tx) }
81
+ end
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def block
88
+ @block ||= begin
89
+ block = Glueby::Internal::RPC.client.getblock(block_hash, 0)
90
+ Tapyrus::Block.parse_from_payload(block.htb)
91
+ end
92
+ end
93
+
94
+ def block_hash
95
+ @block_hash ||= Glueby::Internal::RPC.client.getblockhash(height)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,62 @@
1
+ module Glueby
2
+ # Global configuration on runtime
3
+ #
4
+ # The global configuration treats configurations for all modules in Glueby.
5
+ #
6
+ # @example
7
+ # Glueby.configure do |config|
8
+ # config.wallet_adapter = :activerecord
9
+ # config.rpc_config = { schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass' }
10
+ # end
11
+ class Configuration
12
+
13
+ attr_reader :fee_provider_bears
14
+ alias_method :fee_provider_bears?, :fee_provider_bears
15
+
16
+ def initialize
17
+ @fee_provider_bears = false
18
+ end
19
+
20
+ # Specify wallet adapter.
21
+ # @param [Symbol] adapter - The adapter type :activerecord or :core is currently supported.
22
+ def wallet_adapter=(adapter)
23
+ case adapter
24
+ when :core
25
+ Glueby::Internal::Wallet.wallet_adapter = Glueby::Internal::Wallet::TapyrusCoreWalletAdapter.new
26
+ when :activerecord
27
+ Glueby::Internal::Wallet.wallet_adapter = Glueby::Internal::Wallet::ActiveRecordWalletAdapter.new
28
+ else
29
+ raise 'Not implemented'
30
+ end
31
+ end
32
+
33
+ # Specify connection information to Tapyrus Core RPC.
34
+ # @param [Hash] config
35
+ # @option config [String] :schema - http or https
36
+ # @option config [String] :host - The host of the RPC endpoint
37
+ # @option config [Integer] :port - The port of the RPC endpoint
38
+ # @Option config [String] :user - The user for Basic Authorization of the RPC endpoint
39
+ # @Option config [String] :password - The password for Basic Authorization of the RPC endpoint
40
+ def rpc_config=(config)
41
+ Glueby::Internal::RPC.configure(config)
42
+ end
43
+
44
+ # Use This to enable to use FeeProvider to supply inputs for fees on each transaction that is created on Glueby.
45
+ def fee_provider_bears!
46
+ @fee_provider_bears = true
47
+ end
48
+
49
+ # Use This to disable to use FeeProvider
50
+ def disable_fee_provider_bears!
51
+ @fee_provider_bears = false
52
+ end
53
+
54
+ # Specify FeeProvider configuration.
55
+ # @param [Hash] config
56
+ # @option config [Integer] :fixed_fee - The fee that Fee Provider pays on each transaction.
57
+ # @option config [Integer] :utxo_pool_size - Fee Provider tries to keep the number of utxo in utxo pool as this size using `glueby:fee_provider:manage_utxo_pool` rake task
58
+ def fee_provider_config=(config)
59
+ FeeProvider.configure(config)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,26 @@
1
+ module Glueby
2
+ module Contract
3
+ module AR
4
+ class ReissuableToken < ::ActiveRecord::Base
5
+
6
+ # Get the script_pubkey corresponding to the color_id in Tapyrus::Script format
7
+ # @param [String] color_id
8
+ # @return [Tapyrus::Script]
9
+ def self.script_pubkey(color_id)
10
+ script_pubkey = Glueby::Contract::AR::ReissuableToken.where(color_id: color_id).pluck(:script_pubkey).first
11
+ if script_pubkey
12
+ Tapyrus::Script.parse_from_payload(script_pubkey.htb)
13
+ end
14
+ end
15
+
16
+ # Check if the color_id is already stored
17
+ # @param [String] color_id
18
+ # @return [Boolean]
19
+ def self.saved?(color_id)
20
+ Glueby::Contract::AR::ReissuableToken.where(color_id: color_id).exists?
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -3,6 +3,7 @@ require 'active_record'
3
3
  module Glueby
4
4
  module Contract
5
5
  module AR
6
+ autoload :ReissuableToken, 'glueby/contract/active_record/reissuable_token'
6
7
  autoload :Timestamp, 'glueby/contract/active_record/timestamp'
7
8
  end
8
9
  end
@@ -0,0 +1,38 @@
1
+ module Glueby
2
+ module Contract
3
+ module FeeEstimator
4
+ # @param [Tapyrus::Tx] tx - The target tx
5
+ # @return fee by tapyrus(not TPC).
6
+ def fee(tx)
7
+ return 0 if Glueby.configuration.fee_provider_bears?
8
+ estimate_fee(tx)
9
+ end
10
+
11
+ private
12
+
13
+ # @private
14
+ # @abstract Override in subclasses. This is would be implemented an actual estimation logic.
15
+ # @param [Tapyrus::Tx] tx - The target tx
16
+ # @return fee by tapyrus(not TPC).
17
+ def estimate_fee(tx)
18
+ raise NotImplementedError
19
+ end
20
+ end
21
+
22
+ class FixedFeeEstimator
23
+ include FeeEstimator
24
+
25
+ def initialize(fixed_fee: 10_000)
26
+ @fixed_fee = fixed_fee
27
+ end
28
+
29
+ private
30
+
31
+ # @private
32
+ # @return fee by tapyrus(not TPC).
33
+ def estimate_fee(_tx)
34
+ @fixed_fee
35
+ end
36
+ end
37
+ end
38
+ end
@@ -32,11 +32,11 @@ module Glueby
32
32
  extend Glueby::Contract::TxBuilder
33
33
 
34
34
  class << self
35
- def transfer(sender:, receiver_address:, amount:, fee_provider: FixedFeeProvider.new)
35
+ def transfer(sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
36
36
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
37
37
 
38
38
  tx = Tapyrus::Tx.new
39
- dummy_fee = fee_provider.fee(dummy_tx(tx))
39
+ dummy_fee = fee_estimator.fee(dummy_tx(tx))
40
40
 
41
41
  sum, outputs = sender.internal_wallet.collect_uncolored_outputs(dummy_fee + amount)
42
42
  fill_input(tx, outputs)
@@ -44,13 +44,13 @@ module Glueby
44
44
  receiver_script = Tapyrus::Script.parse_from_addr(receiver_address)
45
45
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_script)
46
46
 
47
- fee = fee_provider.fee(tx)
47
+ fee = fee_estimator.fee(tx)
48
48
 
49
49
  fill_change_tpc(tx, sender, sum - fee - amount)
50
50
 
51
51
  tx = sender.internal_wallet.sign_tx(tx)
52
52
 
53
- Glueby::Internal::RPC.client.sendrawtransaction(tx.to_hex)
53
+ sender.internal_wallet.broadcast(tx)
54
54
  end
55
55
  end
56
56
  end
@@ -0,0 +1,13 @@
1
+ module Glueby
2
+ module Contract
3
+ class Timestamp
4
+ class Syncer
5
+ def block_sync(block)
6
+ Glueby::Contract::AR::Timestamp
7
+ .where(txid: block.transactions.map(&:txid), status: :unconfirmed)
8
+ .update_all(status: :confirmed)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -10,15 +10,17 @@ module Glueby
10
10
  class Timestamp
11
11
  include Glueby::Contract::TxBuilder
12
12
 
13
+ autoload :Syncer, 'glueby/contract/timestamp/syncer'
14
+
13
15
  module Util
14
16
  include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
15
17
  module_function
16
18
 
17
- def create_tx(wallet, prefix, data, fee_provider)
19
+ def create_tx(wallet, prefix, data, fee_estimator)
18
20
  tx = Tapyrus::Tx.new
19
21
  tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: create_script(prefix, data))
20
22
 
21
- fee = fee_provider.fee(dummy_tx(tx))
23
+ fee = fee_estimator.fee(dummy_tx(tx))
22
24
  sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee)
23
25
  fill_input(tx, outputs)
24
26
 
@@ -50,7 +52,7 @@ module Glueby
50
52
 
51
53
  # @param [String] content Data to be hashed and stored in blockchain.
52
54
  # @param [String] prefix prefix of op_return data
53
- # @param [Glueby::Contract::FeeProvider] fee_provider
55
+ # @param [Glueby::Contract::FeeEstimator] fee_estimator
54
56
  # @param [Symbol] digest type which select of:
55
57
  # - :sha256
56
58
  # - :double_sha256
@@ -60,13 +62,13 @@ module Glueby
60
62
  wallet:,
61
63
  content:,
62
64
  prefix: '',
63
- fee_provider: Glueby::Contract::FixedFeeProvider.new,
65
+ fee_estimator: Glueby::Contract::FixedFeeEstimator.new,
64
66
  digest: :sha256
65
67
  )
66
68
  @wallet = wallet
67
69
  @content = content
68
70
  @prefix = prefix
69
- @fee_provider = fee_provider
71
+ @fee_estimator = fee_estimator
70
72
  @digest = digest
71
73
  end
72
74
 
@@ -77,7 +79,7 @@ module Glueby
77
79
  def save!
78
80
  raise Glueby::Contract::Errors::TxAlreadyBroadcasted if @txid
79
81
 
80
- @tx = create_tx(@wallet, @prefix, digest_content, @fee_provider)
82
+ @tx = create_tx(@wallet, @prefix, digest_content, @fee_estimator)
81
83
  @txid = @wallet.internal_wallet.broadcast(@tx)
82
84
  end
83
85
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'active_record'
2
3
 
3
4
  module Glueby
4
5
  module Contract
@@ -55,14 +56,14 @@ module Glueby
55
56
  # @param issuer [Glueby::Wallet]
56
57
  # @param token_type [TokenTypes]
57
58
  # @param amount [Integer]
58
- # @return [Token] token
59
+ # @return [Array<token, Array<tx>>] Tuple of tx array and token object
59
60
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
60
61
  # @raise [InvalidAmount] if amount is not positive integer.
61
62
  # @raise [UnspportedTokenType] if token is not supported.
62
63
  def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1)
63
64
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
64
65
 
65
- txs, color_id, script_pubkey = case token_type
66
+ txs, color_id = case token_type
66
67
  when Tapyrus::Color::TokenTypes::REISSUABLE
67
68
  issue_reissuable_token(issuer: issuer, amount: amount)
68
69
  when Tapyrus::Color::TokenTypes::NON_REISSUABLE
@@ -72,33 +73,44 @@ module Glueby
72
73
  else
73
74
  raise Glueby::Contract::Errors::UnsupportedTokenType
74
75
  end
75
- txs.each { |tx| issuer.internal_wallet.broadcast(tx) }
76
- new(color_id: color_id, script_pubkey: script_pubkey)
76
+
77
+ [new(color_id: color_id), txs]
77
78
  end
78
79
 
79
80
  private
80
81
 
81
82
  def issue_reissuable_token(issuer:, amount:)
82
- estimated_fee = FixedFeeProvider.new.fee(Tapyrus::Tx.new)
83
- funding_tx = create_funding_tx(wallet: issuer, amount: estimated_fee)
84
- tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
83
+ funding_tx = create_funding_tx(wallet: issuer)
85
84
  script_pubkey = funding_tx.outputs.first.script_pubkey
86
85
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
87
- [[funding_tx, tx], color_id, script_pubkey]
86
+
87
+ ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
88
+ # Store the script_pubkey for reissue the token.
89
+ Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
90
+
91
+ funding_tx = issuer.internal_wallet.broadcast(funding_tx)
92
+ tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
93
+ tx = issuer.internal_wallet.broadcast(tx)
94
+ [[funding_tx, tx], color_id]
95
+ end
88
96
  end
89
97
 
90
98
  def issue_non_reissuable_token(issuer:, amount:)
91
99
  tx = create_issue_tx_for_non_reissuable_token(issuer: issuer, amount: amount)
100
+ tx = issuer.internal_wallet.broadcast(tx)
101
+
92
102
  out_point = tx.inputs.first.out_point
93
103
  color_id = Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
94
- [[tx], color_id, nil]
104
+ [[tx], color_id]
95
105
  end
96
106
 
97
107
  def issue_nft_token(issuer:)
98
108
  tx = create_issue_tx_for_nft_token(issuer: issuer)
109
+ tx = issuer.internal_wallet.broadcast(tx)
110
+
99
111
  out_point = tx.inputs.first.out_point
100
112
  color_id = Tapyrus::Color::ColorIdentifier.nft(out_point)
101
- [[tx], color_id, nil]
113
+ [[tx], color_id]
102
114
  end
103
115
  end
104
116
 
@@ -108,6 +120,7 @@ module Glueby
108
120
  # A wallet can issue the token only when it is REISSUABLE token.
109
121
  # @param issuer [Glueby::Wallet]
110
122
  # @param amount [Integer]
123
+ # @return [Array<String, tx>] Tuple of color_id and tx object
111
124
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
112
125
  # @raise [InvalidAmount] if amount is not positive integer.
113
126
  # @raise [InvalidTokenType] if token is not reissuable.
@@ -115,12 +128,17 @@ module Glueby
115
128
  def reissue!(issuer:, amount:)
116
129
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
117
130
  raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE
118
- raise Glueby::Contract::Errors::UnknownScriptPubkey unless @script_pubkey
119
131
 
120
- estimated_fee = FixedFeeProvider.new.fee(Tapyrus::Tx.new)
121
- funding_tx = create_funding_tx(wallet: issuer, amount: estimated_fee, script: @script_pubkey)
122
- tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id)
123
- [funding_tx, tx].each { |tx| issuer.internal_wallet.broadcast(tx) }
132
+ if validate_reissuer(wallet: issuer)
133
+ funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey)
134
+ funding_tx = issuer.internal_wallet.broadcast(funding_tx)
135
+ tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id)
136
+ tx = issuer.internal_wallet.broadcast(tx)
137
+
138
+ [color_id, tx]
139
+ else
140
+ raise Glueby::Contract::Errors::UnknownScriptPubkey
141
+ end
124
142
  end
125
143
 
126
144
  # Send the token to other wallet
@@ -128,7 +146,7 @@ module Glueby
128
146
  # @param sender [Glueby::Wallet] wallet to send this token
129
147
  # @param receiver_address [String] address to receive this token
130
148
  # @param amount [Integer]
131
- # @return [Token] receiver token
149
+ # @return [Array<String, tx>] Tuple of color_id and tx object
132
150
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
133
151
  # @raise [InsufficientTokens] if wallet does not have enough token to send.
134
152
  # @raise [InvalidAmount] if amount is not positive integer.
@@ -137,6 +155,7 @@ module Glueby
137
155
 
138
156
  tx = create_transfer_tx(color_id: color_id, sender: sender, receiver_address: receiver_address, amount: amount)
139
157
  sender.internal_wallet.broadcast(tx)
158
+ [color_id, tx]
140
159
  end
141
160
 
142
161
  # Burn token
@@ -170,12 +189,19 @@ module Glueby
170
189
  color_id.type
171
190
  end
172
191
 
192
+ # Return the script_pubkey of the token from ActiveRecord
193
+ # @return [String] script_pubkey
194
+ def script_pubkey
195
+ @script_pubkey ||= Glueby::Contract::AR::ReissuableToken.script_pubkey(@color_id.to_hex)
196
+ end
197
+
173
198
  # Return serialized payload
174
199
  # @return [String] payload
175
200
  def to_payload
176
201
  payload = +''
177
202
  payload << @color_id.to_payload
178
- payload << @script_pubkey.to_payload if @script_pubkey
203
+ payload << @script_pubkey.to_payload if script_pubkey
204
+ payload
179
205
  end
180
206
 
181
207
  # Restore token from payload
@@ -184,14 +210,36 @@ module Glueby
184
210
  def self.parse_from_payload(payload)
185
211
  color_id, script_pubkey = payload.unpack('a33a*')
186
212
  color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(color_id)
187
- script_pubkey = Tapyrus::Script.parse_from_payload(script_pubkey) if script_pubkey
188
- new(color_id: color_id, script_pubkey: script_pubkey)
213
+ if color_id.type == Tapyrus::Color::TokenTypes::REISSUABLE
214
+ raise ArgumentError, 'script_pubkey should not be empty' if script_pubkey.empty?
215
+ script_pubkey = Tapyrus::Script.parse_from_payload(script_pubkey)
216
+ Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
217
+ end
218
+ new(color_id: color_id)
189
219
  end
190
220
 
191
- def initialize(color_id:, script_pubkey:nil)
221
+ # Generate Token Instance
222
+ # @param color_id [String]
223
+ def initialize(color_id:)
192
224
  @color_id = color_id
193
- @script_pubkey = script_pubkey
225
+ end
226
+
227
+ private
228
+
229
+ # Verify that wallet is the issuer of the reissuable token
230
+ # reutrn [Boolean]
231
+ def validate_reissuer(wallet:)
232
+ addresses = wallet.internal_wallet.get_addresses
233
+ addresses.each do |address|
234
+ decoded_address = Tapyrus.decode_base58_address(address)
235
+ pubkey_hash_from_address = decoded_address[0]
236
+ pubkey_hash_from_script = Tapyrus::Script.parse_from_payload(script_pubkey.chunks[2])
237
+ if pubkey_hash_from_address == pubkey_hash_from_script.to_s
238
+ return true
239
+ end
240
+ end
241
+ false
194
242
  end
195
243
  end
196
244
  end
197
- end
245
+ end