glueby 0.4.4 → 0.5.0

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.
@@ -1,151 +1,151 @@
1
- # frozen_string_literal: true
2
-
3
- require 'securerandom'
4
-
5
- module Glueby
6
- module Internal
7
- class Wallet
8
- # ActiveRecordWalletAdapter
9
- #
10
- # This class represents a wallet adapter that use Active Record to manage wallet and utxos.
11
- # To use this wallet adapter, you should do the following steps:
12
- #
13
- # (1) Generate migrations for wallets, keys, utxos tables.
14
- # The generator `glueby:contract:wallet_adapter` is available for migration.
15
- # ```
16
- # rails g glueby:contract:wallet_adapter
17
- # ```
18
- # this generator generates 3 files to create tables used by ActiveRecordWalletAdatper.
19
- # then, migrate them
20
- # ```
21
- # $ rails db:migrate
22
- # ```
23
- #
24
- # (2) Add configuration for activerecord
25
- # ```
26
- # config = {adapter: 'activerecord', schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass'}
27
- # Glueby::Wallet.configure(config)
28
- # ```
29
- #
30
- # (3) Generate wallet and receive address
31
- # ```
32
- # alice_wallet = Glueby::Wallet.create
33
- # address = alice_wallet.internal_wallet.receive_address
34
- # ```
35
- # `Glueby::Internal::Wallet#receive_address` returns Base58 encoded Tapyrus address like '1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a'.
36
- #
37
- # (4) Send TPC to created address and import into wallet.
38
- # In general, ActiveRecordWalletAdapter handle only utxos generated by glueby.
39
- # So, to start to use wallet, some external utxo should be imported into the wallet first.
40
- # This step should be done by external transaction without Glueby::Wallet, such as 'sendtoaddress' or 'generatetoaddress' RPC command of Tapyrus Core
41
- # ```
42
- # $ tapyrus-cli sendtoaddress 1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a 1
43
- # 1740af9f65ffd8537bdd06ccfa911bf1f4d6833222807d29c99d72b47838917d
44
- # ```
45
- #
46
- # then, import into wallet by rake task `glueby:contract:wallet_adapter:import_tx` or `glueby:contract:wallet_adapter:import_block`
47
- # ```
48
- # $ rails "glueby:contract:wallet_adapter:import_tx[1740af9f65ffd8537bdd06ccfa911bf1f4d6833222807d29c99d72b47838917d]""
49
- # ```
50
- #
51
- # (5) You are ready to use ActiveRecordWalletAdatper, check `Glueby::Internal::Wallet#list_unspent` or `Glueby::Wallet#balances`
52
- # ```
53
- # alice_wallet = Glueby::Wallet.create
54
- # alice_wallet.balances
55
- # ```
56
- class ActiveRecordWalletAdapter < AbstractWalletAdapter
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
67
- wallet_id
68
- end
69
-
70
- def delete_wallet(wallet_id)
71
- AR::Wallet.destroy_by(wallet_id: wallet_id)
72
- end
73
-
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?
76
- end
77
-
78
- def unload_wallet(wallet_id)
79
- end
80
-
81
- def wallets
82
- AR::Wallet.all.map(&:wallet_id).sort
83
- end
84
-
85
- def balance(wallet_id, only_finalized = true)
86
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
87
- utxos = wallet.utxos
88
- utxos = utxos.where(status: :finalized) if only_finalized
89
- utxos.sum(&:value)
90
- end
91
-
92
- def list_unspent(wallet_id, only_finalized = true, label = nil)
93
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
94
- utxos = wallet.utxos
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
98
- utxos.map do |utxo|
99
- {
100
- txid: utxo.txid,
101
- vout: utxo.index,
102
- script_pubkey: utxo.script_pubkey,
103
- color_id: utxo.color_id,
104
- amount: utxo.value,
105
- finalized: utxo.status == 'finalized'
106
- }
107
- end
108
- end
109
-
110
- def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
111
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
112
- wallet.sign(tx, prevtxs, sighashtype: sighashtype)
113
- end
114
-
115
- def broadcast(wallet_id, tx, &block)
116
- ::ActiveRecord::Base.transaction do
117
- AR::Utxo.destroy_for_inputs(tx)
118
- AR::Utxo.create_or_update_for_outputs(tx, status: :broadcasted)
119
- block.call(tx) if block
120
- Glueby::Internal::RPC.client.sendrawtransaction(tx.to_hex)
121
- end
122
- end
123
-
124
- def receive_address(wallet_id, label = nil)
125
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
126
- key = wallet.keys.create(purpose: :receive, label: label)
127
- key.address
128
- end
129
-
130
- def change_address(wallet_id)
131
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
132
- key = wallet.keys.create(purpose: :change)
133
- key.address
134
- end
135
-
136
- def create_pubkey(wallet_id)
137
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
138
- key = wallet.keys.create(purpose: :receive)
139
- Tapyrus::Key.new(pubkey: key.public_key)
140
- end
141
-
142
- def get_addresses(wallet_id, label = nil)
143
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
144
- keys = wallet.keys
145
- keys = keys.where(label: label) if label
146
- keys.map(&:address)
147
- end
148
- end
149
- end
150
- end
151
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Glueby
6
+ module Internal
7
+ class Wallet
8
+ # ActiveRecordWalletAdapter
9
+ #
10
+ # This class represents a wallet adapter that use Active Record to manage wallet and utxos.
11
+ # To use this wallet adapter, you should do the following steps:
12
+ #
13
+ # (1) Generate migrations for wallets, keys, utxos tables.
14
+ # The generator `glueby:contract:wallet_adapter` is available for migration.
15
+ # ```
16
+ # rails g glueby:contract:wallet_adapter
17
+ # ```
18
+ # this generator generates 3 files to create tables used by ActiveRecordWalletAdatper.
19
+ # then, migrate them
20
+ # ```
21
+ # $ rails db:migrate
22
+ # ```
23
+ #
24
+ # (2) Add configuration for activerecord
25
+ # ```
26
+ # config = {adapter: 'activerecord', schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass'}
27
+ # Glueby::Wallet.configure(config)
28
+ # ```
29
+ #
30
+ # (3) Generate wallet and receive address
31
+ # ```
32
+ # alice_wallet = Glueby::Wallet.create
33
+ # address = alice_wallet.internal_wallet.receive_address
34
+ # ```
35
+ # `Glueby::Internal::Wallet#receive_address` returns Base58 encoded Tapyrus address like '1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a'.
36
+ #
37
+ # (4) Send TPC to created address and import into wallet.
38
+ # In general, ActiveRecordWalletAdapter handle only utxos generated by glueby.
39
+ # So, to start to use wallet, some external utxo should be imported into the wallet first.
40
+ # This step should be done by external transaction without Glueby::Wallet, such as 'sendtoaddress' or 'generatetoaddress' RPC command of Tapyrus Core
41
+ # ```
42
+ # $ tapyrus-cli sendtoaddress 1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a 1
43
+ # 1740af9f65ffd8537bdd06ccfa911bf1f4d6833222807d29c99d72b47838917d
44
+ # ```
45
+ #
46
+ # then, import into wallet by rake task `glueby:contract:wallet_adapter:import_tx` or `glueby:contract:wallet_adapter:import_block`
47
+ # ```
48
+ # $ rails "glueby:contract:wallet_adapter:import_tx[1740af9f65ffd8537bdd06ccfa911bf1f4d6833222807d29c99d72b47838917d]""
49
+ # ```
50
+ #
51
+ # (5) You are ready to use ActiveRecordWalletAdatper, check `Glueby::Internal::Wallet#list_unspent` or `Glueby::Wallet#balances`
52
+ # ```
53
+ # alice_wallet = Glueby::Wallet.create
54
+ # alice_wallet.balances
55
+ # ```
56
+ class ActiveRecordWalletAdapter < AbstractWalletAdapter
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
67
+ wallet_id
68
+ end
69
+
70
+ def delete_wallet(wallet_id)
71
+ AR::Wallet.destroy_by(wallet_id: wallet_id)
72
+ end
73
+
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?
76
+ end
77
+
78
+ def unload_wallet(wallet_id)
79
+ end
80
+
81
+ def wallets
82
+ AR::Wallet.all.map(&:wallet_id).sort
83
+ end
84
+
85
+ def balance(wallet_id, only_finalized = true)
86
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
87
+ utxos = wallet.utxos
88
+ utxos = utxos.where(status: :finalized) if only_finalized
89
+ utxos.sum(&:value)
90
+ end
91
+
92
+ def list_unspent(wallet_id, only_finalized = true, label = nil)
93
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
94
+ utxos = wallet.utxos
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
98
+ utxos.map do |utxo|
99
+ {
100
+ txid: utxo.txid,
101
+ vout: utxo.index,
102
+ script_pubkey: utxo.script_pubkey,
103
+ color_id: utxo.color_id,
104
+ amount: utxo.value,
105
+ finalized: utxo.status == 'finalized'
106
+ }
107
+ end
108
+ end
109
+
110
+ def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
111
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
112
+ wallet.sign(tx, prevtxs, sighashtype: sighashtype)
113
+ end
114
+
115
+ def broadcast(wallet_id, tx, &block)
116
+ ::ActiveRecord::Base.transaction do
117
+ AR::Utxo.destroy_for_inputs(tx)
118
+ AR::Utxo.create_or_update_for_outputs(tx, status: :broadcasted)
119
+ block.call(tx) if block
120
+ Glueby::Internal::RPC.client.sendrawtransaction(tx.to_hex)
121
+ end
122
+ end
123
+
124
+ def receive_address(wallet_id, label = nil)
125
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
126
+ key = wallet.keys.create(purpose: :receive, label: label)
127
+ key.address
128
+ end
129
+
130
+ def change_address(wallet_id)
131
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
132
+ key = wallet.keys.create(purpose: :change)
133
+ key.address
134
+ end
135
+
136
+ def create_pubkey(wallet_id)
137
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
138
+ key = wallet.keys.create(purpose: :receive)
139
+ Tapyrus::Key.new(pubkey: key.public_key)
140
+ end
141
+
142
+ def get_addresses(wallet_id, label = nil)
143
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
144
+ keys = wallet.keys
145
+ keys = keys.where(label: label) if label
146
+ keys.map(&:address)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end