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
@@ -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