glueby 0.2.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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