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