glueby 0.4.0 → 0.4.4

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.
data/glueby.gemspec CHANGED
@@ -1,32 +1,33 @@
1
- require_relative 'lib/glueby/version'
2
-
3
- Gem::Specification.new do |spec|
4
- spec.name = "glueby"
5
- spec.version = Glueby::VERSION
6
- spec.authors = ["azuchi"]
7
- spec.email = ["azuchi@chaintope.com"]
8
-
9
- spec.summary = %q{A Ruby library of smart contracts that can be used on Tapyrus.}
10
- spec.description = %q{A Ruby library of smart contracts that can be used on Tapyrus.}
11
- spec.homepage = "https://github.com/chaintope/glueby"
12
- spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
-
15
-
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = "https://github.com/chaintope/glueby"
18
- spec.metadata["changelog_uri"] = "https://github.com/chaintope/glueby"
19
-
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
- end
25
- spec.bindir = "exe"
26
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
- spec.require_paths = ["lib"]
28
-
29
- spec.add_runtime_dependency 'tapyrus', '>= 0.2.9'
30
- spec.add_development_dependency 'activerecord'
31
- spec.add_development_dependency 'sqlite3'
32
- end
1
+ require_relative 'lib/glueby/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "glueby"
5
+ spec.version = Glueby::VERSION
6
+ spec.authors = ["azuchi"]
7
+ spec.email = ["azuchi@chaintope.com"]
8
+
9
+ spec.summary = %q{A Ruby library of smart contracts that can be used on Tapyrus.}
10
+ spec.description = %q{A Ruby library of smart contracts that can be used on Tapyrus.}
11
+ spec.homepage = "https://github.com/chaintope/glueby"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/chaintope/glueby"
18
+ spec.metadata["changelog_uri"] = "https://github.com/chaintope/glueby"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_runtime_dependency 'tapyrus', '>= 0.2.9'
30
+ spec.add_runtime_dependency 'activerecord', '~> 6.1.3'
31
+ spec.add_development_dependency 'sqlite3'
32
+ spec.add_development_dependency 'rails', '~> 6.1.3'
33
+ end
@@ -1,3 +1,8 @@
1
- # Edit configuration for connection to tapyrus core
2
- config = {adapter: 'core', schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass'}
3
- Glueby::Wallet.configure(config)
1
+ # Edit configuration for connection to tapyrus core
2
+ Glueby.configure do |config|
3
+ config.wallet_adapter = :activerecord
4
+ config.rpc_config = { schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass' }
5
+ end
6
+
7
+ # Uncomment next line when using timestamp feature
8
+ # Glueby::BlockSyncer.register_syncer(Glueby::Contract::Timestamp::Syncer)
@@ -1,15 +1,16 @@
1
- class CreateKey < ActiveRecord::Migration<%= migration_version %>
2
- def change
3
- create_table :glueby_keys<%= table_options %> do |t|
4
- t.string :private_key
5
- t.string :public_key
6
- t.string :script_pubkey
7
- t.integer :purpose
8
- t.belongs_to :wallet
9
- t.timestamps
10
- end
11
-
12
- add_index :glueby_keys, [:script_pubkey], unique: true
13
- add_index :glueby_keys, [:private_key], unique: true
14
- end
15
- end
1
+ class CreateKey < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :glueby_keys<%= table_options %> do |t|
4
+ t.string :private_key
5
+ t.string :public_key
6
+ t.string :script_pubkey
7
+ t.string :label, index: true
8
+ t.integer :purpose
9
+ t.belongs_to :wallet
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :glueby_keys, [:script_pubkey], unique: true
14
+ add_index :glueby_keys, [:private_key], unique: true
15
+ end
16
+ end
@@ -1,15 +1,16 @@
1
- class CreateUtxo < ActiveRecord::Migration<%= migration_version %>
2
- def change
3
- create_table :glueby_utxos<%= table_options %> do |t|
4
- t.string :txid
5
- t.integer :index
6
- t.bigint :value
7
- t.string :script_pubkey
8
- t.integer :status
9
- t.belongs_to :key, null: true
10
- t.timestamps
11
- end
12
-
13
- add_index :glueby_utxos, [:txid, :index], unique: true
14
- end
15
- end
1
+ class CreateUtxo < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :glueby_utxos<%= table_options %> do |t|
4
+ t.string :txid
5
+ t.integer :index
6
+ t.bigint :value
7
+ t.string :script_pubkey
8
+ t.string :label, index: true
9
+ t.integer :status
10
+ t.belongs_to :key, null: true
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :glueby_utxos, [:txid, :index], unique: true
15
+ end
16
+ 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,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
@@ -1,100 +1,102 @@
1
- module Glueby
2
- module Contract
3
- # Timestamp feature allows users to send transaction with op_return output which has sha256 hash of arbitary data.
4
- # Timestamp transaction has
5
- # * 1 or more inputs enough to afford transaction fee.
6
- # * 1 output which has op_return, application specific prefix, and sha256 hash of data.
7
- # * 1 output to send the change TPC back to the wallet.
8
- #
9
- # Storing timestamp transaction to the blockchain enables everyone to verify that the data existed at that time and a user signed it.
10
- class Timestamp
11
- include Glueby::Contract::TxBuilder
12
-
13
- module Util
14
- include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
15
- module_function
16
-
17
- def create_tx(wallet, prefix, data, fee_estimator)
18
- tx = Tapyrus::Tx.new
19
- tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: create_script(prefix, data))
20
-
21
- fee = fee_estimator.fee(dummy_tx(tx))
22
- sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee)
23
- fill_input(tx, outputs)
24
-
25
- fill_change_tpc(tx, wallet, sum - fee)
26
- wallet.internal_wallet.sign_tx(tx)
27
- end
28
-
29
- def create_payload(prefix, data)
30
- payload = +''
31
- payload << prefix
32
- payload << data
33
- payload
34
- end
35
-
36
- def create_script(prefix, data)
37
- script = Tapyrus::Script.new
38
- script << Tapyrus::Script::OP_RETURN
39
- script << create_payload(prefix, data)
40
- script
41
- end
42
-
43
- def get_transaction(tx)
44
- Glueby::Internal::RPC.client.getrawtransaction(tx.txid, 1)
45
- end
46
- end
47
- include Glueby::Contract::Timestamp::Util
48
-
49
- attr_reader :tx, :txid
50
-
51
- # @param [String] content Data to be hashed and stored in blockchain.
52
- # @param [String] prefix prefix of op_return data
53
- # @param [Glueby::Contract::FeeEstimator] fee_estimator
54
- # @param [Symbol] digest type which select of:
55
- # - :sha256
56
- # - :double_sha256
57
- # - :none
58
- # @raise [Glueby::Contract::Errors::UnsupportedDigestType] if digest unsupport
59
- def initialize(
60
- wallet:,
61
- content:,
62
- prefix: '',
63
- fee_estimator: Glueby::Contract::FixedFeeEstimator.new,
64
- digest: :sha256
65
- )
66
- @wallet = wallet
67
- @content = content
68
- @prefix = prefix
69
- @fee_estimator = fee_estimator
70
- @digest = digest
71
- end
72
-
73
- # broadcast to Tapyrus Core
74
- # @return [String] txid
75
- # @raise [TxAlreadyBroadcasted] if tx has been broadcasted.
76
- # @raise [InsufficientFunds] if result of listunspent is not enough to pay the specified amount
77
- def save!
78
- raise Glueby::Contract::Errors::TxAlreadyBroadcasted if @txid
79
-
80
- @tx = create_tx(@wallet, @prefix, digest_content, @fee_estimator)
81
- @txid = @wallet.internal_wallet.broadcast(@tx)
82
- end
83
-
84
- private
85
-
86
- def digest_content
87
- case @digest&.downcase
88
- when :sha256
89
- Tapyrus.sha256(@content)
90
- when :double_sha256
91
- Tapyrus.double_sha256(@content)
92
- when :none
93
- @content
94
- else
95
- raise Glueby::Contract::Errors::UnsupportedDigestType
96
- end
97
- end
98
- end
99
- end
100
- end
1
+ module Glueby
2
+ module Contract
3
+ # Timestamp feature allows users to send transaction with op_return output which has sha256 hash of arbitary data.
4
+ # Timestamp transaction has
5
+ # * 1 or more inputs enough to afford transaction fee.
6
+ # * 1 output which has op_return, application specific prefix, and sha256 hash of data.
7
+ # * 1 output to send the change TPC back to the wallet.
8
+ #
9
+ # Storing timestamp transaction to the blockchain enables everyone to verify that the data existed at that time and a user signed it.
10
+ class Timestamp
11
+ include Glueby::Contract::TxBuilder
12
+
13
+ autoload :Syncer, 'glueby/contract/timestamp/syncer'
14
+
15
+ module Util
16
+ include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
17
+ module_function
18
+
19
+ def create_tx(wallet, prefix, data, fee_estimator)
20
+ tx = Tapyrus::Tx.new
21
+ tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: create_script(prefix, data))
22
+
23
+ fee = fee_estimator.fee(dummy_tx(tx))
24
+ sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee)
25
+ fill_input(tx, outputs)
26
+
27
+ fill_change_tpc(tx, wallet, sum - fee)
28
+ wallet.internal_wallet.sign_tx(tx)
29
+ end
30
+
31
+ def create_payload(prefix, data)
32
+ payload = +''
33
+ payload << prefix
34
+ payload << data
35
+ payload
36
+ end
37
+
38
+ def create_script(prefix, data)
39
+ script = Tapyrus::Script.new
40
+ script << Tapyrus::Script::OP_RETURN
41
+ script << create_payload(prefix, data)
42
+ script
43
+ end
44
+
45
+ def get_transaction(tx)
46
+ Glueby::Internal::RPC.client.getrawtransaction(tx.txid, 1)
47
+ end
48
+ end
49
+ include Glueby::Contract::Timestamp::Util
50
+
51
+ attr_reader :tx, :txid
52
+
53
+ # @param [String] content Data to be hashed and stored in blockchain.
54
+ # @param [String] prefix prefix of op_return data
55
+ # @param [Glueby::Contract::FeeEstimator] fee_estimator
56
+ # @param [Symbol] digest type which select of:
57
+ # - :sha256
58
+ # - :double_sha256
59
+ # - :none
60
+ # @raise [Glueby::Contract::Errors::UnsupportedDigestType] if digest unsupport
61
+ def initialize(
62
+ wallet:,
63
+ content:,
64
+ prefix: '',
65
+ fee_estimator: Glueby::Contract::FixedFeeEstimator.new,
66
+ digest: :sha256
67
+ )
68
+ @wallet = wallet
69
+ @content = content
70
+ @prefix = prefix
71
+ @fee_estimator = fee_estimator
72
+ @digest = digest
73
+ end
74
+
75
+ # broadcast to Tapyrus Core
76
+ # @return [String] txid
77
+ # @raise [TxAlreadyBroadcasted] if tx has been broadcasted.
78
+ # @raise [InsufficientFunds] if result of listunspent is not enough to pay the specified amount
79
+ def save!
80
+ raise Glueby::Contract::Errors::TxAlreadyBroadcasted if @txid
81
+
82
+ @tx = create_tx(@wallet, @prefix, digest_content, @fee_estimator)
83
+ @txid = @wallet.internal_wallet.broadcast(@tx)
84
+ end
85
+
86
+ private
87
+
88
+ def digest_content
89
+ case @digest&.downcase
90
+ when :sha256
91
+ Tapyrus.sha256(@content)
92
+ when :double_sha256
93
+ Tapyrus.double_sha256(@content)
94
+ when :none
95
+ @content
96
+ else
97
+ raise Glueby::Contract::Errors::UnsupportedDigestType
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end