glueby 0.4.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +477 -387
  3. data/glueby.gemspec +33 -33
  4. data/lib/generators/glueby/contract/templates/initializer.rb.erb +8 -8
  5. data/lib/generators/glueby/contract/templates/key_table.rb.erb +16 -16
  6. data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +4 -1
  7. data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +16 -16
  8. data/lib/glueby/block_syncer.rb +97 -97
  9. data/lib/glueby/configuration.rb +32 -2
  10. data/lib/glueby/contract/active_record/timestamp.rb +3 -1
  11. data/lib/glueby/contract/errors.rb +1 -0
  12. data/lib/glueby/contract/fee_estimator.rb +5 -1
  13. data/lib/glueby/contract/timestamp/syncer.rb +13 -13
  14. data/lib/glueby/contract/timestamp.rb +153 -102
  15. data/lib/glueby/contract/token.rb +270 -244
  16. data/lib/glueby/contract/tx_builder.rb +97 -30
  17. data/lib/glueby/fee_provider/tasks.rb +140 -140
  18. data/lib/glueby/fee_provider.rb +11 -0
  19. data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +153 -151
  20. data/lib/glueby/internal/wallet/active_record/utxo.rb +51 -51
  21. data/lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb +13 -13
  22. data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +159 -151
  23. data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +194 -186
  24. data/lib/glueby/internal/wallet.rb +164 -162
  25. data/lib/glueby/railtie.rb +10 -9
  26. data/lib/glueby/utxo_provider/tasks.rb +135 -0
  27. data/lib/glueby/utxo_provider.rb +85 -0
  28. data/lib/glueby/version.rb +3 -3
  29. data/lib/glueby.rb +40 -39
  30. data/lib/tasks/glueby/block_syncer.rake +28 -28
  31. data/lib/tasks/glueby/contract/timestamp.rake +46 -39
  32. data/lib/tasks/glueby/fee_provider.rake +18 -18
  33. data/lib/tasks/glueby/utxo_provider.rake +18 -0
  34. metadata +5 -2
@@ -1,102 +1,153 @@
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
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
+ P2C_DEFAULT_VALUE = 1_000
14
+
15
+ autoload :Syncer, 'glueby/contract/timestamp/syncer'
16
+
17
+ module Util
18
+ include Glueby::Internal::Wallet::TapyrusCoreWalletAdapter::Util
19
+ module_function
20
+
21
+ # @param [Glueby::Wallet] wallet
22
+ # @param [Array] data The data to be used for generating pay-to-contract address
23
+ # @return [Array(String, String)]
24
+ # Return (pay-to-contract address, public key used for generating address)
25
+ def create_pay_to_contract_address(wallet, contents: nil)
26
+ pubkey = wallet.internal_wallet.create_pubkey.pubkey
27
+ # Calculate P + H(P || contents)G
28
+ group = ECDSA::Group::Secp256k1
29
+ p = Tapyrus::Key.new(pubkey: pubkey).to_point # P
30
+ commitment = Tapyrus.sha256(p.to_hex(true).htb + contents.join).bth.to_i(16) % group.order # H(P || contents)
31
+ point = p + group.generator.multiply_by_scalar(commitment) # P + H(P || contents)G
32
+ [Tapyrus::Key.new(pubkey: point.to_hex(true)).to_p2pkh, pubkey] # [p2c address, P]
33
+ end
34
+
35
+ def create_txs(wallet, prefix, data, fee_estimator, utxo_provider, type: :simple)
36
+ txb = Tapyrus::TxBuilder.new
37
+ if type == :simple
38
+ txb.data(prefix + data)
39
+ elsif type == :trackable
40
+ p2c_address, payment_base = create_pay_to_contract_address(wallet, contents: [prefix, data])
41
+ txb.pay(p2c_address, P2C_DEFAULT_VALUE)
42
+ end
43
+
44
+ fee = fee_estimator.fee(dummy_tx(txb.build))
45
+ if utxo_provider
46
+ script_pubkey = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
47
+ funding_tx, index = utxo_provider.get_utxo(script_pubkey, fee)
48
+ txb.add_utxo({
49
+ script_pubkey: funding_tx.outputs[index].script_pubkey,
50
+ txid: funding_tx.txid,
51
+ index: index,
52
+ value: funding_tx.outputs[index].value
53
+ })
54
+ else
55
+ sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee)
56
+ outputs.each do |utxo|
57
+ txb.add_utxo({
58
+ script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
59
+ txid: utxo[:txid],
60
+ index: utxo[:vout],
61
+ value: utxo[:amount]
62
+ })
63
+ end
64
+ end
65
+
66
+ prev_txs = if funding_tx
67
+ output = funding_tx.outputs.first
68
+ [{
69
+ txid: funding_tx.txid,
70
+ vout: 0,
71
+ scriptPubKey: output.script_pubkey.to_hex,
72
+ amount: output.value
73
+ }]
74
+ else
75
+ []
76
+ end
77
+
78
+ txb.fee(fee).change_address(wallet.internal_wallet.change_address)
79
+ [funding_tx, wallet.internal_wallet.sign_tx(txb.build, prev_txs), p2c_address, payment_base]
80
+ end
81
+
82
+ def get_transaction(tx)
83
+ Glueby::Internal::RPC.client.getrawtransaction(tx.txid, 1)
84
+ end
85
+ end
86
+ include Glueby::Contract::Timestamp::Util
87
+
88
+ attr_reader :tx, :txid
89
+
90
+ # p2c_address and payment_base is used in `trackable` type
91
+ attr_reader :p2c_address, :payment_base
92
+
93
+ # @param [String] content Data to be hashed and stored in blockchain.
94
+ # @param [String] prefix prefix of op_return data
95
+ # @param [Glueby::Contract::FeeEstimator] fee_estimator
96
+ # @param [Symbol] digest type which select of:
97
+ # - :sha256
98
+ # - :double_sha256
99
+ # - :none
100
+ # @param [Symbol] timestamp_type
101
+ # - :simple
102
+ # - :trackable
103
+ # @raise [Glueby::Contract::Errors::UnsupportedDigestType] if digest is unsupported
104
+ # @raise [Glueby::Contract::Errors::InvalidTimestampType] if timestamp_type is unsupported
105
+ def initialize(
106
+ wallet:,
107
+ content:,
108
+ prefix: '',
109
+ fee_estimator: Glueby::Contract::FixedFeeEstimator.new,
110
+ digest: :sha256,
111
+ utxo_provider: nil,
112
+ timestamp_type: :simple
113
+ )
114
+ @wallet = wallet
115
+ @content = content
116
+ @prefix = prefix
117
+ @fee_estimator = fee_estimator
118
+ raise Glueby::Contract::Errors::UnsupportedDigestType, "#{digest} is invalid digest, supported digest are :sha256, :double_sha256, and :none." unless [:sha256, :double_sha256, :none].include?(digest)
119
+ @digest = digest
120
+ @utxo_provider = utxo_provider
121
+ raise Glueby::Contract::Errors::InvalidTimestampType, "#{timestamp_type} is invalid type, supported types are :simple, and :trackable." unless [:simple, :trackable].include?(timestamp_type)
122
+ @timestamp_type = timestamp_type
123
+ end
124
+
125
+ # broadcast to Tapyrus Core
126
+ # @return [String] txid
127
+ # @raise [TxAlreadyBroadcasted] if tx has been broadcasted.
128
+ # @raise [InsufficientFunds] if result of listunspent is not enough to pay the specified amount
129
+ def save!
130
+ raise Glueby::Contract::Errors::TxAlreadyBroadcasted if @txid
131
+
132
+ funding_tx, @tx, @p2c_address, @payment_base = create_txs(@wallet, @prefix, digest_content, @fee_estimator, @utxo_provider, type: @timestamp_type)
133
+ @wallet.internal_wallet.broadcast(funding_tx) if funding_tx
134
+ @txid = @wallet.internal_wallet.broadcast(@tx)
135
+ end
136
+
137
+ private
138
+
139
+ def digest_content
140
+ case @digest&.downcase
141
+ when :sha256
142
+ Tapyrus.sha256(@content)
143
+ when :double_sha256
144
+ Tapyrus.double_sha256(@content)
145
+ when :none
146
+ @content
147
+ else
148
+ raise Glueby::Contract::Errors::UnsupportedDigestType
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end