module TxCatcher class Transaction < Sequel::Model plugin :validation_helpers one_to_many :deposits def before_validation return if !self.new? || !self.deposits.empty? parse_transaction assign_transaction_attrs @tx_hash["vout"].uniq { |out| out["n"] }.each do |out| amount = CryptoUnit.new(Config["currency"], out["value"], from_unit: :standart).to_i if out["value"] address = out["scriptPubKey"]["addresses"]&.first # Do not create a new deposit unless it actually makes sense to create one if address && amount && amount > 0 self.deposits << Deposit.new(amount: amount, address_string: address) end end end def before_create self.created_at = Time.now end def after_create self.deposits.each do |d| d.transaction = self d.save end end def tx_hash @tx_hash || parse_transaction end def confirmations if self.block_height TxCatcher.current_block_height - self.block_height + 1 else 0 end end # Queries rpc node to check whether the transaction has been included in any of the blocks, # but only if current block_height is nil. def update_block_height!(limit=100) # This calculates the approximate number of blocks to check. # So, for example, if transaction is less than 10 minutes old, # there's probably no reason to try and check more than 2-3 blocks back. # However, to make absolute sure, we always bump up this number for 10 blocks. # Over larger periods of time, the avg block per minute value should even out, so # it's probably going to be fine either way. created_minutes_ago = ((self.created_at - Time.now).to_i/60) blocks_to_check = (created_minutes_ago / 10).abs + 10 return self.block_height if self.block_height blocks_to_check = limit if blocks_to_check > limit i = 0 while i <= blocks_to_check block_hash = TxCatcher.rpc_node.getblockhash(TxCatcher.current_block_height - i) block = TxCatcher.rpc_node.getblock(block_hash) i += 1 Logger.report "--- checking block #{TxCatcher.current_block_height - i} for tx #{self.txid}" if block["tx"] && block["tx"].include?(self.txid) Logger.report "tx #{self.txid} block height updated to #{block["height"]}" self.update(block_height: block["height"]) return block["height"].to_i end end end private def parse_transaction @tx_hash = TxCatcher.rpc_node.decoderawtransaction(self.hex) end def assign_transaction_attrs self.txid = @tx_hash["txid"] end def validate super validates_unique :txid errors.add(:base, "No outputs for this transactions") if self.deposits.empty? end end end