glueby 0.4.0 → 0.4.4

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