glueby 0.2.0 → 0.4.2

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +35 -0
  3. data/Gemfile +1 -0
  4. data/README.md +224 -16
  5. data/glueby.gemspec +2 -2
  6. data/lib/generators/glueby/contract/block_syncer_generator.rb +26 -0
  7. data/lib/generators/glueby/contract/reissuable_token_generator.rb +26 -0
  8. data/lib/generators/glueby/contract/templates/initializer.rb.erb +4 -2
  9. data/lib/generators/glueby/contract/templates/key_table.rb.erb +4 -3
  10. data/lib/generators/glueby/contract/templates/reissuable_token_table.rb.erb +10 -0
  11. data/lib/generators/glueby/contract/templates/system_information_table.rb.erb +12 -0
  12. data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +1 -1
  13. data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +3 -2
  14. data/lib/generators/glueby/contract/templates/wallet_table.rb.erb +2 -2
  15. data/lib/glueby.rb +28 -11
  16. data/lib/glueby/active_record.rb +8 -0
  17. data/lib/glueby/active_record/system_information.rb +15 -0
  18. data/lib/glueby/block_syncer.rb +98 -0
  19. data/lib/glueby/configuration.rb +62 -0
  20. data/lib/glueby/contract.rb +2 -2
  21. data/lib/glueby/contract/active_record.rb +1 -0
  22. data/lib/glueby/contract/active_record/reissuable_token.rb +26 -0
  23. data/lib/glueby/contract/fee_estimator.rb +38 -0
  24. data/lib/glueby/contract/payment.rb +10 -6
  25. data/lib/glueby/contract/timestamp.rb +8 -6
  26. data/lib/glueby/contract/timestamp/syncer.rb +13 -0
  27. data/lib/glueby/contract/token.rb +78 -26
  28. data/lib/glueby/contract/tx_builder.rb +23 -20
  29. data/lib/glueby/fee_provider.rb +73 -0
  30. data/lib/glueby/fee_provider/tasks.rb +141 -0
  31. data/lib/glueby/generator/migrate_generator.rb +1 -1
  32. data/lib/glueby/internal/wallet.rb +56 -13
  33. data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +25 -6
  34. data/lib/glueby/internal/wallet/active_record/utxo.rb +1 -0
  35. data/lib/glueby/internal/wallet/active_record/wallet.rb +15 -5
  36. data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +25 -8
  37. data/lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb +14 -0
  38. data/lib/glueby/internal/wallet/errors.rb +3 -0
  39. data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +42 -14
  40. data/lib/glueby/railtie.rb +14 -0
  41. data/lib/glueby/version.rb +1 -1
  42. data/lib/glueby/wallet.rb +3 -2
  43. data/lib/tasks/glueby/block_syncer.rake +29 -0
  44. data/lib/tasks/glueby/contract/timestamp.rake +4 -26
  45. data/lib/tasks/glueby/fee_provider.rake +18 -0
  46. metadata +26 -11
  47. data/.travis.yml +0 -7
  48. data/lib/glueby/contract/fee_provider.rb +0 -21
  49. data/lib/tasks/glueby/contract/wallet_adapter.rake +0 -42
@@ -1,6 +1,6 @@
1
1
  class CreateTimestamp < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- create_table :timestamps<%= table_options %> do |t|
3
+ create_table :glueby_timestamps<%= table_options %> do |t|
4
4
  t.string :txid
5
5
  t.integer :status
6
6
  t.string :content_hash
@@ -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
data/lib/glueby.rb CHANGED
@@ -6,17 +6,34 @@ module Glueby
6
6
  autoload :Generator, 'glueby/generator'
7
7
  autoload :Wallet, 'glueby/wallet'
8
8
  autoload :Internal, 'glueby/internal'
9
+ autoload :AR, 'glueby/active_record'
10
+ autoload :FeeProvider, 'glueby/fee_provider'
11
+ autoload :Configuration, 'glueby/configuration'
12
+ autoload :BlockSyncer, 'glueby/block_syncer'
9
13
 
10
- begin
11
- class Railtie < ::Rails::Railtie
12
- rake_tasks do
13
- load "tasks/glueby/contract.rake"
14
- load "tasks/glueby/contract/timestamp.rake"
15
- load "tasks/glueby/contract/wallet_adapter.rake"
16
- end
17
- end
18
- rescue
19
- # Rake task is unavailable
20
- puts "Rake task is unavailable"
14
+ if defined? ::Rails::Railtie
15
+ require 'glueby/railtie'
16
+ end
17
+
18
+ # Add prefix to activerecord table names
19
+ def self.table_name_prefix
20
+ 'glueby_'
21
+ end
22
+
23
+ # Returns the global [Configuration](RSpec/Core/Configuration) object.
24
+ def self.configuration
25
+ @configuration ||= Glueby::Configuration.new
26
+ end
27
+
28
+ # Yields the global configuration to a block.
29
+ # @yield [Configuration] global configuration
30
+ #
31
+ # @example
32
+ # Glueby.configure do |config|
33
+ # config.wallet_adapter = :activerecord
34
+ # config.rpc_config = { schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass' }
35
+ # end
36
+ def self.configure
37
+ yield configuration if block_given?
21
38
  end
22
39
  end
@@ -0,0 +1,8 @@
1
+
2
+ require 'active_record'
3
+
4
+ module Glueby
5
+ module AR
6
+ autoload :SystemInformation, 'glueby/active_record/system_information'
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module Glueby
2
+ module AR
3
+ class SystemInformation < ::ActiveRecord::Base
4
+
5
+ def self.synced_block_height
6
+ SystemInformation.find_by(info_key: "synced_block_number")
7
+ end
8
+
9
+ def int_value
10
+ info_value.to_i
11
+ end
12
+
13
+ end
14
+ end
15
+ 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
@@ -1,8 +1,8 @@
1
1
  module Glueby
2
2
  module Contract
3
3
  autoload :Errors, 'glueby/contract/errors'
4
- autoload :FeeProvider, 'glueby/contract/fee_provider'
5
- autoload :FixedFeeProvider, 'glueby/contract/fee_provider'
4
+ autoload :FeeEstimator, 'glueby/contract/fee_estimator'
5
+ autoload :FixedFeeEstimator, 'glueby/contract/fee_estimator'
6
6
  autoload :Payment, 'glueby/contract/payment'
7
7
  autoload :Timestamp, 'glueby/contract/timestamp'
8
8
  autoload :Token, 'glueby/contract/token'
@@ -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,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
@@ -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
@@ -11,6 +11,10 @@ module Glueby
11
11
  # or
12
12
  # Glueby::Wallet.create
13
13
  #
14
+ # Use `Glueby::Internal::Wallet#receive_address` to generate the address of a receiver
15
+ # receiver.internal_wallet.receive_address
16
+ # => '1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a'
17
+ #
14
18
  # Balance of sender and receiver before send
15
19
  # sender.balances[""]
16
20
  # => 100_000(tapyrus)
@@ -18,7 +22,7 @@ module Glueby
18
22
  # => 0(tapyrus)
19
23
  #
20
24
  # Send
21
- # Payment.transfer(sender: sender, receiver: receiver, amount: 10_000)
25
+ # Payment.transfer(sender: sender, receiver_address: '1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a', amount: 10_000)
22
26
  # sender.balances[""]
23
27
  # => 90_000
24
28
  # receiver.balances[""]
@@ -28,25 +32,25 @@ module Glueby
28
32
  extend Glueby::Contract::TxBuilder
29
33
 
30
34
  class << self
31
- def transfer(sender:, receiver:, amount:, fee_provider: FixedFeeProvider.new)
35
+ def transfer(sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
32
36
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
33
37
 
34
38
  tx = Tapyrus::Tx.new
35
- dummy_fee = fee_provider.fee(dummy_tx(tx))
39
+ dummy_fee = fee_estimator.fee(dummy_tx(tx))
36
40
 
37
41
  sum, outputs = sender.internal_wallet.collect_uncolored_outputs(dummy_fee + amount)
38
42
  fill_input(tx, outputs)
39
43
 
40
- receiver_script = Tapyrus::Script.parse_from_addr(receiver.internal_wallet.receive_address)
44
+ receiver_script = Tapyrus::Script.parse_from_addr(receiver_address)
41
45
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_script)
42
46
 
43
- fee = fee_provider.fee(tx)
47
+ fee = fee_estimator.fee(tx)
44
48
 
45
49
  fill_change_tpc(tx, sender, sum - fee - amount)
46
50
 
47
51
  tx = sender.internal_wallet.sign_tx(tx)
48
52
 
49
- Glueby::Internal::RPC.client.sendrawtransaction(tx.to_hex)
53
+ sender.internal_wallet.broadcast(tx)
50
54
  end
51
55
  end
52
56
  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