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
@@ -28,7 +28,7 @@ module Glueby
28
28
 
29
29
  def table_options
30
30
  if mysql?
31
- ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
31
+ ', :options => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"'
32
32
  else
33
33
  ""
34
34
  end
@@ -34,10 +34,13 @@ module Glueby
34
34
  autoload :Errors, 'glueby/internal/wallet/errors'
35
35
 
36
36
  class << self
37
- attr_writer :wallet_adapter
38
-
39
- def create
40
- new(wallet_adapter.create_wallet)
37
+ def create(wallet_id = nil)
38
+ begin
39
+ wallet_id = wallet_adapter.create_wallet(wallet_id)
40
+ rescue Errors::WalletAlreadyCreated => _
41
+ # Ignore when wallet is already created.
42
+ end
43
+ new(wallet_id)
41
44
  end
42
45
 
43
46
  def load(wallet_id)
@@ -53,6 +56,16 @@ module Glueby
53
56
  wallet_adapter.wallets.map { |id| new(id) }
54
57
  end
55
58
 
59
+ def wallet_adapter=(adapter)
60
+ if adapter.is_a?(ActiveRecordWalletAdapter)
61
+ BlockSyncer.register_syncer(ActiveRecordWalletAdapter::Syncer)
62
+ else
63
+ BlockSyncer.unregister_syncer(ActiveRecordWalletAdapter::Syncer)
64
+ end
65
+
66
+ @wallet_adapter = adapter
67
+ end
68
+
56
69
  def wallet_adapter
57
70
  @wallet_adapter or
58
71
  raise Errors::ShouldInitializeWalletAdapter, 'You should initialize wallet adapter using `Glueby::Internal::Wallet.wallet_adapter = some wallet adapter instance`.'
@@ -69,24 +82,50 @@ module Glueby
69
82
  wallet_adapter.balance(id, only_finalized)
70
83
  end
71
84
 
72
- def list_unspent(only_finalized = true)
73
- wallet_adapter.list_unspent(id, only_finalized)
85
+ # @param only_finalized [Boolean] The flag to get a UTXO with status only finalized
86
+ # @param label [String] This label is used to filtered the UTXOs with labeled if a key or Utxo is labeled.
87
+ # - If label is not specified (label=nil), all UTXOs will be returned.
88
+ # - If label=:unlabeled, only unlabeled UTXOs will be returned.
89
+ def list_unspent(only_finalized = true, label = nil)
90
+ wallet_adapter.list_unspent(id, only_finalized, label)
74
91
  end
75
92
 
76
93
  def delete
77
94
  wallet_adapter.delete_wallet(id)
78
95
  end
79
96
 
80
- def sign_tx(tx, prev_txs = [])
81
- wallet_adapter.sign_tx(id, tx, prev_txs)
97
+ # @param [Tapyrus::Tx] tx The tx that is signed
98
+ # @param [Array<Hash>] prev_txs An array of hash that represents unbroadcasted transaction outputs used by signing tx
99
+ # @option prev_txs [String] :txid
100
+ # @option prev_txs [Integer] :vout
101
+ # @option prev_txs [String] :scriptPubkey
102
+ # @option prev_txs [Integer] :amount
103
+ # @param [Boolean] for_fee_provider_input The flag to notify whether the caller is FeeProvider and called for signing a input that is by FeeProvider.
104
+ def sign_tx(tx, prev_txs = [], for_fee_provider_input: false)
105
+ sighashtype = Tapyrus::SIGHASH_TYPE[:all]
106
+
107
+ if !for_fee_provider_input && Glueby.configuration.fee_provider_bears?
108
+ sighashtype |= Tapyrus::SIGHASH_TYPE[:anyonecanpay]
109
+ end
110
+
111
+ wallet_adapter.sign_tx(id, tx, prev_txs, sighashtype: sighashtype)
82
112
  end
83
113
 
84
- def broadcast(tx)
114
+ # Broadcast a transaction via Tapyrus Core RPC
115
+ # @param [Tapyrus::Tx] tx The tx that would be broadcasted
116
+ # @option [Boolean] without_fee_provider The flag to avoid to use FeeProvider temporary.
117
+ # @param [Proc] block The block that is called before broadcasting. It can be used to handle tx that is modified by FeeProvider.
118
+ def broadcast(tx, without_fee_provider: false, &block)
119
+ tx = FeeProvider.provide(tx) if !without_fee_provider && Glueby.configuration.fee_provider_bears?
120
+
121
+ block.call(tx) if block
122
+
85
123
  wallet_adapter.broadcast(id, tx)
124
+ tx
86
125
  end
87
126
 
88
- def receive_address
89
- wallet_adapter.receive_address(id)
127
+ def receive_address(label = nil)
128
+ wallet_adapter.receive_address(id, label)
90
129
  end
91
130
 
92
131
  def change_address
@@ -97,8 +136,8 @@ module Glueby
97
136
  wallet_adapter.create_pubkey(id)
98
137
  end
99
138
 
100
- def collect_uncolored_outputs(amount)
101
- utxos = list_unspent
139
+ def collect_uncolored_outputs(amount, label = nil)
140
+ utxos = list_unspent(true, label)
102
141
 
103
142
  utxos.inject([0, []]) do |sum, output|
104
143
  next sum if output[:color_id]
@@ -112,6 +151,10 @@ module Glueby
112
151
  raise Glueby::Contract::Errors::InsufficientFunds
113
152
  end
114
153
 
154
+ def get_addresses(label = nil)
155
+ wallet_adapter.get_addresses(id, label)
156
+ end
157
+
115
158
  private
116
159
 
117
160
  def wallet_adapter
@@ -7,8 +7,10 @@ module Glueby
7
7
  class AbstractWalletAdapter
8
8
  # Creates a new wallet inside the wallet component and returns `wallet_id`. The created
9
9
  # wallet is loaded from at first.
10
+ # @params [String] wallet_id - Option. The wallet id that if for the wallet to be created. If this is nil, wallet adapter generates it.
10
11
  # @return [String] wallet_id
11
- def create_wallet
12
+ # @raise [Glueby::Internal::Wallet::Errors::WalletAlreadyCreated] when the specified wallet has been already created.
13
+ def create_wallet(wallet_id = nil)
12
14
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
13
15
  end
14
16
 
@@ -32,6 +34,7 @@ module Glueby
32
34
  #
33
35
  # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
34
36
  # @raise [Glueby::Internal::Wallet::Errors::WalletAlreadyLoaded] when the specified wallet has been already loaded.
37
+ # @raise [Glueby::Internal::Wallet::Errors::WalletNotFound] when the specified wallet is not found.
35
38
  def load_wallet(wallet_id)
36
39
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
37
40
  end
@@ -62,11 +65,13 @@ module Glueby
62
65
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
63
66
  end
64
67
 
65
- # Returns all the UTXOs that the wallet has.
68
+ # Returns the UTXOs that the wallet has.
69
+ # If label is specified, return UTXOs filtered with label
66
70
  #
67
71
  # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
68
72
  # @param [Boolean] only_finalized - The UTXOs includes only finalized UTXO value if it
69
73
  # is true. Default is true.
74
+ # @param [String] label - Label for filtering UTXOs
70
75
  # @return [Array of UTXO]
71
76
  #
72
77
  # ## The UTXO structure
@@ -75,7 +80,7 @@ module Glueby
75
80
  # - vout: [Integer] Output index
76
81
  # - amount: [Integer] Amount of the UTXO as tapyrus unit
77
82
  # - finalized: [Boolean] Whether the UTXO is finalized
78
- def list_unspent(wallet_id, only_finalized = true)
83
+ def list_unspent(wallet_id, only_finalized = true, label = nil)
79
84
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
80
85
  end
81
86
 
@@ -83,10 +88,12 @@ module Glueby
83
88
  #
84
89
  # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
85
90
  # @param [Tapyrus::Tx] tx - The transaction will be signed.
86
- # @param [Array] prevtxs array of hash that represents unbroadcasted transaction outputs used by signing tx.
91
+ # @param [Array] prevtxs - array of hash that represents unbroadcasted transaction outputs used by signing tx.
87
92
  # Each hash has `txid`, `vout`, `scriptPubKey`, `amount` fields.
93
+ # @param [Integer] sighashtype - The sighash flag for each signature that would be produced here.
88
94
  # @return [Tapyrus::Tx]
89
- def sign_tx(wallet_id, tx, prevtxs = [])
95
+ # @raise [Glueby::Internal::Wallet::Errors::InvalidSighashType] when the specified sighashtype is invalid
96
+ def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
90
97
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
91
98
  end
92
99
 
@@ -102,8 +109,9 @@ module Glueby
102
109
  # Returns an address to receive coin.
103
110
  #
104
111
  # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
112
+ # @param [String] label The label associated with this address.
105
113
  # @return [String] P2PKH address
106
- def receive_address(wallet_id)
114
+ def receive_address(wallet_id, label = nil)
107
115
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
108
116
  end
109
117
 
@@ -125,6 +133,17 @@ module Glueby
125
133
  def create_pubkey(wallet_id)
126
134
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
127
135
  end
136
+
137
+ # Returns an array of addresses
138
+ #
139
+ # This method is expected to return the list of addresses that wallet has.
140
+ #
141
+ # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
142
+ # @param [String] label The label to filter the addresses.
143
+ # @return [Array<String>] array of P2PKH address
144
+ def get_addresses(wallet_id, label = nil)
145
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
146
+ end
128
147
  end
129
148
  end
130
149
  end
@@ -36,6 +36,7 @@ module Glueby
36
36
 
37
37
  utxo = Utxo.find_or_initialize_by(txid: tx.txid, index: index)
38
38
  utxo.update!(
39
+ label: key.label,
39
40
  script_pubkey: output.script_pubkey.to_hex,
40
41
  value: output.value,
41
42
  status: status,
@@ -13,13 +13,16 @@ module Glueby
13
13
 
14
14
  # @param [Tapyrus::Tx] tx
15
15
  # @param [Array] prevtxs array of outputs
16
- def sign(tx, prevtxs = [])
16
+ # @param [Integer] sighashtype
17
+ def sign(tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
18
+ validate_sighashtype!(sighashtype)
19
+
17
20
  tx.inputs.each.with_index do |input, index|
18
21
  script_pubkey = script_for_input(input, prevtxs)
19
22
  next unless script_pubkey
20
23
  key = Key.key_for_script(script_pubkey)
21
24
  next unless key
22
- sign_tx_for_p2pkh(tx, index, key, script_pubkey)
25
+ sign_tx_for_p2pkh(tx, index, key, script_pubkey, sighashtype)
23
26
  end
24
27
  tx
25
28
  end
@@ -30,9 +33,9 @@ module Glueby
30
33
 
31
34
  private
32
35
 
33
- def sign_tx_for_p2pkh(tx, index, key, script_pubkey)
34
- sighash = tx.sighash_for_input(index, script_pubkey)
35
- sig = key.sign(sighash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C')
36
+ def sign_tx_for_p2pkh(tx, index, key, script_pubkey, sighashtype)
37
+ sighash = tx.sighash_for_input(index, script_pubkey, hash_type: sighashtype)
38
+ sig = key.sign(sighash) + [sighashtype].pack('C')
36
39
  script_sig = Tapyrus::Script.parse_from_payload(Tapyrus::Script.pack_pushdata(sig) + Tapyrus::Script.pack_pushdata(key.public_key.htb))
37
40
  tx.inputs[index].script_sig = script_sig
38
41
  end
@@ -47,6 +50,13 @@ module Glueby
47
50
  Tapyrus::Script.parse_from_payload(output[:scriptPubKey].htb) if output
48
51
  end
49
52
  end
53
+
54
+ def validate_sighashtype!(sighashtype)
55
+ hash_type = sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
56
+ if hash_type < Tapyrus::SIGHASH_TYPE[:all] || hash_type > Tapyrus::SIGHASH_TYPE[:single]
57
+ raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
58
+ end
59
+ end
50
60
  end
51
61
  end
52
62
  end
@@ -54,9 +54,16 @@ module Glueby
54
54
  # alice_wallet.balances
55
55
  # ```
56
56
  class ActiveRecordWalletAdapter < AbstractWalletAdapter
57
- def create_wallet
58
- wallet_id = SecureRandom.hex(16)
59
- wallet = AR::Wallet.create(wallet_id: wallet_id)
57
+
58
+ autoload :Syncer, 'glueby/internal/wallet/active_record_wallet_adapter/syncer'
59
+
60
+ def create_wallet(wallet_id = nil)
61
+ wallet_id = SecureRandom.hex(16) unless wallet_id
62
+ begin
63
+ AR::Wallet.create!(wallet_id: wallet_id)
64
+ rescue ActiveRecord::RecordInvalid => _
65
+ raise Errors::WalletAlreadyCreated, "wallet_id '#{wallet_id}' is already exists"
66
+ end
60
67
  wallet_id
61
68
  end
62
69
 
@@ -65,6 +72,7 @@ module Glueby
65
72
  end
66
73
 
67
74
  def load_wallet(wallet_id)
75
+ raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found" unless AR::Wallet.where(wallet_id: wallet_id).exists?
68
76
  end
69
77
 
70
78
  def unload_wallet(wallet_id)
@@ -81,10 +89,12 @@ module Glueby
81
89
  utxos.sum(&:value)
82
90
  end
83
91
 
84
- def list_unspent(wallet_id, only_finalized = true)
92
+ def list_unspent(wallet_id, only_finalized = true, label = nil)
85
93
  wallet = AR::Wallet.find_by(wallet_id: wallet_id)
86
94
  utxos = wallet.utxos
87
95
  utxos = utxos.where(status: :finalized) if only_finalized
96
+ utxos = utxos.where(label: label) if label && (label != :unlabeled)
97
+ utxos = utxos.where(label: nil) if label == :unlabeled
88
98
  utxos.map do |utxo|
89
99
  {
90
100
  txid: utxo.txid,
@@ -97,9 +107,9 @@ module Glueby
97
107
  end
98
108
  end
99
109
 
100
- def sign_tx(wallet_id, tx, prevtxs = [])
110
+ def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
101
111
  wallet = AR::Wallet.find_by(wallet_id: wallet_id)
102
- wallet.sign(tx, prevtxs)
112
+ wallet.sign(tx, prevtxs, sighashtype: sighashtype)
103
113
  end
104
114
 
105
115
  def broadcast(wallet_id, tx)
@@ -110,9 +120,9 @@ module Glueby
110
120
  end
111
121
  end
112
122
 
113
- def receive_address(wallet_id)
123
+ def receive_address(wallet_id, label = nil)
114
124
  wallet = AR::Wallet.find_by(wallet_id: wallet_id)
115
- key = wallet.keys.create(purpose: :receive)
125
+ key = wallet.keys.create(purpose: :receive, label: label)
116
126
  key.address
117
127
  end
118
128
 
@@ -127,6 +137,13 @@ module Glueby
127
137
  key = wallet.keys.create(purpose: :receive)
128
138
  Tapyrus::Key.new(pubkey: key.public_key)
129
139
  end
140
+
141
+ def get_addresses(wallet_id, label = nil)
142
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
143
+ keys = wallet.keys
144
+ keys = keys.where(label: label) if label
145
+ keys.map(&:address)
146
+ end
130
147
  end
131
148
  end
132
149
  end
@@ -0,0 +1,14 @@
1
+ module Glueby
2
+ module Internal
3
+ class Wallet
4
+ class ActiveRecordWalletAdapter
5
+ class Syncer
6
+ def tx_sync(tx)
7
+ Glueby::Internal::Wallet::AR::Utxo.destroy_for_inputs(tx)
8
+ Glueby::Internal::Wallet::AR::Utxo.create_or_update_for_outputs(tx)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -5,6 +5,9 @@ module Glueby
5
5
  class ShouldInitializeWalletAdapter < StandardError; end
6
6
  class WalletUnloaded < StandardError; end
7
7
  class WalletAlreadyLoaded < StandardError; end
8
+ class WalletAlreadyCreated < StandardError; end
9
+ class WalletNotFound < StandardError; end
10
+ class InvalidSighashType < StandardError; end
8
11
  end
9
12
  end
10
13
  end
@@ -28,9 +28,18 @@ module Glueby
28
28
  RPC_WALLET_ERROR_ERROR_CODE = -4 # Unspecified problem with wallet (key not found etc.)
29
29
  RPC_WALLET_NOT_FOUND_ERROR_CODE = -18 # Invalid wallet specified
30
30
 
31
- def create_wallet
32
- wallet_id = SecureRandom.hex(16)
33
- RPC.client.createwallet(wallet_name(wallet_id))
31
+ def create_wallet(wallet_id = nil)
32
+ wallet_id = SecureRandom.hex(16) unless wallet_id
33
+ begin
34
+ RPC.client.createwallet(wallet_name(wallet_id))
35
+ rescue Tapyrus::RPC::Error => ex
36
+ if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Wallet wallet-#{wallet_id} already exists\./ =~ ex.rpc_error['message']
37
+ raise Errors::WalletAlreadyCreated, "Wallet #{wallet_id} has been already created."
38
+ else
39
+ raise ex
40
+ end
41
+ end
42
+
34
43
  wallet_id
35
44
  end
36
45
 
@@ -42,10 +51,11 @@ module Glueby
42
51
 
43
52
  def load_wallet(wallet_id)
44
53
  RPC.client.loadwallet(wallet_name(wallet_id))
45
- rescue RuntimeError => ex
46
- json = JSON.parse(ex.message)
47
- if json.is_a?(Hash) && json['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Duplicate -wallet filename specified/ =~ ex.message
54
+ rescue Tapyrus::RPC::Error => ex
55
+ if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Duplicate -wallet filename specified/ =~ ex.rpc_error['message']
48
56
  raise Errors::WalletAlreadyLoaded, "Wallet #{wallet_id} has been already loaded."
57
+ elsif ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
58
+ raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found"
49
59
  else
50
60
  raise ex
51
61
  end
@@ -73,11 +83,14 @@ module Glueby
73
83
  end
74
84
  end
75
85
 
76
- def list_unspent(wallet_id, only_finalized = true)
86
+ def list_unspent(wallet_id, only_finalized = true, label = nil)
77
87
  perform_as(wallet_id) do |client|
78
88
  min_conf = only_finalized ? 1 : 0
79
89
  res = client.listunspent(min_conf)
80
90
 
91
+ res = res.filter { |i| i['label'] == label } if label && (label != :unlabeled)
92
+ res = res.filter { |i| i['label'] == "" } if label == :unlabeled
93
+
81
94
  res.map do |i|
82
95
  script = Tapyrus::Script.parse_from_payload(i['scriptPubKey'].htb)
83
96
  color_id = if script.cp2pkh? || script.cp2sh?
@@ -95,9 +108,9 @@ module Glueby
95
108
  end
96
109
  end
97
110
 
98
- def sign_tx(wallet_id, tx, prevtxs = [])
111
+ def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
99
112
  perform_as(wallet_id) do |client|
100
- res = client.signrawtransactionwithwallet(tx.to_hex, prevtxs)
113
+ res = client.signrawtransactionwithwallet(tx.to_hex, prevtxs, encode_sighashtype(sighashtype))
101
114
  if res['complete']
102
115
  Tapyrus::Tx.parse_from_payload(res['hex'].htb)
103
116
  else
@@ -112,9 +125,9 @@ module Glueby
112
125
  end
113
126
  end
114
127
 
115
- def receive_address(wallet_id)
128
+ def receive_address(wallet_id, label = nil)
116
129
  perform_as(wallet_id) do |client|
117
- client.getnewaddress('', ADDRESS_TYPE)
130
+ client.getnewaddress(label || '', ADDRESS_TYPE)
118
131
  end
119
132
  end
120
133
 
@@ -138,9 +151,8 @@ module Glueby
138
151
  RPC.perform_as(wallet_name(wallet_id)) do |client|
139
152
  begin
140
153
  yield(client)
141
- rescue RuntimeError => ex
142
- json = JSON.parse(ex.message)
143
- if json.is_a?(Hash) && json['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
154
+ rescue Tapyrus::RPC::Error => ex
155
+ if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
144
156
  raise Errors::WalletUnloaded, "The wallet #{wallet_id} is unloaded. You should load before use it."
145
157
  else
146
158
  raise ex
@@ -152,6 +164,22 @@ module Glueby
152
164
  def wallet_name(wallet_id)
153
165
  "#{WALLET_PREFIX}#{wallet_id}"
154
166
  end
167
+
168
+ def encode_sighashtype(sighashtype)
169
+ type = case sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
170
+ when Tapyrus::SIGHASH_TYPE[:all] then 'ALL'
171
+ when Tapyrus::SIGHASH_TYPE[:none] then 'NONE'
172
+ when Tapyrus::SIGHASH_TYPE[:single] then 'SIGNLE'
173
+ else
174
+ raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
175
+ end
176
+
177
+ if sighashtype & Tapyrus::SIGHASH_TYPE[:anyonecanpay] == 0x80
178
+ type += '|ANYONECANPAY'
179
+ end
180
+
181
+ type
182
+ end
155
183
  end
156
184
  end
157
185
  end