glueby 0.3.0 → 0.4.3

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.
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