Class: Glueby::Internal::Wallet::ActiveRecordWalletAdapter
- Inherits:
-
AbstractWalletAdapter
- Object
- AbstractWalletAdapter
- Glueby::Internal::Wallet::ActiveRecordWalletAdapter
- Defined in:
- lib/glueby/internal/wallet/active_record_wallet_adapter.rb,
lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb
Overview
ActiveRecordWalletAdapter
This class represents a wallet adapter that use Active Record to manage wallet and utxos. To use this wallet adapter, you should do the following steps:
(1) Generate migrations for wallets, keys, utxos tables. The generator ‘glueby:contract:wallet_adapter` is available for migration. “` rails g glueby:contract:wallet_adapter “` this generator generates 3 files to create tables used by ActiveRecordWalletAdatper. then, migrate them “` $ rails db:migrate “`
(2) Add configuration for activerecord “‘ config = ’activerecord’, schema: ‘http’, host: ‘127.0.0.1’, port: 12381, user: ‘user’, password: ‘pass’ Glueby::Wallet.configure(config) “‘
(3) Generate wallet and receive address “‘ alice_wallet = Glueby::Wallet.create address = alice_wallet.internal_wallet.receive_address “` `Glueby::Internal::Wallet#receive_address` returns Base58 encoded Tapyrus address like ’1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a’.
(4) Send TPC to created address and import into wallet. In general, ActiveRecordWalletAdapter handle only utxos generated by glueby. So, to start to use wallet, some external utxo should be imported into the wallet first. This step should be done by external transaction without Glueby::Wallet, such as ‘sendtoaddress’ or ‘generatetoaddress’ RPC command of Tapyrus Core “‘ $ tapyrus-cli sendtoaddress 1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a 1 1740af9f65ffd8537bdd06ccfa911bf1f4d6833222807d29c99d72b47838917d “`
then, import into wallet by rake task ‘glueby:contract:wallet_adapter:import_tx` or `glueby:contract:wallet_adapter:import_block` “` $ rails “glueby:contract:wallet_adapter:import_tx”“ “`
(5) You are ready to use ActiveRecordWalletAdatper, check ‘Glueby::Internal::Wallet#list_unspent` or `Glueby::Wallet#balances` “` alice_wallet = Glueby::Wallet.create alice_wallet.balances “`
Direct Known Subclasses
Defined Under Namespace
Classes: Syncer
Instance Method Summary collapse
- #balance(wallet_id, only_finalized = true) ⇒ Object
- #broadcast(wallet_id, tx, &block) ⇒ Object
- #change_address(wallet_id) ⇒ Object
- #create_pay_to_contract_address(wallet_id, contents) ⇒ Object
- #create_pubkey(wallet_id) ⇒ Object
- #create_wallet(wallet_id = nil) ⇒ Object
- #delete_wallet(wallet_id) ⇒ Object
- #get_addresses(wallet_id, label = nil) ⇒ Object
- #get_addresses_info(addresses) ⇒ Object
- #has_address?(wallet_id, address) ⇒ Boolean
- #list_unspent(wallet_id, only_finalized = true, label = nil, color_id: nil) ⇒ Object
- #list_unspent_with_count(wallet_id, only_finalized = true, label = nil, color_id: nil, page: 1, per: 25) ⇒ Object
- #load_wallet(wallet_id) ⇒ Object
- #pay_to_contract_key(wallet_id, pubkey, contents) ⇒ Object
- #receive_address(wallet_id, label = nil) ⇒ Object
- #sign_to_pay_to_contract_address(wallet_id, tx, utxo, payment_base, contents) ⇒ Object
- #sign_tx(wallet_id, tx, prevtxs = [], sighashtype: ) ⇒ Object
- #unload_wallet(wallet_id) ⇒ Object
- #wallets ⇒ Object
Methods inherited from AbstractWalletAdapter
Instance Method Details
#balance(wallet_id, only_finalized = true) ⇒ Object
86 87 88 89 90 91 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 86 def balance(wallet_id, only_finalized = true) wallet = AR::Wallet.find_by(wallet_id: wallet_id) utxos = wallet.utxos.where(locked_at: nil) utxos = utxos.where(status: :finalized) if only_finalized utxos.sum(&:value) end |
#broadcast(wallet_id, tx, &block) ⇒ Object
112 113 114 115 116 117 118 119 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 112 def broadcast(wallet_id, tx, &block) ::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do AR::Utxo.destroy_for_inputs(tx) AR::Utxo.create_or_update_for_outputs(tx, status: :broadcasted) block.call(tx) if block Glueby::Internal::RPC.client.sendrawtransaction(tx.to_hex) end end |
#change_address(wallet_id) ⇒ Object
127 128 129 130 131 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 127 def change_address(wallet_id) wallet = AR::Wallet.find_by(wallet_id: wallet_id) key = wallet.keys.create(purpose: :change) key.address end |
#create_pay_to_contract_address(wallet_id, contents) ⇒ Object
176 177 178 179 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 176 def create_pay_to_contract_address(wallet_id, contents) pubkey = create_pubkey(wallet_id) [pay_to_contract_key(wallet_id, pubkey, contents).to_p2pkh, pubkey.pubkey] end |
#create_pubkey(wallet_id) ⇒ Object
133 134 135 136 137 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 133 def create_pubkey(wallet_id) wallet = AR::Wallet.find_by(wallet_id: wallet_id) key = wallet.keys.create(purpose: :receive) Tapyrus::Key.new(pubkey: key.public_key, key_type: Tapyrus::Key::TYPES[:compressed]) end |
#create_wallet(wallet_id = nil) ⇒ Object
61 62 63 64 65 66 67 68 69 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 61 def create_wallet(wallet_id = nil) wallet_id = SecureRandom.hex(16) unless wallet_id begin AR::Wallet.create!(wallet_id: wallet_id) rescue ActiveRecord::RecordInvalid => _ raise Errors::WalletAlreadyCreated, "wallet_id '#{wallet_id}' is already exists" end wallet_id end |
#delete_wallet(wallet_id) ⇒ Object
71 72 73 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 71 def delete_wallet(wallet_id) AR::Wallet.destroy_by(wallet_id: wallet_id) end |
#get_addresses(wallet_id, label = nil) ⇒ Object
139 140 141 142 143 144 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 139 def get_addresses(wallet_id, label = nil) wallet = AR::Wallet.find_by(wallet_id: wallet_id) keys = wallet.keys keys = keys.where(label: label) if label keys.map(&:address) end |
#get_addresses_info(addresses) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 146 def get_addresses_info(addresses) unless addresses.is_a?(Array) addresses = [addresses] end script_pubkeys = addresses.map do |address| Tapyrus::Script.parse_from_addr(address).to_hex rescue ::ArgumentError => e raise Glueby::ArgumentError, "\"#{address}\" is invalid address. #{e.}" rescue RuntimeError => e if e. == 'Invalid version bytes.' || e. == 'Invalid address.' raise Glueby::ArgumentError, "\"#{address}\" is invalid address. #{e.}" else raise e end end keys = AR::Key.where(script_pubkey: script_pubkeys) keys.map do |key| { address: key.address, public_key: key.public_key, wallet_id: key.wallet.wallet_id, label: key.label, purpose: key.purpose, script_pubkey: key.script_pubkey } end end |
#has_address?(wallet_id, address) ⇒ Boolean
200 201 202 203 204 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 200 def has_address?(wallet_id, address) script_pubkey = Tapyrus::Script.parse_from_addr(address) wallet = AR::Wallet.find_by(wallet_id: wallet_id) wallet.keys.exists?(script_pubkey: script_pubkey.to_hex) end |
#list_unspent(wallet_id, only_finalized = true, label = nil, color_id: nil) ⇒ Object
102 103 104 105 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 102 def list_unspent(wallet_id, only_finalized = true, label = nil, color_id: nil) utxos = list_unspent_internal(wallet_id, color_id, only_finalized, label) utxos_to_h(utxos) end |
#list_unspent_with_count(wallet_id, only_finalized = true, label = nil, color_id: nil, page: 1, per: 25) ⇒ Object
93 94 95 96 97 98 99 100 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 93 def list_unspent_with_count(wallet_id, only_finalized = true, label = nil, color_id: nil, page: 1, per: 25) utxos = list_unspent_internal(wallet_id, color_id, only_finalized, label) utxos = utxos.page(page).per(per) if per > 0 { count: utxos.total_count, outputs: utxos_to_h(utxos) } end |
#load_wallet(wallet_id) ⇒ Object
75 76 77 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 75 def load_wallet(wallet_id) raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found" unless AR::Wallet.where(wallet_id: wallet_id).exists? end |
#pay_to_contract_key(wallet_id, pubkey, contents) ⇒ Object
181 182 183 184 185 186 187 188 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 181 def pay_to_contract_key(wallet_id, pubkey, contents) # Calculate P + H(P || contents)G group = ECDSA::Group::Secp256k1 p = pubkey.to_point # P commitment = create_pay_to_contract_commitment(pubkey, contents) point = p + group.generator.multiply_by_scalar(commitment) # P + H(P || contents)G Tapyrus::Key.new(pubkey: point.to_hex(true), key_type: Tapyrus::Key::TYPES[:compressed]) end |
#receive_address(wallet_id, label = nil) ⇒ Object
121 122 123 124 125 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 121 def receive_address(wallet_id, label = nil) wallet = AR::Wallet.find_by(wallet_id: wallet_id) key = wallet.keys.create(purpose: :receive, label: label) key.address end |
#sign_to_pay_to_contract_address(wallet_id, tx, utxo, payment_base, contents) ⇒ Object
190 191 192 193 194 195 196 197 198 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 190 def sign_to_pay_to_contract_address(wallet_id, tx, utxo, payment_base, contents) key = create_pay_to_contract_private_key(wallet_id, payment_base, contents) sighash = tx.sighash_for_input(utxo[:vout], Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb)) sig = key.sign(sighash, algo: :schnorr) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C') script_sig = Tapyrus::Script.parse_from_payload(Tapyrus::Script.pack_pushdata(sig) + Tapyrus::Script.pack_pushdata(key.pubkey.htb)) tx.inputs[utxo[:vout]].script_sig = script_sig tx end |
#sign_tx(wallet_id, tx, prevtxs = [], sighashtype: ) ⇒ Object
107 108 109 110 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 107 def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all]) wallet = AR::Wallet.find_by(wallet_id: wallet_id) wallet.sign(tx, prevtxs, sighashtype: sighashtype) end |
#unload_wallet(wallet_id) ⇒ Object
79 80 |
# File 'lib/glueby/internal/wallet/active_record_wallet_adapter.rb', line 79 def unload_wallet(wallet_id) end |