glueby 0.4.4 → 0.5.0

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