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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +35 -0
- data/Gemfile +1 -0
- data/README.md +224 -16
- data/glueby.gemspec +2 -2
- data/lib/generators/glueby/contract/block_syncer_generator.rb +26 -0
- data/lib/generators/glueby/contract/reissuable_token_generator.rb +26 -0
- data/lib/generators/glueby/contract/templates/initializer.rb.erb +4 -2
- data/lib/generators/glueby/contract/templates/key_table.rb.erb +4 -3
- data/lib/generators/glueby/contract/templates/reissuable_token_table.rb.erb +10 -0
- data/lib/generators/glueby/contract/templates/system_information_table.rb.erb +12 -0
- data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +1 -1
- data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +3 -2
- data/lib/generators/glueby/contract/templates/wallet_table.rb.erb +2 -2
- data/lib/glueby.rb +28 -11
- data/lib/glueby/active_record.rb +8 -0
- data/lib/glueby/active_record/system_information.rb +15 -0
- data/lib/glueby/block_syncer.rb +98 -0
- data/lib/glueby/configuration.rb +62 -0
- data/lib/glueby/contract.rb +2 -2
- data/lib/glueby/contract/active_record.rb +1 -0
- data/lib/glueby/contract/active_record/reissuable_token.rb +26 -0
- data/lib/glueby/contract/fee_estimator.rb +38 -0
- data/lib/glueby/contract/payment.rb +10 -6
- data/lib/glueby/contract/timestamp.rb +8 -6
- data/lib/glueby/contract/timestamp/syncer.rb +13 -0
- data/lib/glueby/contract/token.rb +78 -26
- data/lib/glueby/contract/tx_builder.rb +23 -20
- data/lib/glueby/fee_provider.rb +73 -0
- data/lib/glueby/fee_provider/tasks.rb +141 -0
- data/lib/glueby/generator/migrate_generator.rb +1 -1
- data/lib/glueby/internal/wallet.rb +56 -13
- data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +25 -6
- data/lib/glueby/internal/wallet/active_record/utxo.rb +1 -0
- data/lib/glueby/internal/wallet/active_record/wallet.rb +15 -5
- data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +25 -8
- data/lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb +14 -0
- data/lib/glueby/internal/wallet/errors.rb +3 -0
- data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +42 -14
- data/lib/glueby/railtie.rb +14 -0
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby/wallet.rb +3 -2
- data/lib/tasks/glueby/block_syncer.rake +29 -0
- data/lib/tasks/glueby/contract/timestamp.rake +4 -26
- data/lib/tasks/glueby/fee_provider.rake +18 -0
- metadata +26 -11
- data/.travis.yml +0 -7
- data/lib/glueby/contract/fee_provider.rb +0 -21
- data/lib/tasks/glueby/contract/wallet_adapter.rake +0 -42
@@ -34,10 +34,13 @@ module Glueby
|
|
34
34
|
autoload :Errors, 'glueby/internal/wallet/errors'
|
35
35
|
|
36
36
|
class << self
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
73
|
-
|
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
|
-
|
81
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
@@ -13,13 +13,16 @@ module Glueby
|
|
13
13
|
|
14
14
|
# @param [Tapyrus::Tx] tx
|
15
15
|
# @param [Array] prevtxs array of outputs
|
16
|
-
|
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) + [
|
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
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
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
|
46
|
-
|
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
|
142
|
-
|
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
|